🫧
アクセサブルなスイッチUIの作り方 ——— React + Tailwind CSSで実装例をご紹介

モバイルアプリでよく見かける即時反映スイッチUI。これの実装例をご紹介します。
実装例
スイッチUIを実装するには、<button>要素にrole="switch"とaria-checkedを付けましょう。
import { useState } from 'react';
function Switch() {
const [isOn, setIsOn] = useState(false);
return (
<button
role="switch"
aria-checked={isOn}
aria-label="ほげ"
onClick={() => setIsOn((prev) => !prev)}
className={`
relative inline-flex h-6 w-10 items-center rounded-full
transition-colors duration-200 ease-in-out
focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2
${isOn ? 'bg-blue-600' : 'bg-gray-300'}
`}
>
<span
className={`
inline-block h-4 w-4 rounded-full bg-white
transition-transform duration-200 ease-in-out
${isOn ? 'translate-x-5' : 'translate-x-1'}
`}
/>
</button>
);
}
ポイント
-
role="switch"を指定 -
aria-checkedで状態を明示(true/false) -
<button>要素なのでキーボード操作(Space/Enter)が自動的にサポートされる
よくない実装例
❌ div
function BadSwitchDiv() {
const [isOn, setIsOn] = useState(false);
return (
<div
onClick={() => setIsOn(!isOn)}
className={`
relative inline-flex h-6 w-10 items-center rounded-full cursor-pointer
${isOn ? 'bg-blue-600' : 'bg-gray-300'}
`}
>
<span
className={`
inline-block h-4 w-4 rounded-full bg-white
${isOn ? 'translate-x-6' : 'translate-x-1'}
`}
/>
</div>
);
}
ポイント
- キーボード操作ができない(Tabでフォーカスできない)
- スクリーンリーダーがボタンとして認識しない
❌ checkbox
function BadSwitchCheckbox() {
const [isOn, setIsOn] = useState(false);
return (
<label className="relative inline-flex h-6 w-10 items-center rounded-full cursor-pointer">
<input
type="checkbox"
checked={isOn}
onChange={(e) => setIsOn(e.target.checked)}
className="sr-only peer"
/>
<span
className={`
absolute inset-0 rounded-full
${isOn ? 'bg-blue-600' : 'bg-gray-300'}
`}
/>
<span
className={`
absolute inline-block h-4 w-4 rounded-full bg-white
${isOn ? 'translate-x-5' : 'translate-x-1'}
`}
/>
</label>
);
}
ポイント
- チェックボックスとスイッチは意味が異なる
- チェックボックス: 選択/非選択(フォーム送信時に使用)
- スイッチ: 即座に効果が反映されるオン/オフの切り替え
- スクリーンリーダーが「チェックボックス」と読み上げてしまう
- フォーム送信が必要な設定画面などでは、
<input type="checkbox">の実装も有効です
まとめ
スイッチUIを実装するには、<button>要素にrole="switch"とaria-checkedを付けましょう。
Discussion