WAI-ARIA presentation(none) ロールの仕様、aria-hidden=”true” との違い

木々が生い茂る森の写真の上にROLE PRESENTATIONの文字が重なっている、ROLEとPRESENTATIONについた括弧は透明で消えかかっており、PRESENTATIONだけがはっきりと見えている

こんにちは、開発ディレクターの横内です、腹筋してください。社内でほそぼそとアクセシビリティの布教活動に勤しんでいます。ウィルゲート Advent Calendar 2019 の 16 日、始まります。

presentation ロールは ARIA で定義されているロールの一つです。要素のロールへ presentation ロールを指定することで、要素がもつネイティブセマンティクスロールを上書きして、特にセマンティクスを持たない要素としてブラウザや支援技術から扱われるようにできます。

元々筆者は presentation ロールについてなんとなくの理解はしておりました。ただ、aria-hidden ステートを利用しても同じようなことを実現できる気がしていたため、「どちらを使えばよいのだろう?」という疑問がありました。presentation ロールへの正しい理解なしにはその疑問は晴れないでしょう。本稿では presentation ロールに関する知識を深めた上で、aria-hidden ステートとの違いについて考えていきます。

本稿につきましてはARIA 1.1の仕様を元にして、2019年12月に執筆しております。仕様の鮮度に関してはご注意下さい。

要約

  • presentaion ロールを設定された要素は、span 要素や div 要素と同じく特定のセマンティクスを持たなくなる。
  • role="presentation" はネイティヴセマンティクスを削除するために使い、 aria-hidden="true" は要素そのものをアクセシビリティツリーから削除するために使う。
  • Using ARIA2.9 Use of Role=presentation or Role=noneを読むのが一番わかりやすい。

アクセシビリティツリー

presentation ロールについて解説する前に、前提知識としてアクセシビリティツリーについて(極めて)簡単に述べておきます。ブラウザは HTML/CSS をレンダリングツリーに変換したあと、スクリーンリーダーなどの支援技術が利用しやすいように、さらにアクセシビリティツリーと呼ばれる形式に変換します。支援技術はアクセシビリティ API を介してアクセシビリティツリーを読み取ります。

アクセシビリティツリーにぶら下がるアクセシビリティノードは、名前、説明、ロール及びステートの 4 つの情報しか持っていません。ブラウザは記述した HTML/CSS を、よりプリミティブな形にして支援技術に提供しています。

スクリーンショット: DevToolsでheading要素を選択している。Accessibility ペインにはアクセシビリティツリーと、アクセシビリティノードの詳細な情報が表示されている。
Chrome DevToolsを使うと構築されたアクセシビリティツリーを覗くことができます。

仕様

presentation ロールの定義を「presentation (ロール) - Accessible Rich Internet Applications (WAI-ARIA) 1.1 日本語訳」より引用します。

暗黙のネイティヴロールセマンティックスがアクセシビリティAPIに対応づけされない要素。

https://momdo.github.io/wai-aria-1.1/#presentation

presentation ロールが適用された要素は、ネイティヴロールセマンティクスがないものとして扱われます。加えて、ネイティブロールに関連するステートやプロパティも同じように削除されます。ただし、要素がもつコンテンツ(テキスト等)自体は削除されません。

<!-- ① -->
<h4 role="presentation" aria-level="2">人類皆腹筋が割れている</h4>

<!-- ② -->
<span>人類皆腹筋が割れている</span>

<!-- ③ -->
人類皆腹筋が割れている

サンプルコードの①〜③まで、すべて同じ意味になります。h4 要素に付与されている aria-level は、h4 要素のネイティヴロールである heading ロールに関連する ARIA プロパティであるため、ないものとして扱われます。

また、子要素を必要とする要素(table 要素や ul 要素など)に presentation ロールが付与されていた場合、子要素のネイティヴセマンティクスも削除されます。

ただし、例外もあります。フォーカス可能な要素に presentation ロールが付与されている場合は、要素が理解可能かつ操作可能であることを保証するために、ネイティブセマンティクスは削除されません。例えば入力可能なテキストフィールドに presentation ロールが付与されていても、引き続き入力ができるよう presentation ロールは無視されます。

ユースケース

では presentation ロールは一体どういったユースケースで利用するのでしょうか。Accessible Rich Internet Applications (WAI-ARIA) 1.1 日本語訳 から引用します。

ユースケースの例:

・コンテンツが完全にプレゼンテーションな(スペーサー画像、装飾的なグラフィック、または要素をクリアするような)要素。
・完全な代替テキストが使用可能でありaria-labelledbyと(必要な場合)aria-describedbyでマークアップされるimgロールをもつコンテナにおける画像。
・CSSの追加のマークアップ"フック"として使用される要素。または
・レイアウトテーブルおよび/またはその関連する行、セル、その他のいずれか。

https://momdo.github.io/wai-aria-1.1/#presentation

それぞれ順を追って詳しく見ていきます。

コンテンツが完全にプレゼンテーションな要素

昔盛んに使われていたレイアウト手法の一つとして、スペーサー GIF があります。透明かつ 1×1px の GIF 画像をサイズを指定して挿入することで、余白をコントロールする手法です。挿入された画像は文章的には何の意味も持っていませんから、presentation ロールを使用して img ロールを削除することが推奨されます。

<p>腹筋ローラーの力を?</p>
    <img role="presentation" alt="" src="/img/spcaer.gif" height="30">
<p>信じろ!</p>

なお、aria-hidden="true" を使えば img 要素をアクセシビリティツリーから削除することができるため、aria-hidden を利用しても良いでしょう。

img ロールを持つコンテナにおける画像

img ロールを付与されたコンテナ要素の中に、alt が空の img 要素とコンテナ全体の説明を含むマークアップパターンの場合、img 要素へ presentation ロールをつけます。

<div role="img" aria-labelledby="caption">
    <img src="/event/20191001.jpg" alt="" role="presentation">
    <p id="caption">腹筋ローラーで激しく筋肉を追い込んでいる様子</p>
</div>

全体を包む div 要素に img ロールが付与されているため、div 以下の要素はまとめて画像として扱われます。div 要素は aria-labelledby で紐付けれれた p 要素によりテキストで説明されているため、alt が空である img 要素は純粋な視覚コンテンツとしてしか機能していません。なので img 要素は presentation ロールを指定してネイティヴセマンティクスを隠します。 逆に言えば、presentation ロールが付与された img 要素には、意味のある alt 属性は付与してはいけません。

レイアウトテーブルおよび/またはその関連する行、セル、その他のいずれか

Web の歴史をたどると、table 要素がレイアウトに使われていた時代がありました。日本では「テーブルレイアウト」という呼称が有名かもしれません。そのような使い方はセマンティクス的には正しくありませんから、ロール属性へ presentation を指定することで table ロールを削除します。

table 要素には tr 要素や td 要素といった、必ず持つ子要素があります。親である table 要素に presentaion ロールを設定した場合、必須である子要素にもpresentation ロールが継承され、ネイティヴセマンティクスが削除されます。ただし、子要素の td の中にさらに table 要素を追加した場合は、ネストした table 要素には presentation 要素は継承されません。この継承は例えば、 ul 要素と li 要素でも見られます。ul 要素に presentation ロールを設定した場合も子である li 要素のネイティヴセマンティクスは削除されます。

<table role="pressentaion">
    <tr><!-- ① -->
        <th><!-- ② -->
            <h1>……</h1><!-- ③ -->
        </th>
    </tr>
    <tr>
        <td>
            <table>……</table><!-- ④ -->
        </td>
    </tr>
    ……
</table>

①及び②はネイティヴセマンティクスを削除され、③及び④は削除されません。

実際のコードを開発者ツールを使って覗いてみて下さい。

See the Pen role="presentation" DEMO by hiroki yokouchi (@8845musign) on CodePen.

余談ですが、現在は table 要素を使ったようなレイアウトをしたい場合は、display: table を使えば実現可能です。また、Flexbox や CSS Grid などの柔軟なレイアウト手段が存在することから table をレイアウトに用いる必要性はほぼゼロになっていると言えます。

ロールの競合解決

仕様ではロールが競合した場合の解決ルールが決まっています。すでに本稿で述べられている内容も含みますが、仕様に述べられているルールを一通り列挙していきます。

競合解決の 5 ルール:

  1. span や div などの暗黙的に presentation ロールを持つ要素はアクセシビリティツリーに公開されてはならない
  2. グローバル ARIA ステートおよびプロパティ※1は常にアクセシビリティAPIに公開されなければならない
  3. グローバル ARIA ステートおよびプロパティが要素に設定されていた場合、presentation ロールは無視され、暗黙的なネイティヴセマンティクスはアクセシビリティツリーに公開される
  4. prentation ロールが継承された子要素に、role 属性によりセマンティクスが設定されていた場合、アクセシビリティツリーがおかしな構造を取る場合、アクセシビリティツリーを修復しても良い
  5. presentation ロールを持つ要素がフォーカスを持つ、もしくはインタラクティブな要素である場合は、presentation ロールを無視してネイティブセマンティクスを公開しなくてはならない

※1 Aria in HTMLのARIA Roles, States and Propertiesにまとめがあります。

いくつかのルールを詳しく説明します。

ルール 3「グローバル ARIA ステートおよびプロパティが要素に設定されていた場合〜」

罠です。例えば h2 要素に グローバル ARIA プロパティである aria-live が設定されていた場合、presentation ロールを無視して、ネイティヴセマンティクスがアクセシビリティAPIに公開されます。presentation ロールを設定したからといって、必ずしもネイティヴセマンティクスが削除されるわけではありません。注意しましょう。

"presentation"、aria-live="polite" が指定された h2 要素を ChromeのDevTools でアクセシビリティペイン通して見ると、role に heading が設定されている。

(コード)

ルール 4「prentation ロールが継承された子要素に、role 属性によりセマンティクスが設定されていた場合〜」

筆者が一番理解に頭を悩ませたルールです。サンプルコードを元にルールについて説明します。

<ul role="presentation">
    <li role="cell">Item</li>
</ul>

ul 要素に presentation ロールが付与されているため、ul 要素自体はセマンティクスを持ちません。子要素には cell ロール(テーブル等のセルを表します)が付与されています。アクセシビリティツリー上ではcell ロールを待った要素が単独で配置されてしまっています。本来なら、cell ロールを持つ要素は、table ロールや row ロールを持つ要素を親に内包されているべきです。

このような場合に、ユーザーエージェント(ブラウザ)はアクセシビリティツリーをよしなに修復してもよいということがルールで言われています。例えばcell ロールを削除する、table ロールや row ロールを補完する、などの対応が考えられるでしょう。ただし、すべてのブラウザにおいて実装が MUST であるわけではありません。

アクセシビリティツリーの修復は Chrome 81.0.4018.0(Official Build)canary (64 ビット)にて確認することができました(2020 年 1 月5 日時点)。サンプルコードの例では li 要素に generic ロール が付与され、roleによって付与されたセマンティクスが削除されました。

aria-hidden ステートとの使い分け

presentation ロールと用途を混同しやすいものとして、aria-hidden ステートが挙げられます。presentation ロールは要素のネイティブセマンティクスを削除し、aria-hidden ステートは aria-hidden="true" としたときに、要素そのものをアクセシビリティツリーから隠します。presentaion ロールは既存のマークアップから問題のあるロールを削除するために使い、aria-hidden ステートは文章構造自体には関与しない要素を削除するために使います。

例えば見出しなどで、ある単語が繰り返し使われている例を考えてみましょう。

See the Pen Text Effect by hiroki yokouchi (@8845musign) on CodePen.

繰り返されている単語の片方は、単語を装飾するためにエフェクトが掛けられており、一方の単語の背景に配置されています。エフェクトが掛けられた単語は意味的には必要がありません。この様な場合はaria-hidden ステートを使って片方の単語をアクセシビリティツリーから隠してしまうのがよいでしょう。presentation ロールを使用した場合は、もちろん文字列自体は削除されません。この場合文章は「腹筋ローラーの力を信じろ 腹筋ローラーの力を信じろ」となり、腹筋ローラーへの信仰心をひどく強要する表現となるでしょう。

混同しやすい presentation ロールと aria-hidden ステートですが、使い所を間違えないようにしましょう。

presentation ロールと同義の none ロール

ARIA1.1 から presentation ロールと同じ働きをする none ロールが追加されました。presentation ロールは単語「presentation」や「presentational」から連想する意味により誤解を招くことがあります。また、先に述べた aria-hidden="true" との混同も大きな問題だったのです。「none」としたほうが、正しくロールの意味を伝えやすいと思います。

現在のモダンブラウザは none ロールをサポートしていますので、presentation ロールではなく none ロールを利用すると良いと思います。ただし、Internet Exploer 11 はサポートしていませんので注意が必要です(今更IEを意識する必要はないのかもしれませんが)。

まとめ

適切にマークアップを行っていれば presentation ロールが必要となるケースは殆どないと思います。CSS の表現力が飛躍的に向上した今、無理やりなマークアップを行う必要がなくなってきたためです。

ただし、どうしても必要なケースでは aria-hidden="true" との使い分け、継承ルールや競合の解決を理解した上で使用しましょう。正直どう動作するのか分かりづらい点も多いため、使わないことに越したことないと思います。

なお、仕様とにらめっこしながら勉強していた私ですが、

ARIAの使い方についてはUsing ARIAを見ろという神の啓示をいただきました。わかりやすいです。皆さんもぜひご覧ください。

明日のウィルゲート Advent Calendar 2019 は msm_2 さんより「SwiftUIを使ったアーキテクチャ」です。 ここまでお読みいただきあありがとうございました。

参考文献