チェック描画
チェック時にレ点が線で描かれるアニメーション。操作の手応えを視覚で強調する。
プレビュー
操作クリック/タップで動きます。
使いどころ
完了したかどうかを視覚的に強調したいチェック操作に向く。ToDoリストの完了チェック、利用規約への同意チェックのように「押した」という手応えをユーザーに返したい単発の操作が適している。特に本実装はcheck-textに取り消し線と色変化を自動で連動させているため、タスク完了を線で消す表現とセットで使うと最も効果が出る。逆に、テーブルの行選択や一括操作用のチェックボックスのように、短時間に何十個も連続でチェックする場面には向かない。stroke-dashoffsetの描画に0.3秒かかる設計なので、連打すると前のチェックの線が引き切る前に次を押すことになり、もっさりした印象になる。また一部だけ選択されたことを示すindeterminate状態のスタイルをこの実装は持たないため、親子関係のある一括選択チェックボックスにそのまま使うと、実際にはindeterminateなのに見た目は未チェックのままになる。
仕組み
ネイティブのinput type=checkboxをposition:absolute; opacity:0; width/height:0で視覚的に消しつつDOMには残し、キーボード操作とchecked状態の管理をブラウザに任せている。見た目を担うcheck-boxはaria-hidden付きの装飾要素で、中のsvg polylineにstroke-dasharray:22とstroke-dashoffset:22を仕込み、チェック時にdashoffsetを0へ0.3秒のcubic-bezierで動かすことで「線が引かれる」動きを作る。状態の伝播はすべて一般兄弟結合子(~)によるCSSだけで完結しており、.check-input:checked ~ .check-boxで枠線と背景色を、中のcheck-markでdashoffsetを、.check-input:checked ~ .check-textで文字色と取り消し線を同時に切り替える。JSは一切使われておらず、labelでinputとテキストを囲んでいるためクリック領域の判定もブラウザ標準に委ねている。
実装の注意
最大の抜けは、隠したinputにフォーカスが当たったときの視覚的なフォーカスリングが実装のどこにも定義されていないこと。inputはwidth/height 0で視覚的な輪郭を持たないため、ブラウザ既定のoutlineが仮に出ても実質見えず、キーボードでTab移動しているユーザーは今どのチェックボックスにいるか分からなくなる。次に、stroke-dasharray:22はこのpolyline(3,10 8,15 17,5)の実測パス長に厳密に合わせた値なので、チェックマークの形やviewBoxを変更する際にこの数値を再計算し忘れると、線が短すぎて途中で止まる、または始点に余りが出るといった見た目の破綻が起きる。また兄弟結合子はマークアップの順序に依存するため、input・check-box・check-textの順を入れ替えると3つの連動が音もなく効かなくなる。
アクセシビリティ
labelでネイティブのinput type=checkboxを包む構成なので、ラベルの読み上げ・Spaceキーでのトグル・checked状態の通知といった基本はブラウザとスクリーンリーダーの組み合わせにそのまま乗っており、装飾のcheck-boxとsvgにはaria-hiddenが付いて二重読み上げも防いでいる。ただし実装には明確な欠落があり、:focus-visibleに対応するフォーカスリングのスタイルが一切用意されていない。視覚的な入力欄がwidth/height 0のため、キーボード操作時に「今ここにいる」という手がかりが画面上から消えてしまう。.check-input:focus-visible ~ .check-boxにoutlineを足す対応が必須である。またprefers-reduced-motionの分岐もなく、border-color・background・stroke-dashoffset・colorの4つのtransitionが常に動く。一部選択を示すindeterminate状態のスタイルも用意されていないため、親子チェックボックスに転用するなら別途JSでindeterminateプロパティを操作し、対応するCSSを足す必要がある。
調整して使う
調整しやすいのは3点。まず描画時間とイージング(既定0.3秒のcubic-bezier)で、規約同意のような一度きりの重要な操作なら0.4〜0.5秒に伸ばして間を演出し、密なリストでは0.15秒程度に縮めて操作感を優先する。次にマークの形で、polylineを別の形(プラス記号など)に差し替えることもできるが、その際は必ずgetTotalLength()で新しいパスの実測長を測り、stroke-dasharray/dashoffsetの22という数値を置き換える必要がある。最後にbox-shapeで、border-radius: 7pxの角丸正方形を999pxにすれば丸いラジオボタン風の見た目になり、タスクリストのような密なUIではサイズを20px前後に縮めるとよい。取り消し線付きのcheck-textはToDo向けの演出なので、規約同意や設定のON/OFFに転用する場合は取り消し線のスタイルだけ外すべきである。
コード(コピーして使えます)
<label class="check-label">
<input class="check-input" type="checkbox">
<span class="check-box" aria-hidden="true">
<svg class="check-svg" viewBox="0 0 20 20" fill="none">
<polyline class="check-mark" points="3,10 8,15 17,5"/>
</svg>
</span>
<span class="check-text">タスクを完了</span>
</label>