できる限りのアリア。
この記事は「アクセシビリティ Advent Calendar 2023」22日めの記事です。
HTMLの語彙力では事足りない、ちょっと凝ったUIを、どんな環境でも同じように閲覧できるようにするためのチェックポイントまとめ。ここでは、Webサイトで割とよく見る「ナビゲーションUI」のWebアクセシビリティを確保する術を見てゆきます:)。
Webアクセシビリティ
Webサイトは、インターネットとブラウザがあれば、何時でも、何処でも、誰でも、どんな環境でも見ることができるものです。なので、Webサイトは「何時でも何処でも誰でもどんな環境でも(以下、すべての人にという)」見れるように作っておかないといけません。
そう、Webサイトはアクセシブルでないと意味がないんですね。:D
アクセシブル
アクセシブルは「アクセスしやすい」という意味。
Webサイトのアクセシブル具合のことを「Webアクセシビリティ」と言って、Webコンテンツの「情報へのアクセスのしやすさ」のことを指します。
「情報にアクセスしやすい」状態とは、文字が読みやすくて、見分けやすい配色で、情報が整理されていて、すべての人にきちんと伝わるようデザインされている、ということ。
また、でんぱが悪くてもすばやくアクセスできたり、どんな端末でも正しく閲覧できたり、どんな状況でも手間なく操作できて、「すべての人が同じ感じに同じ情報をゲットできる」状態が望ましいです。
ブラウザはアクセシブル
実はブラウザは、マウスやタッチで操作する以外にも、キーボードだけでも操作できるし、スクリーンリーダーを使えば、Webコンテンツを音声で読み上げてもくれるし、点字ディスプレイに繋げば点字で出力もしてくれます。他にも、ブラウザを口や足や目の動きで操作する人のための「支援技術」が用意されていて、Webは、本当に“すべての人”が不便なく利用できる、優秀なメディアなんだなーって感じます。:)
しかもそれは、HTMLで素直にマークアップするだけで実装されるのだから、ブラウザってほんとすごいんです。
- 「素直にマークアップ」とは
- その情報が持つ役割や意図に見合った「相応しいHTMLタグ」でマークアップすること。
例えば、そのページのメインコンテンツはmain要素にして、コラムやセクションはsection要素、タイトルならh1要素、段落ならp要素、強調したい単語やフレーズにはstrong要素やem要素を使ったり、ボタンにはbutton要素を使ったり、別ページへのリンクはa要素でhref属性を指定して、別サイトへのリンクにはtarget属性を指定したり、一覧はul要素、用語についての説明を箇条書きしたい時にはdl要素を使ったり……することです。
ブラウザは、「セマンティクスをしっかり明示する(コンテンツの意図がブラウザに伝わるように書く)」ようにすれば、スクリーンへの表示の仕方や、スクリーンリーダーの読み上げ方、マウスやキーボードで操作した時の反応を、意図した通りに実装してくれるんですね:)。HTMLを「マシンリーダブル」に書けば「アクセシブル」にも繋がるということです;)。マシンリーダぶればアクセシぶれるということです。
下サンプルは“素直なマークアップ”で作ったグローバルメニューです(別窓で開いてみてください)。
マウスやタッチ操作、キーボードやスクリーンリーダーでも操作できるのがわかります。
スクリーンリーダー(VoiceOver)の使い方
macOSならば、⌘+F5を押せば、「VoiceOver」というスクリーンリーダーが使えます。(もう一回⌘+F5を押せば終了します。)
VoiceOver利用中には以下のショートカットキーが使えます。
- カーソルの位置からすべて読み上げ
- control+option+A
- すべて読み上げの一時停止/再生
- control
- 次の項目へ進む
- control+option+→
- 前の項目に戻る
- control+option+←
- ローターを表示する
- control+option+U
- ローターを閉じる
- esc
control+optionは、Caps Lockでも代用できます。
ローターは、Webページの内容を、分類ごとに俯瞰できるべんり機能。
下図のように、「ランドマーク」、「見出し」、「リンク」、「フォームコントロール」などの項目が索引されて、気になるコンテンツに素早くアクセスすることができます。
WebサイトのUI
前項で、「素直にマークアップすればアクセシブルになる」みたいに書いたけれど、それだけじゃ事足りないケースがあります。むしろ事足りないケースの方が多いくらいです。
Webサイトはよく、コンテンツをより分かりやすく、伝わりやすくするために、情報に優劣を付けたり整理したりして、UIを工夫することがあります。
例えば、「ナビゲーションメニューの下層ページへのリンクは隠しておいて、メインページへのリンクにカーソルを乗せた時だけ表示する」ようなUIは、HTMLだけじゃ完結できなくて、CSSやJavaScriptを使って挙動を作ることになります。
そういったUIは、マウス操作やタッチ操作を想定して作られることが多いので、キーボードやスクリーンリーダーではうまく操作できないままだったりするんですね…。:‹
HTMLだけでは表現しきれない「UIの意図」を、ブラウザに伝えるために、「WAI-ARIA」があります。
WAI-ARIA
「WAI-ARIA(以下、ARIA属性という)」は、素直なマークアップだけでは伝えることが難しい“UIの意図”をブラウザに正しく伝えるために、「HTMLのセマンティクスを補完する」ということをします。HTMLにある語彙だけでは伝えきれない思いを伝える、手助けしてくれる存在です。
セマンティクスを織り成す「役割」と「プロパティと状態」を、それぞれ、「role属性」と、「aria-**属性」を使って補完します。
role属性の値には、heading
とかparagraph
とかlink
とか、HTML要素の“役割”を決めるキーワードを指定します。
aria-**属性は、属性自体にaria-label
やaria-hidden
など種類があって、値の指定の仕方もまちまちです。
キーワードにも指定する値にもすべて、事細かな決まりがあるので、詳しくは以下のページを参照のこと。
そして、HTML要素にも、最初から暗黙的にセマンティクスが決まっています。<h1>
の役割はもともとheadingだし、<p>
はparagraphで、<a href="">
にはlinkという役割が、最初から決まっています。
なのでrole属性はよっぽどの時(ARIAにあってHTMLにない役割を伝えたい時)にしか使いません。
「UIのセマンティクスを補完する」という名目でARIA属性を利用する場合は、主にaria-**属性の方を使います。
HTML要素が最初から持ってる“暗黙的なセマンティクス”については、以下のページに事細かに載っています。
ここからは実際に、「とあるUI」のセマンティクスを補完してゆきます。
ここでは、Webサイトで割とよく見る、所謂「ドロップダウンメニュー(カーソルを乗せると下層コンテンツへのリンクが現れるメニュー)」と、所謂「ドロワーメニュー(ボタンクリックで出したり隠したりできるグローバルメニュー)」を、できる限りアクセシブルにしてみます。
その前に、このUIの仕様と意図を解っておかないとです:)。
UIの仕様と意図
ドロップダウンメニューもドロワーメニューも、上サンプルのグローバルメニューを元に、ユーザビリティを底上げしたものを想定しています。上サンプルでは、メインページへのリンク(以下、メインリンクという)だけだったけれど、そこにさらに、下層ページへのリンク(以下、サブリンクという)も追加して、以下のように実装します。
- ドロップダウンメニュー
- メインリンクとサブリンクをいっぺんに表示するとヘッダーが盛り盛りになっちゃうので、サブリンクは隠しておいて、メインリンクにカーソルを乗せた時だけ表示されるようにします。
- ドロワーメニュー
- 画面右上に固定したボタン(以下、ハンバーガーボタンという)をクリックすると、メニューが現れたり隠れたりするようにします。画面の右端から出たり入ったりするので、リンクはたて並びのレイアウトになります。
下層ページの存在も知らしめたいからサブリンクも載せる訳だけれど、サブリンクはメインリンクよりも控えめにレイアウトして、優劣を付けます。
また、サイト内のコンテンツを、メインリンクのタイトルで分類して、その内訳をサブリンクのタイトルで一覧することで、サイト全体を俯瞰できるようにする目的もあります。
グローバルメニューがサイトマップのような役割も担っている、欲張りナビゲーションの集大成です;)。
レイアウトはレスポンシブに実装しているから、どんなデバイスでも問題なく表示されるはずです。
画面幅が広いデバイスでは「ドロップダウンメニュー」、画面幅が狭いデバイスでは「ドロワーメニュー」になるというわけです:)。
しかし、マウスやタッチでの操作を想定して作られているので、キーボード操作やスクリーンリーダーを使っての閲覧はからっきしです…:(。
補完、いってみましょう!
アクセシビリティの確保
ここでは、以下の4つの状況を想定しています。
このどれでもで、同じ感じに情報が届けられないとダメです。
- デスクトップでマウスを使って閲覧
- カーソルを動かして、リンクやボタンをクリックしたり、スクロールして閲覧します。
- デスクトップでキーボードを使って閲覧
- 矢印キーやspaceキーでスクロール、tabキーでクリック可能な要素へフォーカスを移動させながら閲覧します。フォーカス中にreturnキーを押せばクリックできます。
- スクリーンリーダーで音声を聞きながら閲覧
- 操作は前述参照。画面は見ずに、読み上げられる音声を頼りに閲覧します。
- モバイルデバイスでタッチ操作で閲覧
- 横幅の狭い画面を指でタッチして操作します。スワイプでスクロール、タップでクリック相当の操作になります。
デスクトップはmacOS、モバイルデバイスはiOSを想定。
今のところ、マウスを使って閲覧する時と、タッチ操作で閲覧する時は大丈夫そうだけれど、キーボードを使って閲覧する時のことや、スクリーンリーダーで音声を聞きながら閲覧する時のことは、何も考えられていない状態です:(。
セマンティクス補完の手掛かり
とはいえ、どうすれば使いやすいのか基準がわからないと、どこをどうしたら良いのやら、さっぱり不安ですよね…。そんな時に確認するとよいのが以下のサイト。
ARIA属性の仕様や使用方法は事細かく決められていて、ここに詳しく指南されています。デザインパターンごとにサンプルが用意されているので、実装時のお手本になりますね。
ただし、ここで紹介されているサンプルは、「ARIAを使って独自実装するとこんなに大変なんだよ」という例なので、全部が全部お手本通りにはしません。
あくまでARIA属性の使い処の参考として見ると良いです。
作りたいUIがお手本のサンプルのどれともマッチしない場合は、似たUIを組み合わせたり、アドリブを利かせて実装します。
この記事で実装するUIは、「ARIA Authoring Practices Guide」の中の、「Example Disclosure Navigation Menu with Top-Level Links」にとても近い感じがしますね。
「ディスクロージャ(開示)」という折りたたみ(非表示)と展開(表示)を可能にするパターンです。
ドロワーメニューも、このパターンに当てはまりそうです。
ともあれ、あくまでARIA属性の使い処だけ参考にして、作るUIの意図に沿って実装してゆくことになります。
ではいってみましょう!
キーボードを使って閲覧
前項のサンプルを、tabキーで、グローバルメニューを順にフォーカスしてゆくと、急にフォーカスがいなくなってしまうことがあります。どうやら、隠してるサブリンクにもフォーカスが移動してるみたいですね。
サブリンクにフォーカスした時にも、ドロップダウンすればよいでしょうか。
前項のサンプルでは:hover擬似クラスを使って、メインリンクにカーソルが乗った時にドロップダウンするようにしていたのだけれど、ここにさらに、:focus-within擬似クラスを使った指定も追加すれば、子要素にフォーカスしてる要素がある時にもドロップダウンするようになります。
フォーカスした時にも、メニューが展開するようになりました:)。
けどこれだと、UIの意図に背いてる感じがしますね…。せっかく隠してるのに、tabキーでメインコンテンツに辿り着くまで、ぜんぶ開いて見てかなきゃいけないなんて本末転倒ですX(。
ドロップダウンは開きたい時に開くようにして、フォーカスするのはいま表示されている要素だけにした方が、理に適ってるはず!
a要素の中に入れてたを外に出して、新たにbutton要素として、a要素の隣に置きました。ドロップダウンの開閉は、このボタン(以下、ドロップダウンボタンという)の上でreturnキーを押下して操作する手筈です。
さらに、ドロップダウンの中のサブリンクにはtabindex属性を付けてフォーカスが移動しないようにしておきます。
そして、ドロップダウンした時には、JavaScriptでtabIndex
の値を切り替えて、フォーカスできるようにします。
また、ドロワーが開いてる時に、ドロワーの外にフォーカスが移動してしまうと、フォーカス迷子の原因になりそうなので、ドロワーが開いてる時にはフォーカスをメニュー内から出さないようにします。
メニュー最後のリンク上でtabキーを押下した時は、ハンバーガーボタンへフォーカスを戻して、ハンバーガーボタン上でshift+tabを押下した時には、最後のリンクへフォーカスさせます。
キーボードでも、マウスと同じ感じで操作できるようになりましたね:D!
音声を聞きながら閲覧
上のサンプルを別窓で開いたらば、おもむろに⌘+F5を押して「VoiceOver」を起動します。
tabキーでクリック可能な項目を移動してゆくと、以下のような感じに読み上げられます。
- 閲覧済み、リンク、イメージ、Lopan.jp Sample、バナー 現在、リンク上にいます。このリンクをクリックするには、Control-Option-スペースを押します。
- 閲覧済み、リンク、企業情報、リスト6項目 現在、リンク上にいます。このリンクをクリックするには…
- ドロップダウン、ボタン, グループ 現在、ボタン, グループ上にいます。このボタンをクリックするには、Control-Option-スペースを押します。
- 閲覧済み、リンク、ニュース 現在、リンク上にいます。このリンクをクリックするには…
︙
- 閲覧済み、リンク、ブログ 現在、リンク上にいます。このリンクをクリックするには…
- ドロップダウン、ボタン, グループ 現在、ボタン, グループ上にいます。このボタンをクリックするには…
- リンク、お問い合わせ 現在、リンク上にいます。このリンクをクリックするには、Control-Option-スペースを押します。
VoiceOverが言うように、ドロップダウンボタン上でcontrol+option+spaceを押せばドロップダウンしてtab移動できるようにもなるし、問題なさそう:)。
VoiceOverでは、control+option+→で、クリック可能に関わらず項目を読み進めることもできます。上と同じ範囲を読み進めると、以下のような感じに読み上げられます。
- バナー 現在、Webコンテンツ内のバナー上にいます。このWeb領域を終了するには、Control-Option-Shift-上矢印を押します。
- 閲覧済み、リンク、イメージ、Lopan.jp Sample 現在、リンク上にいます。このリンクをクリックするには、Control-Option-スペースを押します。
- ナビゲーション 現在、ナビゲーション内にいます。
- リスト6項目 現在、リスト内にいます。
- 閲覧済み、リンク、企業情報 現在、リンク上にいます。このリンクをクリックするには…
- ドロップダウン、ボタン, グループ 現在、ボタン, グループ上にいます。このボタンをクリックするには…
- リスト3項目、レベル2 現在、リスト内にいます。
- 閲覧済み、リンク、メッセージ 現在、リンク上にいます。このリンクをクリックするには…
- リンク、ビジョン、3の2 現在、リンク上にいます。このリンクをクリックするには…
- リンク、会社概要 現在、リンク上にいます。このリンクをクリックするには…
︙
- 閲覧済み、リンク、ブログ、6の5 現在、リンク上にいます。このリンクをクリックするには…
- ドロップダウン、ボタン, グループ 現在、ボタン, グループ上にいます。このボタンをクリックするには…
- リスト4項目、レベル2 現在、リスト内にいます。
- リンク、サービス 現在、リンク上にいます。このリンクをクリックするには…
- リンク、デザイン・技術、4の2 現在、リンク上にいます。このリンクをクリックするには…
- リンク、カルチャー、4の3 現在、リンク上にいます。このリンクをクリックするには…
- リンク、Other 現在、リンク上にいます。このリンクをクリックするには…
- リストの終わり 現在、リスト上にいます。このリストの項目間を移動するには、Control-Option-右矢印 またはControl-Option-左矢印を押します。
- リンク、お問い合わせ 現在、リンク上にいます。このリンクをクリックするには…
- リストの終わり 現在、リスト上にいます。このリストの項目間を移動するには…
- 終わり、ナビゲーション 現在、ナビゲーション上にいます。
- 終わり、バナー 現在、Webコンテンツ内のバナー上にいます。このWeb領域を終了するには、Control-Option-Shift-上矢印を押します。
すごくぜんぶ読み上げてくれますね…!
しかも、tabキーの時と違い、ドロップダウンの中身まで読み上げられてしまいました…。
スクリーンリーダーが読み上げる内容は「アクセシビリティツリー」で確認することができます。
アクセシビリティツリーは、スクリーンリーダーなどの支援技術で使用される筋書きのようなもの。
ブラウザは、HTMLやCSS、JavaScriptによって生成されたWebページの内容を解析して、伝えるべき内容を組み立ててくれてるんです。
アクセシビリティツリーについて詳しくは下記ページを参照のこと。
HTMLやCSS、JavaScriptだけでは、伝えきれないUIの意図を補完してくれる役割を担うのがARIA属性です。ARIA属性で指定した値は、アクセシビリティツリーに影響を与えます。ブラウザが組み立てる筋書きを、意図して変えることができるんですね:D。
例えば、tabindex属性では伝えきれなかった「隠しているということ」は、aria-hidden属性で伝えることができます。
ARIA属性を指定すれば、スクリーンリーダーでの読み上げに、ドロップダウンメニューの表示/非表示の状態を反映することができるってわけです;)!
上サンプルでブラウザに伝わっていなかった、「サブリンクを隠していること」、「ドロップダウンボタンで開閉できること」、「画面幅が狭い時はメニューが隠れること」、「ハンバーガーボタンで開閉できること」。
この4つの想いをブラウザに伝えるために使うARIA属性は以下の通り。
- aria-controls属性
- 指定した要素がどの要素を制御するのかを伝えます。制御する要素のid名を指定します。
今回のサンプルの場合は、ハンバーガーボタンでメニューを制御、ドロップダウンボタンでサブリンクを制御します。 - aria-expanded属性
- 制御する要素(aria-controls属性に指定したidを持つ要素)が、表示されているかを伝えます。表示されているなら
true
、非表示ならfalse
を指定します。 - aria-hidden属性
- 指定した要素が非表示かどうかを伝えます。表示されているなら
false
、非表示ならtrue
を指定します。 - aria-label属性
- 指定した要素にフォーカスされた時に読み上げられる名前を指定します。
前述でaria-**属性は「プロパティと状態」を補完するのだけれど、上の4つの中では、aria-controls
とaria-label
は「プロパティ」を補完します。
「プロパティ」は、資産とか特性とか効能とかいう意味を持つ言葉で、aria-controls
はその要素に「どの要素を制御するのか」という効能を付与して、aria-label
はその要素に「その名前で読まれる」という特性を付与している感じ。
一方、aria-expanded
とaria-hidden
は「状態」を補完します。
その要素が隠れたり現れたりするたびに、要素の状態は変化するので、逐一ブラウザに状態の変化を報告しないといけません。UIを操作するたびに、JavaScriptで属性の値を書き換える必要があるんです。
そんな理由で、ARIA属性は、最初からJavaScriptで付けてゆきます:D。
ドロワーメニューに関するARIA
- ハンバーガーボタン(
<button type="button" class="nav_toggle">
)は、nav
というidを持つ要素を制御します。「メインメニュー」という名前で読んでもらって、最初は隠れている状態なので、aria-expanded属性でfalse
を指定しておきます。 - グローバルメニューのリンク一覧(
<ul class="nav_list">
)に、nav
というidを指定します。「メニュー」という名前で読んでもらって、最初は隠しているので、aria-hidden属性でtrue
を指定しておきます。
ドロップダウンメニューに関するARIA
- 各ドロップダウンボタンは、
sub-*
という連番のidを持つ要素を制御します。「メインリンクのメニュー(企業情報のところなら「企業情報のメニュー」となる)」という名前で読んでもらって、最初は隠れてるので、aria-expanded属性でfalse
を指定しておきます。 - 各サブリンク(
<ul class="nav_sub">
)に、sub-*
という連番のidを指定します。メインリンクと同じ名前で読んでもらって、最初は隠してるので、aria-hidden属性でtrue
を指定しておきます。
あとは、ドロワーメニュー、ドロップダウンメニューの開閉時に、aria-expanded属性とaria-hidden属性の値を書き換えます。
ドロワーメニュー開閉のARIA
開く時には、ハンバーガーボタンのaria-expanded属性の値をtrue
に、メニューのaria-hidden属性の値をfalse
にします。閉じる時はその逆です:)。
ドロップダウンメニュー開閉のARIA
ドロワーメニューとまったく同じ要領で、開く時には、ドロップダウンボタンのaria-expanded属性の値をtrue
に、サブリンクのaria-hidden属性の値をfalse
にします。閉じる時はその逆です:D!
スクリーンリーダー(VoiceOver)でも、UIの意図を汲んだ感じで操作できるようになりました;D!
まとめとあとがき
ブラウザはアクセシブルだけれど、HTMLだけで出来ない事をしようとした途端に、そこだけアクセシブルじゃなくなっちゃいます。だから、Webサイトにある「使いやすく工夫されたUI」は意識的に、マウス操作、タッチ操作、キーボード操作、スクリーンリーダーを使った操作を前提に作るようにします。
この記事では、「出たり隠れたりするUI」のアクセシビリティを確保するために、以下に気をつけましたよ!
- 開閉専用のボタンを用意して、returnキーを押下しても開閉できるようにしておくこと。
- ボタンに、JavaScriptでclickイベントを設定しておけば、クリックも、returnキーも、タップでも操作できるようになります。
- 表示されている内容と読み上げられる内容に齟齬がない状態を保つこと。
- 適宜「aria-controls属性」と「aria-label属性」を付けて、出したり隠したりするたびに「aria-expanded属性」と「aria-hidden属性」の値を切り替えるようにします。
最後まで読んでいただきありがとうございました!
以上、「できる限りのアリア。」でした。
参考文献: