1
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

「CSSだけで作るタブメニュー」の重大な問題と改善案

Posted at

会社の後輩コーダーが、次の案件で「CSSだけで作れるタブメニュー」を使う予定です!と言って、紹介記事を教えてくれました。
そのサンプルコードを見てみましたが、アクセシビリティ面に重大な問題があります。その記事だけではなく、「CSS タブ」などで検索して1ページ目に出てくる記事は、すべて同様の手法を使っていました。

私としては、後輩にも、令和のすべてのコーダーにもこの手法を使ってほしくありません。問題点と、改善したサンプルコードを共有します。

サンプル

まずは、問題がある例をCodePenで再現しました。こちらのリンクからご覧ください。

スクリーンショット 2025-01-04 17.23.43.jpg

それを改善した例がこちらです。

スクリーンショット 2025-01-04 17.26.05.jpg

同じですね。見た目は、全く同じです。
先に挙げた例の問題点を説明します。

問題点

1. クリック、タップ以外での操作ができない

「CSSだけで作るタブメニュー」の仕組みですが、タブメニューのボタンに隠してあるラジオボタンと、表示したいコンテンツをCSSの「後続兄弟結合子」で関連付けることによって実現しています。

#type01-all:checked ~ #type01-all__content,
#type01-programming:checked ~ #type01-programming__content,
#type01-design:checked ~ #type01-design__content {
  display: block;
}

さらにタブメニューのボタンをlabel要素 にして、for属性で結びつけることで、クリック可能としています。

<input id="type01-all" type="radio" name="tab_type01" checked>
<label class="tabs__item" for="type01-all">アクセシビリティ</label>

これには大きな問題はないのですが、検索して見つかる記事はすべて、選択の対象となるラジオボタンを display:none で隠してしまっています。

.tabs.tabs--type01 input[name*="tab_"] {
  display: none;
}

display:none で隠した要素は、存在しないものとして扱われます。

フォーカスができないため、クリック、タップ以外での操作はできません。

一方で、スクリーンリーダーからコンテンツを隠すので、visibility:hidden や display:none は使用しないでください。 正当な理由があるのでなければ、なぜこのコンテンツをスクリーンリーダーから隠したいのでしょうか。

2. スクリーンリーダーの読み上げ対象にならない

存在しないものとして扱われる、ということは、スクリーンリーダーでも対象外となります。ボタンのテキストは読み上げられますが、そこに切り替えのインターフェイスがあることは、スクリーンリーダーの利用者にはわかりません。

3. タブとコンテンツに関連性がない

これは細かい話ですが、問題がある例では、タブとコンテンツには文書上の関連性がありません。見出しが3つ並んだリストと、見出しがないコンテンツを3つ並べたセクション群でしかありません。

改善案

これらの問題を、ラジオボタンと後続兄弟結合子を使うというルールを外さずに改善を試みたのが、先に挙げた改善案です。

1. ラジオボタンとコンテンツを position で隠す

position: absolute で隠した要素は、ブラウザ上で対象外となりません。音声読み上げでも正しく読み上げられます。デジタル庁のサイトでも採用されている手法で隠しています。

.tabs.tabs--type02 input {
  border: 0 !important;
  clip: rect( 0, 0, 0, 0 ) !important;
  height: 1px !important;
  margin: -1px !important;
  overflow: hidden !important;
  padding: 0 !important;
  position: absolute !important;
  white-space: nowrap !important;
  width: 1px !important;
}

さらに、タブコンテンツの方も、表示しなくても認識されるよう position: absolute に書き換えています。もしもJavaScriptを使用していて、 aria-hidden 属性を付与していた場合は、コンテンツにはそこまでしなくてもよいかもしれませんが…

2. WAI-ARIAを使用する

改善したタブメニューは、aria属性による最適化をしています。明確にタブをグループ化し aria-label 属性を付与し、これがタブメニューであることを明示しています。
aria-label 属性は、要素が role 属性を持つことが前提なので role="tab" も付与しています。

<div class="tabs__list" role="tab" aria-label="タブメニュー:アクセシビリティについて">
  <label id="type02-all" class="tabs__item" for="type02-all-input">
    <input id="type02-all-input" type="radio" name="tab_type02" checked>
    アクセシビリティ
  </label>

・・・以下略

さらにコンテンツ側には aria-labelledby 属性を付与しています。この属性は、コンテンツの見出しに該当する要素をIDで指定します。下記の、一つ目のコンテンツは type02-all ですから、タブボタンの「アクセシビリティ」が見出しである、ということになります。
こちらにも role 属性を付与しています。

<div class="tabs__content" id="type02-all__content" role="tabpanel" aria-labelledby="type02-all">

・・・以下略

タブメニューをまとめたことで、HTMLのツリーが一階層下がり、セレクタも再考が必要になりました。 :has() を使用することで同様の挙動を実装しています。
セレクタがIDに依存してしまっていることが気に入らない

.tabs.tabs--type02 .tabs__list:has(#type02-all > input:checked) ~ #type02-all__content,
.tabs.tabs--type02 .tabs__list:has(#type02-programming > input:checked) ~ #type02-programming__content,
.tabs.tabs--type02 .tabs__list:has(#type02-design > input:checked) ~ #type02-design__content {
  overflow: visible;
  position: static;
  height: auto;
  padding: 2rem;
}

3. フォーカスに対応

問題がある例は、キーボードでフォーカスしたときを考慮していません。このため、フォーカスしたときも同じスタイルが適用されるようにしています。ラジオボタンにチェックが入っていて、さらにフォーカスもしている状態にも対応しなければなりません。

フォーカスしたときだけ、アウトラインリングが出るようにしたい…という場合は、CSSはより複雑になると思います。

.tabs.tabs--type02 .tabs__item:hover,
.tabs.tabs--type02 .tabs__item:has(input:focus) {
  background-color: #dddddd;
}

・・・中略

.tabs.tabs--type02 .tabs__item:has(input:checked),
.tabs.tabs--type02 .tabs__item:has(input:focus:checked) {
  background-color: #3399CC;
  color: #fff;
}

おわりに

問題点はどれもアクセシビリティに関することです。時間がないんだ、そこまで考慮しなくていい…というのであれば個人の自由ですが、最近はマウスで操作できない人=障害を持つ人とは限りません。

ゲームサイトであればコントローラやVRデバイスで操作している可能性もありますし、マウスが電池切れで使えない…!などのケースもあるでしょう。効率化のために音声読み上げを使用している人もいます。令和のコーダーであれば、最低限のアクセシビリティ対応は常識として捉えるべきです。

ここまで書いておいてなんですが、厳密にタブメニューを設計するなら、やはりJavaScriptを併用した方が良いです。タブコンテンツに aria-hidden 属性を使用できます。

私の案は完全でないと思います。ぜひ改良をお願いいたします。

1
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?