ウェブアクセシビリティの基本:ランドマークロールを理解する

ウェブアクセシビリティの基本:ランドマークロールを理解する

ランドマークロール

WAI-ARIAには、role属性とaria-*属性があります。要素の役割を表すrole属性には、「ランドマークロール」と呼ばれるものがあり、その名の通り、目印としての役割を果たします。

ランドマークロールは、主に視覚障害者が利用するOSの機能であるスクリーンリーダーのユーザーがウェブページの重要な場所を確認したり、ウェブサイトやアプリケーション内で目的の場所にすばやく移動したりするのに役立つ構造的なメカニズムです。

スクリーンリーダーには各見出しや、ランドマークへジャンプする機能があります。VoiceOverの場合、ローターを起動することでその機能にアクセスできます。

スクリーンショット:ランドマーク、バナー、ナビゲーション、本文、フッター
macOS VoiceOverのローターメニュー

ランドマークは具体的には以下の8つがあります。

暗黙のロール

HTML要素はそれぞれ暗黙のロールを持ちます。その中にはランドマークロールになる要素があります。

以下のソースコードのように、body直下に置かれたheader要素は role="banner" 、main要素は role="main" 、footer要素は role="contentinfo" となります。

<body>
  <header>
    <!-- サイトヘッダー: role="banner" -->
  </header>
  <main>
    <!-- メインコンテンツ: role="main" -->
  </main>
  <footer>
    <!-- サイトフッター: role="contentinfo" -->
  </footer>
</body>

上記3つのロールは、ページ内に1つであるべきです(iframeなど入れ子になった文書の場合を除く)。

banner, contentinfo

ところがheader要素やfooter要素はページの中で複数回使用することができます。これらは、例えばsection要素、article要素、main要素などの内部で使用されることがあります。

下記はarticle要素の中でfooter要素を使用した例です。

<article>
  <!-- ブログ記事 -->
  <footer>
    <p><b>投稿日:</b><time>2023-05-17</time></p>
  </footer>
</article>

この例の場合、<footer>要素のロールはdivやspanと同じgenericとなり、ランドマーク要素として認識されません。ChromeのアクセシビリティパネルではFooterAsNonLandmark、Firefoxではsection、SafariではARIAロール一致なしと表示されます。(2023年5月現在)

つまり、祖先要素によってロールが変わります。
article、aside、main、nav、sectionまたはrole=article、complementary、main、navigation、regionの子孫要素でない場合に、header要素はbannerロール、footer要素はcontentinfoロールとなるのです。

main

main要素がもっている暗黙のロールです。メインコンテンツを表します。

アクセシビリティのガイドラインWCAGにブロックスキップという達成基準があります。複数のウェブページ上で繰り返されるコンテンツのブロックをスキップするメカニズムを可能にするというものです。スキップリンクを実装する場合もありますが、main要素を適切に使うことで達成できます。

💡スキップリンク

ページ冒頭におくメインコンテンツ等へジャンプするためのリンクです。tabキーでフォーカスが当たるまでは非表示にしているサイトも多いです。 ランドマークが適切に実装してあれば不要という意見もありますが、2022年の支援技術利用状況調査報告書では「よく使う」「時々使う」を含めると80%になるようです。

region

role="region"は、他に適切なランドマークが存在しない場合に使用される一般的なランドマークです。
regionロールにするにはsection要素を使ってマークアップします。
section要素はheader, footer要素の例のように、特定の条件下でロールが変わります。
section要素のデフォルトのロールは「対応するロールなし」であり、ランドマーク要素ではありません。ChromeのアクセシビリティツリーではSectionと表示されますが、これは独自の仕様のようです。

ところがアクセシブルネームを与えることで、section要素は暗黙のロールをregionに変更し、ランドマークになることができます。アクセシブルネームをつける方法としては、aria-labelledby属性、aria-label属性、title属性の3種類の方法がありますが、可視の見出し要素に関連付けるaria-labelledbyで実装するのがベストです。

<section aria-labelledby="name">
  <h1 id="name">Region Heading</h1>
</section>
aria-labelledby属性を使ってアクセシブルネームをつけた例

しかし、ウェブページ内すべてのsection要素にアクセシブルネームをつけるのは誤ったアプローチです。ランドマークが多すぎると、目的の位置に到達することが難しくなるためです。

ネイティブのHTML要素sectionを使うことが理想ですが、何らかの理由でsection要素を使用できない場合、例えばdiv要素に指定する場合でも、アクセシブルネームがある場合のみrole=”region”として認識されます。divで先ほどの例を実装すると以下のようになります。

<div role="region" aria-labelledby="name">
  <h1 id="name">Region Heading</h1>
</div>

div要素の暗黙のロールはgenericでセマンティクスを持たないという意味です。ちなみにgenericロールはユーザーエージェント実装者のためのロールで、私たちウェブ開発者は指定できませんので注意してください。また、ロールをWAI-ARIAで変更していないdiv要素はaria-labelledbyやaria-labelを使用してアクセシブルネームをつけることはできません。つまり、この例ではrole属性だけでもaria-labelledby属性だけでも無効となってしまいます。

navigation

名前の通り、ナビゲーションを表すランドマークです。
nav要素が暗黙のロールとしてnavigationをもっています。bannerやmainと違ってnavigationはページ内に複数あっても構いません。その場合はアクセシブルネームをつけて区別できるようにします。

スクリーンリーダーはランドマークの種類をアナウンスします。グローバルナビゲーションに「メインナビゲーション」というアクセシブルネームをつけた場合、「メインナビゲーション、ナビゲーション」とアナウンスされ冗長です。グローバルナビゲーションの場合には「サイト」「メイン」など、ページネーションの場合は「ページ」というように「ナビゲーション」という言葉は冗長になるので省きます。

また、すべてのリンク一覧をnavigationとする必要はありません。HTML仕様書にもfooterの利用規約などのリンク一覧に使う必要はないと記載されています。

反対にnavigationに含めた方がいい要素があります。それはドロワーメニューのボタンです。nav要素の外側に置かれている実装をよく見かけますが、ランドマークジャンプ機能でnavigationに移動した後に展開することを考慮するとnav要素の中に含めるのがよいでしょう。

complementary

メインコンテンツを補完する独立した情報を表します。
このロールはHTMLのaside要素が暗黙のロールとしてもっています。同一ページ内で複数使うことができますが、navigation同様アクセシブルネームをつけて区別できるようにします。

form

フォームのコンテンツのまとまりを表します。
form要素にアクセシブルネームがある場合にはformランドマークになり、ない場合にはランドマークとして認識されません。

またフォームの内容が検索の場合には次に説明するsearchロールを使用してください。

ページに複数のフォームがある場合には一意のアクセシブルネームを与えてランドマークとしてマークアップし、それぞれのフォームの目的がわかるようにします。

search

searchロールは検索エリアを表します。
これまでは、role="search"の暗黙のロールを持つHTML要素が存在しなかったため、WAI-ARIAをform要素等に使用して記述していました。

嬉しいことに、今年3月HTMLの仕様にsearch要素が追加されました。これにより、すべてのランドマークに対応するネイティブのHTML要素が揃いました!

form要素を使わずJavaScriptを使用する場合は検索ボックスを含むエリアをsearchタグで囲むだけです。

<search>
  <label for="id">サイト内検索</label>
  <input type="search" id="id">
  <button type="button">検索</button>
</search>

form要素を使用する場合はform要素ごと囲みます。

<search>
  <form action="search.php">
    <label for="query">サイト内検索</label>
    <input id="query" name="q" type="search">
    <button>検索</button>
  </form>
</search>

ただし、仕様に追加されたばかりでユーザーエージェントではまだ対応されていません。当面はrole="search"で補完することで正しく認識されます。

<search role="search">
  <label for="id">サイト内検索</label>
  <input type="search" id="id">
  <button type="button">検索</button>
</search>
<search role="search">
  <form action="search.php">
    <label for="query">サイト内検索</label>
    <input id="query" name="q" type="search">
    <button>検索</button>
  </form>
</search>

また、searchロールが複数になる場合にはアクセシブルネームをつけて区別できるようにします。

おわりに

ランドマークロールについて、見落としがちなポイントを中心に解説しました。

すべての視覚可能なコンテンツはランドマークの中にマークアップします。
そうすることで、スクリーンリーダーユーザーは本文を読んでいる途中で検索エリアにジャンプして検索したり、ナビゲーションから操作したりするすることができます。

実装上の注意点としては文中にも書きましたが、可能な限りネイティブのHTML要素を使うことです。

また確認方法としてスクリーンリーダーの使い方もぜひ覚えるとよいですが、慣れないうちはブラウザの拡張機能Landmarksでも確認ができるので試してみるとよいでしょう。