ちいつる

マイクロ・フィードバック

トグルスイッチ

クリックでつまみが滑らかにスライドする ON/OFF スイッチ。設定画面の定番UIパターン。

プレビュー

クリック

操作クリック/タップで動きます。

使いどころ

「今どちらの状態か」が一目でわかってほしい、即時反映される二択の設定に向く。通知のON/OFF、ダークモード、公開・非公開のように、切り替えた瞬間に効果が発生するものが典型例。逆に、チェックしてから最後に「保存」を押す種類の設定にトグルを使うと、切り替えた時点で反映されたと誤解されるため、そこはチェックボックスの方が正しい。選択肢が3つ以上あるときや、二択でも「はい/いいえ」より具体的な語(例: 円グラフ/棒グラフ)で示した方がわかりやすいときは、トグルではなくセグメンテッドコントロールを選ぶ。ラベルは必ずスイッチの外に置き、ONのときの状態を説明する肯定形にする。「通知を無効にしない」のような二重否定は、ONがどちらの意味かわからなくなる典型的な失敗。

仕組み

土台は input type="checkbox" を1つ置き、それを視覚的に隠して隣の span を装飾する構成にする。見た目だけ div で作るのではなくネイティブのチェックボックスを使うのが重要で、こうするだけでキーボード操作、フォーム送信、支援技術への状態通知がすべて無料で付いてくる。つまみの移動は :checked 疑似クラスと兄弟セレクタを組み合わせ、input:checked + .track .knob { transform: translateX(20px) } のように書く。レールの色も同じ条件で切り替える。移動に left ではなく translateX を使うのは、レイアウトを再計算させずコンポジタだけで動かすため。トランジションは transform と background-color に絞り、0.2秒前後、cubic-bezier で終わりを少し緩めるとつまみが「収まった」感触になる。

実装の注意

チェックボックスを display: none で隠すのが最大の落とし穴で、こうするとキーボードのフォーカスが当たらず操作できなくなる。opacity: 0 と position: absolute で視覚的にだけ隠し、要素としては生かしておく。つまみのサイズを固定 px で、移動距離も固定 px で書くと、ユーザーがブラウザの文字サイズを変えたときにつまみがレールから外れる。em で書くか、移動距離を calc(100% - つまみ幅) で表すと崩れない。タップ領域は見た目のレールより広く取る必要があり、スイッチ全体を label で包んで最低 44×44px を確保する。見た目が小さいまま当たり判定も小さいと、モバイルで何度も押し直すことになる。非同期で保存する場合は、通信が失敗したときにつまみを元の位置へ戻す処理も要る。見た目だけ切り替わって実際は保存されていない状態は、ユーザーが最も気づきにくい不具合になる。

アクセシビリティ

ネイティブのチェックボックスを土台にしていれば、状態は checked として支援技術に正しく伝わる。div で自作する場合は role="switch" と aria-checked を自分で管理し、tabindex="0" とキーボードイベントも実装しなければならず、手間に対して得るものが少ない。ラベルは label 要素の for 属性か、input を label で包む形で必ず関連付ける。これがないとスクリーンリーダーは「チェックボックス、オン」としか読まず、何の設定かわからない。フォーカス表示は :focus-visible でレール全体を囲むアウトラインを出すのが分かりやすい。色だけで状態を示すのも避けたい点で、色覚特性によってはONとOFFのレール色が同じ明度に見えることがある。つまみの位置という形の違いが付いているので基本は問題ないが、小さく表示する場面では ON/OFF の文字やチェック記号を添えると確実になる。

調整して使う

調整の中心はレールの寸法とトランジション時間。レールの幅はつまみの直径の1.8〜2.2倍にすると、ONとOFFの位置の差がはっきりする。トランジションは0.15〜0.25秒が快適な範囲で、0.3秒を超えると操作に対して鈍く感じられる。つまみに軽い box-shadow を足すとレールから浮いて見え、押せる要素だと伝わりやすくなる。状態の色は、OFFをグレー、ONをブランドカラーにするのが定石だが、危険な設定(公開、削除の許可など)をONにするトグルではONを警告色にして「今その状態である」ことを強調する手もある。小さく並べる場合は、つまみの移動距離を translateX の固定値ではなくcalc(100% - つまみ幅 - 余白×2) で表すと、サイズ違いを1つのCSSで作れる。

コード(コピーして使えます)

<div class="toggle-wrap">
  <button class="toggle" role="switch" aria-checked="false" aria-label="設定">
    <span class="knob"></span>
  </button>
  <span class="toggle-label">OFF</span>
</div>

タグ

関連パーツ