セグメントコントロールUI 9選 — コピペOK
セグメントコントロールとは、iOSの設定画面でよく見る、いくつかの選択肢から一つを選ぶと背景のピルがその位置までスライドするタブ切り替えボタンです。セグメントコントロールから絵文字リアクション・カラースウォッチまで、名前は知らなくてもアプリで見たことがある選択UI9種をコードごとまとめました。
自動再生 · タイルを押すとその項目へ · すべて1つのzip
- 01 iOS ピルセグメントコントロール
- 02 アイコン2択セグメント
- 03 複数選択フィルターチップ
- 04 サイズラジオチップ
- 05 絵文字リアクションポップアップ
- 06 絵文字バーストカウント
- 07 グリッドカラースウォッチ
- 08 オドメーター式時刻スクラバー
- 09 チェックマーク並び替えドロップダウン
9つは人気順ではなく、一つの画面で出会う順番で並べました。01・02はいくつかの表示から一つを選ぶセグメントコントロール、03・04はチップを押して選ぶ場合、05・06は絵文字でリアクションを残す場合、07は色、08は時刻、09は並び替え基準を選ぶ場合です。
01iOSピルセグメントコントロール
均等幅の3つのラジオの上を、青いピル背景がtranslateXで選ばれたラベルの位置までスライドします。設定画面の表示モード切り替えや、注文状況フィルター(すべて/進行中/完了)に使えます。
.seg__pill {
position: absolute;
width: calc((100% - 8px) / 3);
transform: translateX(0);
transition: transform $duration $easing;
}
#seg1-2:checked ~ .seg__track .seg__pill { transform: translateX(100%); }
#seg1-3:checked ~ .seg__track .seg__pill { transform: translateX(200%); }
02アイコン2択セグメント
リスト・グリッドのどちらかを選ぶと、ピル背景がトラック幅の半分だけtranslateXで移動し、選んだアイコンだけが濃い色になります。掲示板のリスト/グリッド表示切替でよく見る動きです。
.iv__pill {
position: absolute;
width: $sp-12;
transform: translateX(0);
transition: transform $duration $easing;
}
#iv1-2:checked ~ .iv__track .iv__pill { transform: translateX(100%); }
03複数選択フィルターチップ
複数のチェックボックスをチップ状に並べ、選んだチップだけ背景が中央からscaleXで広がり、インラインSVGのチェックアイコンがscale(0)から1で現れます。送料無料・当日発送・割引のようなECサイトのフィルターに向いています。
.cf__chip::before {
content: "";
position: absolute; inset: 0;
background: $color;
transform: scaleX(0);
transform-origin: center;
transition: transform $duration $easing;
}
.cf__chip:has(.cf__input:checked)::before { transform: scaleX(1); }
04サイズラジオチップ
S・M・L・XLのラジオチップから一つを選ぶと、そのチップだけscale(.84)に軽く沈んでから弾み戻り、枠線が太くなります。商品サイズ選択や予約人数選択に使われるパターンです。
.sz__chip:has(.sz__input:checked) {
border-color: $color;
border-width: 3px;
animation: sz-press $duration $easing;
}
@keyframes sz-press {
0% { transform: scale(1); }
35% { transform: scale(.84); }
70% { transform: scale(1.05); }
100% { transform: scale(1); }
}
05絵文字リアクションポップアップ
リアクションボタンを押すと絵文字4つの帯がopacityとtranslateYで浮かび上がり、ボタンのaria-expandedがリアルタイムで更新されます。メッセージへのリアクションや投稿への反応に使う仕組みです。
.rp__pop {
transform: translate(-50%, 8px) scale(.85);
opacity: 0;
pointer-events: none;
transition: transform $duration $easing, opacity $duration $easing;
}
.rp.is-open .rp__pop {
transform: translate(-50%, 0) scale(1);
opacity: 1;
pointer-events: auto;
}
06絵文字バーストカウント
絵文字を選ぶとその絵文字がscaleで大きく弾み、周りの点4つが外側に散って消える間に、隣のバッジの数字が1つ増えます。ショート動画のハートリアクションと同じ仕掛けです。
.bc.is-play .bc__emoji { animation: bc-emoji $duration $easing; }
.bc.is-play .bc__burst { animation: bc-burst $duration $easing; }
@keyframes bc-emoji {
0% { transform: scale(1); }
35% { transform: scale(1.5); }
60% { transform: scale(.9); }
100% { transform: scale(1); }
}
07グリッドカラースウォッチ
6色のスウォッチを3×2グリッドに並べ、一つを選ぶとその右下に小さなチェックバッジがscale(0)から1で現れます。商品カラーオプションのグリッドやテーマカラー選択画面に合います。
.cw__check {
transform: scale(0);
transition: transform $duration $easing;
}
.cw__check-icon { /* 인라인 Phosphor SVG — currentColor 를 물려받는다 */
position: absolute; inset: 3px;
color: $color;
}
.cw__cell:has(.cw__input:checked) .cw__check { transform: scale(1); }
08オドメーター式時刻スクラバー
時・分の上下ボタンを押すと現在の数字がtranslateYで押し出され、次の数字がその場所に滑り込む、車の走行距離計のような動きです。予約時間の選択やアラーム時刻の設定に使えます。
function roll(col, dir) {
var win = col.querySelector('.tp__win');
var cur = win.querySelector('.is-cur'), next = win.querySelector('.is-next');
var v = (parseInt(cur.textContent, 10) + dir + 60) % 60;
next.textContent = String(v).padStart(2, '0');
cur.classList.add('is-leaving');
next.classList.add('is-arriving');
}
09チェックマーク並び替えドロップダウン
並び替えボタンを押すとリストがopacityとtranslateYで開き、選んだ項目だけにチェックマークが付き、トリガーの文字もその項目名に変わります。商品一覧の並び替えや掲示板の投稿並び替えに使います。
.sd__menu {
transform: translate(-50%, -6px);
opacity: 0;
transition: transform $duration $easing, opacity $duration $easing;
}
.sd.is-open .sd__menu { transform: translate(-50%, 0); opacity: 1; }
.sd__check { /* 인라인 Phosphor SVG 체크 */
color: $color;
transform: scale(0);
transition: transform $dur-instant $easing;
}
.sd__item.is-checked .sd__check { transform: scale(1); }
どこで壊れるか — 隠したinputと:has()の落とし穴
01・02・03・04・07はラジオ・チェックボックスの本物の<input>をposition: absolute; width: 1px; height: 1px; clip: rect(0,0,0,0);で見た目だけ隠しています。display: noneやvisibility: hiddenを使うとフォーカス順序から完全に外れ、キーボードだけで操作する人はTabキーでたどり着けなくなります。03・07が使う:has()はChrome・Edge 105+、Safari 15.4+、Firefox 121+でのみ動作し、それより古いバージョンではチェック表示が出ないだけでクリック自体は問題なく動きます。08のオドメーターの動きはCSSだけでは2桁の数字を同時に押し出し滑り込ませるのが難しく、roll()という12行のJavaScriptがタイミングを合わせています。9つのファイルがそのまま入ったzipの圧縮パスワードはe9nu36x4で、05・09のようにボタンで開くポップアップは外側クリックやEscでも閉じるべきですが、このデモは分量を抑えるためEscのみ実装し、外側クリックでの終了は省いています — 実際に使う際はdocumentへのクリックリスナーを追加するのが安全です。
アクセシビリティ
01・02・03・04・07は本物の<input>を隠しつつ残しているため、スクリーンリーダーはラジオ・チェックボックスをそのまま読み上げ、Tabとスペース・矢印キーだけで状態を変えられます。05・09のポップアップ・メニューのトリガーは開閉のたびにaria-expandedを更新し、09の各項目はrole="menuitemradio"とaria-checkedで現在の選択を伝えます。06のカウントはaria-live="polite"なので、反応数が変わるとスクリーンリーダーが静かに新しい数字を読み上げます。prefers-reduced-motion: reduceでは9つ全部でトランジション・アニメーションを止めますが、ピル・チェック・メニューは行き着いた状態のまま残るため、動きを消しても状態の情報自体は消えません。
@media (prefers-reduced-motion: reduce) {
.seg__pill, .cf__chip::before, .cw__check { transition: none; }
.bc.is-play .bc__emoji, .bc.is-play .bc__burst { animation: none !important; }
}
選択UI以外のCSSアニメーションは実用UIカテゴリーにもあり、このサイトがどんな基準でデモを検証しているかは紹介ページにまとめています。
FAQ
セグメントコントロールとタブは何が違いますか?
タブは押すと別のページやコンテンツ領域に移動することが多いですが、セグメントコントロールは同じ画面の中でフィルターや表示モードのような値を一つだけ変えます。01・02がページ移動なしで即座に状態だけ変わる理由です。
:has()が使えないブラウザでは03・04・07は完全に壊れますか?
いいえ。:has()がなくてもチェックマークやリングが表示されないだけで、本物の<input>の:checked状態やフォームの送信値は普通に動作します。見た目のフィードバックだけが失われます。
React版の09もEscで閉じますか?
はい。react/09-sort-dropdown-checkmark-menuのコンポーネントにも同じonKeyDownリスナーが入っており、Escを押すとopenがfalseになってメニューが閉じます。外側クリックで閉じる処理は落とし穴の節で書いた通りこちらにも入っていないので、実際の運用では追加をおすすめします。