淡々とWAI-ARIAについて書く1
HTML, Accessibility淡々と WAI-ARIA の role や state についてまとめた。 WAI-ARIA 1.2 が対象。
- role="heading"
- role="button"
- role="main"
- role="banner"
- role="document" と role="application"
- role="navigation"
- role="contentinfo"
role="heading"
https://www.w3.org/TR/wai-aria-1.2/#heading
文書の構造に関わる、「文書構造ロール」のひとつ。
heading ロールは見出しを表現する。対応する要素は h1
から h6
となる。
レベルは aria-level
属性で設定する。1 以上の整数で指定し、初期値は 2 となる。
html
<div role="heading" aria-level="1">見出しレベル1</div>
HTML の要素にない特徴として、7 以降のレベルを設定できる。
上限は不明だけど、VoiceOver は 9 までを認識する。
HTML との整合性を保つため、7 以降を設定するのは避けるべきとされる。
(MDN の Accessibility Concerns より)
html
<div role="heading" aria-level="9">見出しレベル9</div>
h1
から h6
のスタイルを避けるため、この role を使って見出しコンポーネントを作るケースもある。
(それを推奨しているわけではない)
tsx
import * as React from 'react'
type Props = {
level: number
}
export const HeadingComponent: React.FC<Props> = props => {
return (
<div role="heading" aria-level={props.level}>
{props.children}
</div>
)
}
hgroup
の子要素にもなれるはずだけど、hgroup
あたりの実装がされていないので詳細は不明。
(セクショニングや hgroup
については闇の歴史があり、別の記事でまとめる)
role="button"
https://www.w3.org/TR/wai-aria-1.2/#button
ボタンであることを示す。対応する要素は button
。
html
<div role="button" tabindex="0">ボタンだよ</div>
button
は対話型コンテンツ(interactive content)のひとつで、フォーカス可能。
しかし div
はフォーカス可能ではないので、tabindex
属性と共に用いることが望ましいとされる。
また、ロールは本来の button
要素が持つ、Enter キーや Space キーを押下した時の挙動を再現しない。
キーボードイベントもハンドラに追加する必要があり、取り扱いには注意が必要。
tsx
import * as React from 'react'
type Props = {
handleClick: () => void
}
export const ButtonComponent: React.FC<Props> = props => {
const onKeyDown = React.useCallback(
(event: React.KeyboardEvent<HTMLDivElement>) => {
// Enter か Space キーが押されているかチェック
if (event.key === 'Enter' || event.key === ' ') {
props.handleClick()
}
},
[props],
)
return (
<div
role="button"
tabindex="0"
onClick={props.handleClick}
onKeyDown={onKeyDown}
>
ボタンだよ
</div>
)
}
キーを押して props.handleClick()
を発火させるのは変だけど…キー操作は mousedown
と mouseup
を模倣し、それらの action で発火するものが click なので、あながち間違いとは言えない。
本来の button
にない利点は、キーボードでボタンを押す時の奇妙な振る舞いを再現しないこと。
Chrome では、ボタン上でSpaceキーを押すと keyPress
時にイベントが発火するけど、Firefox では keyUp
で発火する。なぜこの仕様なのかは、GUI の歴史から追っている途中…。
また、button
特有のスタイルからも解放される。
例えば、react-aria-menubuttonは、開発者が CSS でスタイルを設定をしやすいように、この role で button
を表現している。
role="main"
https://www.w3.org/TR/wai-aria-1.2/#main
ランドマークロールのひとつ。ランドマークは特定のコンテンツの場所を示すもので、支援技術のナビゲートを助ける役割を持つ。
main ロールは主要なコンテンツの場所であることを示す。対応する要素は main
。
html
<main role="main"></main>
通常、main
要素には暗黙的な main ロールが与えられるので、わざわざ同じロールを付ける必要はないけど、 IE は main
要素に対応してなくて、対応させるために同じ role の main ロールを与えるハックをしている。
このように、後方互換性を保つため二重のロールが与えられているケースもあり、闇を感じさせてくれる。
そもそも IE はこれを block ではなく inline な要素として解釈するので、余計な display: block;
を避けるためにも、main
要素を使わず、div
要素に main ロールを付けて表現することがある。
html
<div role="main"></div>
main ロールは 1 ページに 1 つでなければいけないけど、hidden
属性があるものは無視されるので、動的に切り替える目的であれば複数記述できる。
html
<div role="main"></div>
// hidden 属性があるので認識されない
<div role="main" hidden></div>
role="banner"
https://www.w3.org/TR/wai-aria-1.2/#banner
ランドマークロールのひとつ。
ページの上の方にあって、サイト名とかコンテンツの導入的な部分を示す。
関連する要素は header
。
html
<body>
<header></header>
</body>
html
<body>
<div role="banner"></div>
</body>
body
要素の直下にない banner ロールは役割を持たない。
HTML Accessibility API Mappings 1.0 では、body
の中とそうでない場合で明確にロールを分けている。
例えば、main
, article
, section
などの要素内に記述された header
要素は、ロールを持っていない。
html
<body>
<header><h1>ヘッダー</h1></header>
<main>
<!-- これは問題ないが、bannerとしての役割がない -->
<header><h2>メインのヘッダー</h2></header>
</main>
</body>
上記も踏まえて、ページ内で認識できる banner ロールは基本的に 1 つ。
ただし、document ロールや application ロールがそれぞれの banner ロールを持つのは可能。
(document ロールと application ロールについては次の項目で説明)
html
<body>
<header id="header">
<h1>ヘッダー</h1>
</header>
<div id="nestedDocument" role="document" tabIndex="0">
<header id="nestedDocumentHeader">
<h2>ネストされたドキュメントのヘッダー</h1>
</header>
</div>
</body>
article
のヘッダー部分、という位置付けで header
が使われるケースもある。
役割的に意味はないけど、div
を使うよりは見通しが良いかも。
html
<body>
<!-- banner ロールが付くのはここ -->
<header>
<h1>ホームページ</h1>
</header>
<main>
<article>
<!-- ここは banner ではないが、問題なし -->
<header>
<h2>Article です</h2>
</header>
<section>
<h3>Section です</h3>
<p>p です</p>
</section>
</article>
</main>
</body>
role="document" と role="application"
banner ロールの例に出てきたやつらで、それぞれが文書構造ロールにあたる。
これらのロールは、支援技術の読み取りに関係する。
https://www.w3.org/TR/wai-aria-1.2/#document
document ロールは、支援技術に読み取りをさせたいコンテンツであることを示す。
要するに対応する要素は Web ページそのもので、デフォルトで読み取りに対応している。
その中でもフォーカスさせたいテキストがある時に、tabindex
を併用して使われることがある。
html
<div id="mydocument" role="document" tabindex="0">
<p>俺の声が聞こえるか</p>
</div>
https://www.w3.org/TR/wai-aria-1.2/#application
一方 application ロールは、支援技術の入力をアプリケーションに伝えることを示す。
その名前から誤解されがちだけど、ページがアプリケーションであることを示すものではない。
button
や input
などの対話型コンテンツ、HTML や WAI-ARIA だけでは表現しきれない、多様な操作を求めるコンポーネントに対して使用するもの。
例えば、WAI-ARIA 1.2 での説明では、HTML で表現することが難しい「スライド作成ツール」を挙げていて、実際にGoogle Slideは、body
要素で application ロールを使用している。
application は、内部にフォーカス可能な要素が必ず 1 つはあるべきとされる。
また、フォーカス可能な要素を管理するため aria-activedescendant
属性などを併用する必要がある。
aria-activedescendant
は他の記事で触れるけど、フォーカスされている要素の id を指定するもの。
html
<!-- 例としては良くないものです -->
<div id="myapp" role="application" tabindex="0">
<div role="banner">何らかのアプリケーション</div>
<div role="toolbar" aria-activedescendant="button02" tabindex="0">
<div id="button01" role="button" aria-pressed="false" tabindex="0">
その1
</div>
<div id="button02" role="button" aria-pressed="true" tabindex="0">
その2
</div>
<div id="button03" role="button" aria-pressed="false" tabindex="0">
その3
</div>
</div>
</div>
Google レベルのデスクトップアプリケーション的な UI を構築する時に、はじめて考えるものかもしれない。
操作を一手に担うため、使うには大いなる責任が伴う。
role="navigation"
https://www.w3.org/TR/wai-aria-1.2/#navigation
ランドマークロールのひとつ。
ページのナビゲーション(リンク集など)が入る部分で、対応する要素は nav
。
aria-label
や aria-labelledby
属性を設定すると、それがナビゲーションの説明になる。
例えばスクリーンリーダーは、下記を サンプルページへのリンク集, ナビゲーション
のように読み上げる。
html
<!-- aria-labelが説明になる -->
<nav aria-label="サンプルページへのリンク集">
<ul>
<li><a href="./knowheading/">headingを知る</a></li>
<li><a href="./knowbanner/">bannerを知る</a></li>
<li><a href="./knowheading/">headingを知る</a></li>
</ul>
</nav>
<!-- 中に見出しなどがある場合、aria-labelledbyを使ってidと紐づけられる -->
<nav aria-labelledby="navigationHeading">
<h2 id="navigationHeading">サンプルページへのリンク集</h2>
<ul>
<li><a href="./knowheading/">headingを知る</a></li>
<li><a href="./knowbanner/">bannerを知る</a></li>
<li><a href="./knowheading/">headingを知る</a></li>
</ul>
</nav>
navigation ロールは 1 つのページに複数置ける。それぞれに説明があると良さそう。
role="contentinfo"
https://www.w3.org/TR/wai-aria-1.2/#contentinfo
ランドマークロールのひとつ。
著作者など、ドキュメントの情報を示す時に使われる。
対応する要素は footer
。
html
<div role="contentinfo">フッターです</div>
<footer>同じくフッターです</footer>
こちらも banner と全く同じで基本 1 つ、body
の子でない footer
にロールはない。
また document ロールや application ロールを持った要素の中で、別のものとして扱える。
おわり
つづく