GODRICH
#cssアニメーション#ボタン#ローディング#トランジション#スクロール#ダッシュボード#実用ui#デザイントレンド

セグメントコントロールUI 9選 — コピペOK

セグメントコントロールとは、iOSの設定画面でよく見る、いくつかの選択肢から一つを選ぶと背景のピルがその位置までスライドするタブ切り替えボタンです。セグメントコントロールから絵文字リアクション・カラースウォッチまで、名前は知らなくてもアプリで見たことがある選択UI9種をコードごとまとめました。

自動再生 · タイルを押すとその項目へ · すべて1つのzip

9つは人気順ではなく、一つの画面で出会う順番で並べました。01・02はいくつかの表示から一つを選ぶ セグメントコントロール ios 風の部品、03・04はチップを押して選ぶ場合、05・06は絵文字でリアクションを残す場合、07は色、08は時刻、09は並び替え基準を選ぶ場合です。日付の範囲を選ぶ側はカレンダー 選択 UI 9選に、画像なしで人を表す表示はプロフィール UI デザイン 9種にあります。

01iOSピルセグメントコントロール

何が動くか

均等幅の3つのラジオの上を、青いピル背景がtranslateXで選ばれたラベルの位置までスライドします。 移動の途中でピルはscaleX(1.25)に引き伸ばされ、到着直前に1.05で少し跳ね返るゼリーストレッチを重ね、iOSのスプリング感触に一歩近づきます。

iOS ピルセグメントコントロール
新しいタブで開く

01 · iOS ピルセグメントコントロール — 計測 累積面積 6.007% · 動いたフレーム 15/23GODRICH オリジナル · SCSS と React は zip に同梱 · パスワードを見る

translateX(N*100%)균등폭 flex0.22초

コードで見る所

設定画面の表示モード切り替えや、注文状況フィルター(すべて/進行中/完了)に使えます。

.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で移動し、選んだアイコンだけが濃い色になります。 ピルは01と同じゼリーストレッチで移動中にscaleXで伸び、到着時にオーバーシュートして返り、トラックが弾力のある素材に見えます。

アイコン2択セグメント
新しいタブで開く

02 · アイコン2択セグメント — 計測 累積面積 4.462% · 動いたフレーム 14/23GODRICH オリジナル · SCSS と React は zip に同梱 · パスワードを見る

translateX 50%aria-label0.18초

コードで見る所

掲示板のリスト/グリッド表示切替でよく見る動きです。

.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で現れます。 塗りにもチェックにもオーバーシュートのキーフレームを重ね、塗りは一瞬はみ出して戻り、チェックは1を越えて1.2まで跳ね上がります。

複数選択フィルターチップ
新しいタブで開く

03 · 複数選択フィルターチップ — 計測 累積面積 3.999% · 動いたフレーム 6/23GODRICH オリジナル · SCSS と React は zip に同梱 · パスワードを見る

scaleX 채움:has() 선택자0.2초

コードで見る所

送料無料・当日発送・割引のような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)に軽く沈んでから弾み戻り、枠線が太くなります。 戻り際には1.05のスプリングオーバーシュートを通過し、指で実際に押したような反発力のある手触りを生みます。

サイズラジオチップ
新しいタブで開く

04 · サイズラジオチップ — 計測 累積面積 2.096% · 動いたフレーム 10/23GODRICH オリジナル · SCSS と React は zip に同梱 · パスワードを見る

scale 바운스cubic-bezier 오버슈트0.3초

コードで見る所

商品サイズ選択や予約人数選択に使われるパターンです。

.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がリアルタイムで更新されます。 浮かび上がる途中で縦に潰れ、次に横にはみ出るゼリーの揺れを重ね、柔らかいシールが飛び出す感覚を出します。

絵文字リアクションポップアップ
新しいタブで開く

05 · 絵文字リアクションポップアップ — 計測 累積面積 6.434% · 動いたフレーム 7/23GODRICH オリジナル · SCSS と React は zip に同梱 · パスワードを見る

opacity+translateYaria-expandedEsc 로 닫힘

コードで見る所

メッセージへのリアクションや投稿への反応に使う仕組みです。

.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つ増えます。 ジャンプ自体はゼリーストレッチで、x軸とy軸が交互に伸び縮みするため、ピークで左右に広がり、戻りでは縦に反発します。

絵文字バーストカウント
新しいタブで開く

06 · 絵文字バーストカウント — 計測 累積面積 0.487% · 動いたフレーム 9/23GODRICH オリジナル · SCSS と React は zip に同梱 · パスワードを見る

scale 바운스burst dot ×4aria-live

コードで見る所

ショート動画のハートリアクションと同じ仕掛けです。

.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で現れます。 バッジは1.25まで少し傾きながら跳ね上がってから収まり、選んだ瞬間を指先で確認させます。

グリッドカラースウォッチ
新しいタブで開く

07 · グリッドカラースウォッチ — 計測 累積面積 0.578% · 動いたフレーム 7/23GODRICH オリジナル · SCSS と React は zip に同梱 · パスワードを見る

scale(0)→scale(1) 체크3×2 grid0.22초

コードで見る所

商品カラーオプションのグリッドやテーマカラー選択画面に合います。

.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で押し出され、次の数字がその場所に滑り込む、車の走行距離計のような動きです。 収まった後、数字は縦に1.15倍伸びてから少し沈むゼリー着地を一度挟み、機械式メーターの歯車が噛み合う反動を真似ます。予約時間の選択やアラーム時刻の設定に使えます。

オドメーター式時刻スクラバー
新しいタブで開く

08 · オドメーター式時刻スクラバー — 計測 累積面積 0.499% · 動いたフレーム 7/23GODRICH オリジナル · SCSS と React は zip に同梱 · パスワードを見る

translateY 롤오버data-dir 버튼0.18초

コードで見る所

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で開き、選んだ項目だけにチェックマークが付き、トリガーの文字もその項目名に変わります。 メニューは縦のゼリーオーバーシュート(0.9→1.04)で開き、チェックマークは0から1.3までスプリングで飛び込みます。

チェックマーク並び替えドロップダウン
新しいタブで開く

09 · チェックマーク並び替えドロップダウン — 計測 累積面積 8.764% · 動いたフレーム 11/23GODRICH オリジナル · SCSS と React は zip に同梱 · パスワードを見る

role=menuopacity+translateY0.2초

コードで見る所

商品一覧の並び替えや掲示板の投稿並び替えに使います。

.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()の落とし穴

セグメントコントロール ui、どこで壊れるか?

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がタイミングを合わせています。ログイン後の画面でこのピルが現れる形はアカウント セキュリティ 設定 UI 9選のトグルグループでも確かめられます。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がページ移動なしで即座に状態だけ変わる理由です。値を一つ選ぶ別の道としての開く式はセレクトボックス デザイン css 9選が別個に扱っています。

:has()が使えないブラウザでは03・04・07は完全に壊れますか?

いいえ。:has()がなくてもチェックマークやリングが表示されないだけで、本物の<input>の:checked状態やフォームの送信値は普通に動作します。見た目のフィードバックだけが失われます。

React版の09もEscで閉じますか?

はい。react/09-sort-dropdown-checkmark-menuのコンポーネントにも同じonKeyDownリスナーが入っており、Escを押すとopenがfalseになってメニューが閉じます。外側クリックで閉じる処理は落とし穴の節で書いた通りこちらにも入っていないので、実際の運用では追加をおすすめします。

解凍パスワードを入力してください

パスワードはこの記事の本文の中にあります。読み進めると出てきます。