$48
トピックを検索
ホバーでしか現れないカードの操作ボタン
商品一覧の「保存」と「カートに入れる」が、ポインターをカードに重ねるまで隠れていて、ほかの方法では表示されません。
.card:hover .actions { visibility: visible; } 問題
キーボード、スイッチ、スクリーンリーダー、タッチスクリーン、画面拡大鏡の利用者は、カードの操作を見つけられず、使えません。表示するのは、止まったポインターだけだからです。
ショップの商品一覧は、すっきりとデザインされています。各カードには写真、商品名、価格だけがあります。カードにポインターを重ねると、価格の下に操作の行がふわりと現れます。「保存」と「カートに入れる」です。ポインターを離すと消えます。一覧は静かなまま、マウスを使う人には操作が届きます。
マウスを使わずにTabキーを押してみます。フォーカスは、あるカードの商品名から次のカードの商品名へ移ります。「保存」と「カートに入れる」には届きません。操作の行は、カードにホバーするまで visibility: hidden で隠されていて、隠れたボタンにはフォーカスが入らないからです。スマートフォンには、重ねておくポインターがありません。カードの写真や商品名をタップすると商品ページが開き、一覧で操作が表示されることはありません。
同じデザインには、もう一つの実装があります。行を opacity: 0 で隠し、カードの下端に、価格の上に重ねて滑り込ませるものです。こちらではTabで「保存」に届きますが、フォーカスがある間もボタンは完全に透明なので、キーボードの利用者には何も変わって見えず、Enterで何が起きるか分かりません。画面拡大鏡では、ポインターが着いた途端に行が価格を覆い、Escapeを押しても何も起きません。
操作は、止まったポインターに対してしか存在しません。WCAG 2.1.1(キーボード)は、すべての機能をキーボードで操作できることを求めています。失敗例F54は、同じことをスクリプトで行う場合を挙げています。ポインティングデバイスのイベントでしか届かない機能です。visibility: hidden や display: none で隠すと、ボタンはTabの順序から外れます。opacity: 0 では順序に残りますが、見えません。見えないボタンを押せても、助けにはなりません。タッチスクリーンはそもそもホバーできないため、スマートフォンの利用者にはどちらの実装も届きません。
表示された行がほかの内容を覆う場合は、1.4.13(ホバー又はフォーカスで表示されるコンテンツ)にも関わります。ホバーで現れて元の内容を覆うコンテンツは、ポインターやフォーカスを動かさずに消せる必要があります。通常はEscapeです。価格の上に滑り込む操作は、閉じる手段がないため、これを満たしません。
このパターンは、商品一覧、ファイルの一覧、写真のギャラリー、ダッシュボードなど、デザイナーが初期状態を静かに保ちたい場面でよく使われます。そのカードの「保存」や「削除」が、そこにしかないこともよくあります。
10グループ中5グループに影響
- 全盲 (影響あり)
- 弱視 (影響あり)
- 色覚 (影響なし)
- ろう (影響なし)
- 難聴 (影響なし)
- 発話 (影響なし)
- 手の操作 (影響あり)
- 届く範囲・力 (影響あり)
- 認知 (影響あり)
- 光過敏 (影響なし)
- 手の操作が限られる キーボードやスイッチの利用者には、カードに重ねるポインターがありません。操作は現れないか、見えないボタンにフォーカスが入ります。
- 視覚を使わない スクリーンリーダーの利用者もキーボードで移動します。visibility: hiddenはボタンをアクセシビリティツリーからも外すため、カードに「保存」があること自体が伝わりません。
- 届く範囲・力が限られる スマートフォンにはホバーがありません。カードをタップすると、操作が出る前にリンク先へ移動します。手をカードの上で止めておけない人は、パソコンでも操作を見られません。
- 視覚が限られる 画面拡大鏡の利用者は、カードの一部しか見えません。ポインターが着いた途端に操作が商品名や価格の上に滑り込み、下にあるものを読むために消す方法がありません。
- 言語・認知・学習の能力が限られる カードに重ねると操作が出ることは、カードのどこにも書かれていません。この仕組みを知らない人は、操作を見つけられません。
マークアップに問題はありません。カードの中に、名前のある本物のボタンがあります。静的なスキャンはそれを見て合格にします。問題はCSSのルールと、起きないことにあります。誰もホバーせず、ほかに行を表示するものがありません。ツールは、カードにホバーしてボタンが現れるのを確認し、次にそのボタンにフォーカスして、まだ見えているかを確かめる必要があります。修正をAIに頼むと、カードに tabindex="0" を付けることがあります。カードはTabの順序に入りますが、やはり何も表示されません。visibility を opacity に替えることもあり、問題の形が変わるだけです。
試してみる
カードにポインターを重ねてから、マウスを使わずにTabキーで「保存」に移動してください。
一覧の下の欄に、フォーカスのある要素と、それが見えているかが出ます。マウスなしで「保存」と「カートに入れる」に届き、届いたときに見えていますか?
問題あり 修正できますか?
$18
修正方法
フォーカス時とタッチスクリーンでも操作を表示し、専用の場所に置きます。または、すべてのカードに常に表示します。
- 1ホバーで表示される操作は、カードかその操作にフォーカスがあるときも表示される。
- 2ホバーのない画面では、ポインターの状態に関係なく操作が表示される。
- 3表示される操作は専用の場所にあるか、Escapeでフォーカスを動かさずに隠せる。
カードにホバーするまで隠れた操作
<article class="card"> <a href="/tote"><img src="tote.jpg" alt="リネンのトート、ナチュラル">リネンのトート</a> <p class="price">$48</p> <div class="actions"> <button type="button">保存</button> <button type="button">カートに入れる</button> </div></article>フォーカス時とタッチスクリーンでも表示し、専用の行に置く
この修正では HTML を追加しません。
この方法を選ぶ理由:ジェスチャーは近道にし、唯一の方法にしない
行を表示するのは :hover だけです。visibility: hidden で隠れたボタンはTabの順序から外れるため、キーボードでは届かず、タッチスクリーンはホバーしません。
:focus-within は、カードの中のどれかの要素にフォーカスがある間、カードに一致します。Tabでカードのリンクに着いた時点で行が現れ、その後のボタンにフォーカスが届いたときには見えています。visibility も同じルールで切り替えないと、ボタンはTabの順序から外れたままです。メディアクエリー hover: none は、スマートフォンや、タッチで使うタブレットで真になります。それがなければ、行は一度も表示されません。行は価格の下の専用の場所に置かれたままなので、現れても何も覆わず、1.4.13が消す手段を求めることもありません。ポインターでの表示は、ポインターを使うすべての人のために、そのまま残ります。
どちらも基準を満たします。フォーカスとタッチでの表示は、ポインターの利用者にはデザインどおりの見た目を保ち、CSSのルール3つで済みます。ただし、カードを置く場所が増えるたびに同じルールが必要で、hover: none の代替によってスマートフォンのレイアウトはパソコンと違う見た目になります。デザインファイルにその状態を描いておくべきです。常に表示する方法は初期状態の見た目を変え、ボタンがそこになじむようにデザイナーが描く必要があります。ショップの一覧の「カートに入れる」や、ファイル一覧の「削除」のように、操作が重要なカードでは、ボタンを描き込んでください。ショップが頼る操作を、誰かがホバーに気付くことに委ねるべきではありません。ホバーでの表示は、ボタンを並べると込み合う一覧の、二次的な操作に限り、フォーカスとタッチのルールを添えて使います。
三つ目の形として、各カードに常に表示するメニューボタンを置き、操作をそのメニューに入れる方法があります。場所を最も取らず、メニューの全体は右クリックや長押し、スワイプでしか出ない操作メニューのガイドで示しています。
ホバーでの表示は状態の一つで、状態には描画が必要です。ホバー状態の隣に、カードのフォーカス状態を加えてください。操作を表示し、最初のボタンにフォーカスの枠を描きます。さらに、操作を常に表示したタッチ用の状態も加えます。開発者はそこから :focus-within と hover: none のルールを組み立てられます。常に表示する版では、ボタンを実際のサイズで描いてください。2.5.8が求める最小のターゲットは24 × 24ピクセルで、枠線だけのピル型ボタンは、枠線がカードに対して3:1、文字が4.5:1のコントラストを必要とします。コントラスト比は色見本の横に書いておくと、配色を変えても残ります。
一覧に多くのカードがあるときは、各ボタンに、どの商品に対する操作かが分かる名前を付けます。視覚的に隠した「リネンのトートを保存」というspanか、カードの商品名を指す aria-describedby です。スクリーンリーダーでボタンだけをたどる人に、「保存、保存、保存」と聞こえないようにします。表示されたボタンのフォーカスの枠は、ほかと同じくカードに対して3:1が必要です。フォーカス表示が見えない独自デザインのボタンを参照してください。カード全体がリンクで、その中にボタンがある場合、操作要素の入れ子は不正なHTMLで、読み上げも乱れます。リンクは商品名と写真に付け、ボタンはその隣に置いてください。
強制配色でも opacity と visibility はそのまま効くため、隠した行はそこでも隠れたままです。上の修正はそのまま動きます。display: none と display: flex で切り替える行も、修正の visibility と同じように動きますが、ふわりと表示することはできません。どちらかに決めて、すべてのカードコンポーネントに同じ方法を使ってください。
AI コーディングアシスタントで修正しますか? このガイドを Markdown で取得、または全ガイドをコーディングエージェントで使う。
デザインの案
操作は、隠さなくても控えめにできます。
同じカードの操作を5通りに描きました。カードにポインターを重ねてから、マウスを使わずにTabで移動してみてください。3つはどの使い方でもボタンが見え、2つはマウスにしか見えません。
合格
- 常に表示する枠線のボタン ガイドの2つ目の修正です。カードの文字色の小さなピル型ボタンで、高さは24ピクセル、ホバーとフォーカスで塗りつぶします。何も隠さないので、キーボードでもスマートフォンでも同じカードです。
- 控えめな文字のボタン、ホバーで下線 初期状態が最も静かな形です。#555555の文字だけで、白に対して7.45:1、ホバーかフォーカスで下線が付くまで枠もありません。押しやすさのため、高さと間隔は24ピクセル以上を保ちます。
- ホバーでもフォーカスでも表示 ガイドの1つ目の修正です。行はポインターを待ちますが、Tabがカードに着いた時点で:focus-withinが表示し、ホバーのない画面では常に表示します。専用の行にあるので、何も覆いません。
不合格
- カードにホバーするまで非表示WCAG 2.1.1 visibility: hiddenでボタンがTabの順序から外れるため、キーボードでは飛ばされ、ホバーしないタッチスクリーンでは一度も表示されません。
- カードに重ねた透明な行WCAG 2.4.7 opacity: 0のままなのでボタンはTabの順序に残り、フォーカスが「保存」に入っても画面には何も表示されません。ホバーすると行が価格の上に重なり、どのキーでも消せません。
修正の確認
5つの確認
確認方法: キーボード、マウス、タッチ、ズーム、スクリーンリーダー
OS、ブラウザーと支援技術のバージョン、ビルド、実施日、各手順の実際の結果を記録してください。カードのコンポーネントや一覧のテーマを変更した際は、再度確認します。
制限
このサンプルは、どのアプリケーションでも同じ結果になることを保証するものではありません。表示のルールは、操作がカードの中の普通のボタンであることを前提にしています。ライブラリーのカードコンポーネントは、ボタンをDOMの別の場所に描くことがあり、そこでは :focus-within が効きません。実際の一覧で、キーボード、スマートフォン、画面拡大鏡を使って確認してください。
よくある問題を再現した学習用のサンプルです。実在のクライアントの診断結果ではありません。コードは出発点となる実装例のため、実際の製品でも確認してください。ここでは支援技術でのテスト結果は報告していません。
コードを更新しても、使いやすさを保つために
ガイドの内容を、コーディングエージェントやCIで使える開発ルールにまとめます。更新後は、キーボードとスクリーンリーダーで再テストし、問題が再発していないか確認します。