本文へジャンプ

インクルーシブなナビゲーションの実装(2)

Posted by MONSTER DIVE

前回はナビゲーションのマークアップまでを実装しました。
今回は引き続き、実際の操作を意識したマークアップ、role属性、aria属性の実装をしていきます。

実装:マークアップ

引き続きこのMD-Blogのナビゲーションを例に、表示領域の幅が狭い場合は折りたたまれるよう(ハンバーガーメニューになるよう)に実装します。
前回作成した「.global-nav」に、メニューを開閉するためのbutton要素を追加しました。classの命名については今回は省略します。

<nav class="global-nav">
    <button class="global-nav__button" type="button">メニュー</button>
    <ul class="global-nav__list">
        <li class="global-nav__item"><a href="/about/">About</a></li>
        <li class="global-nav__item"><a href="/service/">Service</a></li>
        <li class="global-nav__item"><a href="/project/">Project</a></li>
        <li class="global-nav__item"><a href="/job/">Recruit</a></li>
        <li class="global-nav__item"><a href="/blog/">Blog</a></li>
        <li class="global-nav__item"><a href="/contact/">Contact</a></li>
    </ul>
</nav>

フォーカスと要素の配置

button要素をnav要素の中に配置するのには理由があります。

聴覚的にウェブサイトを閲覧するユーザー(閲覧時にキーボードを操作するため、以降はキーボードユーザーと呼びます)は、ランドマークロール毎に要素間を移動することができるスクリーンリーダーのジャンプ機能などを使用して、目的のコンテンツへとスムーズにページ内を移動します。
しかし、ナビゲーションにたどり着いた時にメニューが閉じており、メニューを操作するボタンがナビゲーションの外に配置されていたらどうなるでしょう。キーボードユーザーはナビゲーションを開くためのボタンが見つけられずに迷子になってしまいます。

ここではランドマークロール毎に要素間を移動するという部分がポイントです。ナビゲーションランドマークロールに移動した後、開閉ボタンにたどり着けるようにbutton要素はnav要素の中に配置します。
さらに、キーボードを使用した要素の移動は記述の上から順に行われるため、nav要素の最初の子要素として配置するとよいでしょう。また、button要素にフォーカスした後すぐにリンクにたどり着けるため、後続にメニューであるul要素を配置します。

ボタンのアイコンと読み上げ

先ほど配置したbutton要素に、メニューを表す3本線のアイコンを描画します。また、見た目をアイコン単体のボタンにしたいので「メニュー」というテキストをVisually hiddenというCSSのテクニックで視覚的に非表示にしつつ、スクリーンリーダーの読み上げを可能にします。
Visually hiddenについてはご存知の方も多く、既に詳しい記事がたくさんあると思うので、調べてみてください。

<nav class="global-nav">
    <button class="global-nav__button" type="button">
        <span class="global-nav__button-icon"></span>
        <span class="_visually-hidden">メニュー</span>
    </button>
    <ul class="global-nav__list">
        <li class="global-nav__item"><a href="/about/">About</a></li>
        <li class="global-nav__item"><a href="/service/">Service</a></li>
        <li class="global-nav__item"><a href="/project/">Project</a></li>
        <li class="global-nav__item"><a href="/job/">Recruit</a></li>
        <li class="global-nav__item"><a href="/blog/">Blog</a></li>
        <li class="global-nav__item"><a href="/contact/">Contact</a></li>
    </ul>
</nav>

アイコンにi要素を使用している例をまれに見かけますが、i要素は、

代わりの声やムードでテキストの範囲を表すか、またはそうでなければ、たとえば分類学上の名称、専門用語、他言語の慣用句、意見、または西洋のテキストで船名など、異なる品質のテキストを示す方法で通常の文からのオフセットを表す。

「HTML(WHATWG Living Standard)」日本語訳より引用

とあり、アイコンとして使用するのは少し不自然に感じるため、span要素を使用しましょう。

これは.global-nav__button-iconというclassに背景画像としてアイコンを指定している例ですが、背景画像での描画には問題があります。
配色の区別が難しいユーザーや、通常の配色では眩しいと感じるユーザーなどが使用する、ハイコントラストモード(画面の配色を白黒反転する設定。全く別のものですが、最近増えてきたダークモードを想像すると分かりやすいかもしれません)を設定すると背景画像が反転せず見えなくなってしまいます。

<nav class="global-nav">
    <button class="global-nav__button" type="button">
        <span class="global-nav__button-icon">
            <svg>
                <g>
                    <path ...>
                </g>
            </svg>
        </span>
        <span class="_visually-hidden">メニュー</span>
    </button>
    <ul class="global-nav__list">
        <li class="global-nav__item"><a href="/about/">About</a></li>
        <li class="global-nav__item"><a href="/service/">Service</a></li>
        <li class="global-nav__item"><a href="/project/">Project</a></li>
        <li class="global-nav__item"><a href="/job/">Recruit</a></li>
        <li class="global-nav__item"><a href="/blog/">Blog</a></li>
        <li class="global-nav__item"><a href="/contact/">Contact</a></li>
    </ul>
</nav>

ということで、背景画像に設定していたアイコンをinline SVGとして持ってきました。

SVGの代替テキスト

img要素にalt属性があるように、SVGにもtext要素、title要素、desc要素というものがあり、それぞれの要素を使用することでテキスト情報を付与することができます。
今回はtitle要素とdesc要素を使用して、3本線のアイコンに代替テキストを設定します。

<svg>
    <g>
        <title>メニューアイコン</title>
        <desc>3本の横線が縦に並び、メニューの折りたたみを表している</desc>
        <path ...>
    </g>
</svg>

title要素は短い説明に使用され、desc要素との併用も許可されており、title要素のテキストが描画されることはありません。
desc要素はより詳細な説明をする場合に使用され、title要素との併用も許可されており、title要素と同じく要素のテキストが描画されることはありません。

ここまで実装したところで、別の問題が浮上します。title要素、desc要素で設定したテキストを、現状スクリーンリーダーは読み上げてくれません。
キーボードユーザーにもこれがアイコンであること、設定したtitle要素とdesc要素をスクリーンリーダーに読み上げてもらうために、aria属性と、前回説明したrole属性を使用します。

<svg role="img">
    <g>
        <title>メニューアイコン</title>
        <desc>3本の横線が縦に並び、メニューの折りたたみを表している</desc>
        <path ...>
    </g>
</svg>

先にrole属性から見ていきましょう。role="img"は、

描画オブジェクトのコレクションによって形成されているかどうかに関わりなく、文書内の1つのグラフィックを表す。

Accessible Rich Internet Applications (WAI-ARIA) 1.2 日本語訳より引用

とあり、グラフィックであるメニューアイコンにはimgロールを付与します。 さらに、

imgのロールをもつ要素を認識可能にするために、著者はaria-labelまたはaria-labelledby属性を使用してラベルを提供しなければならない。

Accessible Rich Internet Applications (WAI-ARIA) 1.2 日本語訳より引用

とあるので、svg要素にラベルの提供をしていきます。
aria-label属性とaria-labelledby属性はほぼ同義ですが、HTML上にラベルとなるテキストがある場合はaria-labelledby属性、ない場合はaria-label属性を使用します。今回の例では、描画はされないですが、title要素、desc要素としてラベルに設定したいテキストがHTML上に存在するので、aria-labelledby属性を使用します。

実装:aria属性

aria属性とはWAI-ARIAで定義されている仕様で、要素の状態を明示するものです。
値はそれぞれの属性によりtrue/falseや整数、文字列などの場合があるので、使用前に確認しましょう。

今回使用するaria-labelledby属性の値は1つ以上のID参照のリストです。つまり、ラベルとして設定したいテキストの要素にid属性を設定し、そのid属性の名前をaria-labelledby属性の値とします。

<svg role="img" aria-labelledby="title">
    <g>
        <title id="title">メニューアイコン</title>
        <desc>3本の横線が縦に並び、メニューの折りたたみを表している</desc>
        <path ...>
    </g>
</svg>

先ほどrole="img"を付与したsvg要素に、aria-labelledby="title"を追加しました。そして、title要素にid="title"を追加しています。これにより、スクリーンリーダーの読み上げは「メニューアイコン イメージ」となり、キーボードユーザーにもsvg要素がアイコンであることを伝えることができます。
1つ以上のID参照のリストなので、もちろん複数指定することも可能です。今回はdesc要素の読み上げは冗長に感じたためtitle要素のみ設定しましたが、desc要素にid="desc"を追加し、aria-labelledby="title desc"とすると、スクリーンリーダーの読み上げは「メニューアイコン 3本の横線が縦に並び、メニューの折りたたみを表している イメージ」となります。

<nav class="global-nav">
    <button class="global-nav__button" type="button">
        <span class="global-nav__button-icon">
            <svg role="img" aria-labelledby="title">
                <g>
                    <title id="title">メニューアイコン</title>
                    <desc>3本の横線が縦に並び、メニューの折りたたみを表している</desc>
                    <path ...>
                </g>
            </svg>
        </span>
        <span class="_visually-hidden">メニュー</span>
    </button>
    <ul class="global-nav__list">
        <li class="global-nav__item"><a href="/about/">About</a></li>
        <li class="global-nav__item"><a href="/service/">Service</a></li>
        <li class="global-nav__item"><a href="/project/">Project</a></li>
        <li class="global-nav__item"><a href="/job/">Recruit</a></li>
        <li class="global-nav__item"><a href="/blog/">Blog</a></li>
        <li class="global-nav__item"><a href="/contact/">Contact</a></li>
    </ul>
</nav>

まとめ

今回は引き続きマークアップとrole属性、aria属性を実装しました。
「role属性は要素の役割を明示するもの」「aria属性は要素の状態を明示するもの」とだけ聞くととても便利なものに感じますが、どちらも使い方を間違えるとユーザーの混乱を招くものになります。
便利と不便は表裏一体です。強い薬と同様に、WAI-ARIAは副作用に十分気をつけて使用しましょう。

参考資料

Recent Entries
MD EVENT REPORT
What's Hot?