BLOG
ゼロから始めるWEBアクセシビリティ改善【Part2】
INDEX
こんにちは。アシスタントエンジニアのナガノマです!
ゼロから始めるWEBアクセシビリティ改善【Part2】の回となります。(【Part1】はこちら)
今回はいくつかデモを交えて、アンチパターンと改善例についてご紹介していきます。
また、実際にスクリーンリーダーを体験してみることで実感しやすい部分もあるため、Macユーザーの方はぜひ以下の手順を試してみてください!
手順(※今回はMac標準のVoiceOverを使用します)
・起動:
command
+F5
・読み上げの移動:
option
+control
+U
(※アクセシビリティに関する文献をもとに執筆しておりますが、私はアクセシビリティの専門家ではありませんので、あくまで参考程度にご拝読いただけますと幸いです。)
事例1. 見出し
英文+和文構成の見出しデモになります。
このデモでの課題は「同じ意味を持つテキスト情報がアクセシビリティツリー上に登録されてしまう」という点です。
<h2>
<span class="en">COMPANY INFOMATION</span>
<span class="ja">会社情報</span>
</h2>
例えば、スクリーンリーダーを使用して読み上げた際に「COMPANY INFOMATION」と「会社情報」の両方が読み上げられることになります。
同じ意味のコンテンツが重複しているため、片方のみを読み上げ対象とする対応が必要となります。
解決策としては、読ませたい部分以外にaria-hidden
属性をつけて、アクセシビリティツリー上から除外することなどが挙げられます。
<h2>
<span class="en" aria-hidden="true">COMPANY INFOMATION</span>
<span class="ja">会社情報</span>
</h2>
事例2. フォームの必須項目
汎用的なフォームUIの事例です。
このデモでの課題は「必須項目である旨のテキスト情報は渡されていない」という点です。
視覚的にコンテンツを閲覧できるユーザーであれば「※ = 必須項目」という補完が可能かもしれませんが、スクリーンリーダー上では「リファレンスマーク」などの読み上げとなるため、「※」が本来の役割を果たせていないことになります。
解決策として、aria-label
を使用してテキスト情報を上書きすることなどが挙げられます。こうすることでアクセシビリティツリー上のname
部分にテキスト情報が登録されます。
<span class="required" aria-label="必須">
<span>※</span>
</span>
また、もう一つの手法としてvisually-hidden(※)があります。これはHTMLにインラインでテキストを記述し、そのテキストをCSSで視覚的に非表示にするというものです。
※aria-hidden
と語呂が似ていますが、全くの別物で、WAI-ARIAのHTML属性と直接的な関係はありません
※TailwindCSSなどでは、sr-only
という形式で使用されます
<span class="required">
<span aria-hidden="true">※</span>
<span class="visually-hidden">必須</span>
</span>
<style>
.visually-hidden {
position: absolute;
width: 1px;
height: 1px;
clip-path: inset(100%);
}
</style>
diplay:none;
を指定すると、アクセシビリティツリー上からも除外されてしまいますが、この手法は視覚的にのみ隠すため、アクセシビリティツリーには登録させることができます。また、この例では「※」は上書きされるわけではないため、aria-hidden="true"
を付与してアクセシビリティツリーから除外しています。
結果は同じですが、aria-label
の読み上げに対応していない支援技術などにおいては、この手法が有効となります。
事例3. リンク
見出し+コンテンツ+リンクの順で構成された例です。
このデモでの課題は「リンク先に関する情報がわかりづらい」という点です。
<section class="sec">
<div class="inner">
<h2 class="hdg">私たちについて</h2>
<div class="content">
<p class="txt">このウェブサイトは、....</p>
<!-- 略 -->
<a href="/about" class="link">詳細はこちら</a>
</div>
</section>
この例では、見出しとリンクの間に長めのコンテンツが挟まっています。
視覚的にコンテンツを閲覧できるユーザーであれば、見出し部分とリンクを視覚的に紐づけることで、リンク部分が「私たちについて」の詳細ページのリンクである、という予測がある程度できるかと思います。
ただ、スクリーンリーダーで読み上げる場合、詳細ページへのリンクまでに辿り着くまでのコンテンツ情報が全て読み上げられます。よって、リンクに辿り着くまでに、このセクションが「私たちについて」を述べているものであるという認識が頭から離れていってしまう恐れがあります。
また、リンク内のテキストについても「詳細はこちら」のみで、単体では何の詳細リンクなのか伝わりづらくなっています。
解決策はリンク部分である<a>
に対し、title
属性を使用してリンク先の情報を記述するなどが挙げられます。こうすることで、アクセシビリティツリーにおけるdescription
の部分にテキスト情報を登録することができます。
<section class="sec">
<div class="inner">
<h2 class="hdg">私たちについて</h2>
<div class="content">
<p class="txt">このウェブサイトは、....</p>
<!-- 略 -->
<a href="/about" class="link" title="私たちについて">詳細はこちら</a>
</div>
</section>
また、文書の構成自体にも課題がある(リンクテキストが「詳細はこちら」のみ)ため、テキストを変更して良い場合は、テキストそのものを修正しても良いかと思います。
<section class="sec">
<div class="inner">
<h2 class="hdg">私たちについて</h2>
<div class="content">
<p class="txt">このウェブサイトは、....</p>
<!-- 略 -->
<a href="/about" class="link" >私たちについての詳細はこちら</a>
</div>
</section>
事例4. ボタン
一般的なハンバーガメニューのデモレイアウトです。
このデモでの課題は、以下が挙げられます
・キーボードやスクリーンリーダーからアクセス(クリック、展開など)ができない
・スクリーンリーダーでは、この要素がボタンであることが伝わらない
・スクリーンリーダーでは、この要素が展開可能であるかどうかの情報が伝わらない
<div class="hamburger js-hamburger">
<span></span>
</div>
<nav id="nav" class="nav js-nav">
<ul class="navList">
<li class="navItem"><a href="#">ABOUT</a></li>
<li class="navItem"><a href="#">WORKS</a></li>
<li class="navItem"><a href="#">CONTACT</a></li>
</ul>
</nav>
HTML Living Standardで定義されているインタラクティブコンテンツでマークアップを行わなかった場合、このコンテンツはフォーカス対象に含まれず、キーボードでのメニューの選択、クリック(展開)ができません。
また、スクリーンリーダーからも要素にアクセスできない(フォーカスが当たらない)ため、クリックなどの諸々の操作が実行できないことになります。
加えて、「ドロワーメニューを展開するためのボタン」という役割を持っているものの、この要素がボタンであり、展開可能で、クリックするとどこのメニューが展開されるのか、などの情報がHTML上に登録されていないため、スクリーンリーダーからでは何のUIなのか情報を得ることができません。
これらを解決するために以下のように書き換えます。
<button class="hamburger js-hamburger" aria-controls="nav" aria-expanded="false" aria-label="メニューを開く" type="button">
<span></span>
</button>
<nav id="nav" class="nav js-nav" aria-hidden="true">
<ul class="navList">
<li class="navItem"><a href="#">ABOUT</a></li>
<li class="navItem"><a href="#">WORKS</a></li>
<li class="navItem"><a href="#">CONTACT</a></li>
</ul>
</nav>
まずはキーボード関連の機能を有効化するためにHTMLタグをインタラクティブ要素に置き換えます。今回はボタンなのでbutton
タグを使用しています。
実はこれだけでキーボード、ならびにスクリーンリーダー上でフォーカス、クリックなどができるようになり、要件は概ね満たすことができますが、ハンバーガメニューであることを加味してWAI-ARIAで情報を追加しています。
・aria-label: ボタンに名前を定義
・aria-expanded: 展開可能か(折りたたまれているか)どうかを定義
・aria-controls: どの要素を展開するかを定義
ハンバーガーメニューに限らず、モーダルメニューやアコーディオンパネル等でも同様に、展開用のトリガーの部分はインタラクティブコンテンツを使用してマークアップすることが重要です。
事例5. 少し複雑なUI
以降は完成系のコードのみをご紹介します。
(コードの詳細な解説は後日LABにて掲載を予定しています。)
① アコーディオンパネル
1つ目は汎用的なHTMLタグ、2つ目はdetails
とsummary
タグで実装したものになります。
実装ポイントは以下となります。
・キーボード、スクリーンリーダーで、展開用のトリガーにアクセス(フォーカス、クリックなど)ができる
・スクリーンリーダーで、展開用のトリガーが展開可能であることが読み上げられる
・折り畳み時は、折り畳まれたコンテンツがスクリーンリーダーで読み上げられない
・折り畳み時は、折り畳まれたコンテンツにフォーカスが当たらない
1つ目のデモはこれらに則ってWAI-ARIA、並びにJavaScriptを駆使して実装しています。対して、2つ目のデモはdetails
とsummary
を使っており、WAI-ARIAを使用していません。ただし、スクリーンリーダーやキーボード操作を試してみるとわかりますが、2つ目もこれらの要件を満たしていることがわかります。
これはdetails
、summary
が、ブラウザによる補完によってデフォルトでこれらの機能を備え持っているためです。details
内の要素は、summary
要素を除き、折りたたみ時に読み上げ対象外かつフォーカスができない状態となります。また、summary
要素はインタラクティブコンテンツであり、キーボード操作でアクションが可能な上に、スクリーンリーダー上では展開可能であることを伝えてくれます。
②タブメニュー
実装ポイントは以下となります。
・タブメニューのUIであることがスクリーンリーダー上で読み上げられる
・キーボード、スクリーンリーダーで、タブ切替ボタンにアクセス(フォーカス、クリックなど)ができる
・スクリーンリーダーで、タブ切替ボタンが選択済みか否かが読み上げられる
・タブ切替ボタンは
← →
キーで、タブ切替ボタンとタブパネル間はtab
キーで移動ができる・Windowsの
Home
キー/End
キーで最初/最後のボタンにフォーカスを移動できる・アクティブなタブパネル以外にはキーボードフォーカスが当たらない
・アクティブなタブパネル以外はスクリーンリーダー上で読み上げられない
タブメニューはアコーディオンにおけるdetails
、summary
のような専用のHTMLタグが存在しないため、WAI-ARIAを駆使して実装します。
WAI-ARIAのrole
属性には、タブメニュー用の値(tablist
、tab
)が存在するため、こちらを採用します。読み上げの除外はaria-hidden
やCSSのdisplay:none
を駆使し、フォーカス移動はtabIndex
属性を使用して制御しています。矢印キーなどのキーボード操作系はJavascriptのkeyup
イベントで実装しています。
その他、具体的な構造についてはソースコードをご覧ください。
③モーダルメニュー
1つ目は汎用的なHTMLタグ、2つ目はdialog
タグを使って実装しています。
実装ポイントは以下となります。
・モーダルメニューのUIであることがスクリーンリーダー上で読み上げられる
・モーダルが閉じている時、モーダルコンテンツにフォーカスができない
・モーダルが閉じている時、モーダルコンテンツはスクリーンリーダー上で読み上げられない
・モーダルを展開した時、フォーカスをモーダルコンテンツ内に移動させる
・モーダル展開中、フォーカスはモーダルの外に出られない
・モーダル展開中、モーダルコンテンツのみをスクリーンリーダーの読み上げ対象とする
・モーダルを閉じた時、フォーカスを元の場所(開くボタン)に戻す
・モーダル展開中、
esc
キーでモーダルを閉じることができる
モーダルメニューは見た目のシンプルさ以上に求められる機能が多く、展開中はフォーカスをメニュー内に閉じ込めるフォーカストラップやキーボード操作でのメニュー制御、メインコンテンツの不活性化などの処理が必要となります。
ただし、アコーディオンにおけるdetails
、summary
と同様、dialog
タグはこれらの機能をデフォルトで備え持っているため、より短いコードで実装できます。
終わりに
汎用UIのWEBアクセシビリティ改善例についてご紹介しました。
ゼロから学んでみて一番感じたのは、WAI-ARIA以前に、ブラウザが特定のHTMLタグに付与するデフォルトの機能がめちゃくちゃすごいということです。
少しクセのあるdetails
、summary
、dialog
も、この記事を読んだ後だと裏に秘められた機能の凄さを実感できたのではないでしょうか。
アクセシブルなサイトを作る道のりはまだまだ長いですが、今後も精進していきたいと思います。
ご拝読ありがとうございました!
参考
- 伊原力也、小林大輔、桝田草一、山本伶『Webアプリケーションアクセシビリティ――今日から始める現場からの改善』(技術評論社、2023年2月27日)
- MDN「Accessibility tree (アクセシビリティツリー)」
- MDN 「WAI-ARIAの基本」
- W3C 「Accessible Rich Internet Applications (WAI-ARIA) 1.2」
- W3C 「ARIA Authoring Practices Guide (APG)」
- W3C「ARIA in HTML」
- W3C「Core Accessibility API Mappings 1.2」
- Web Content Accessibility Guidelines (WCAG) 2.2
- Web Content Accessibility Guidelines (WCAG) 2.1(日本語訳 / 解説書 / 達成方法集)
- Web Content Accessibility Guidelines (WCAG) 2.0(日本語訳 / 解説書 / 達成方法集)
- TAG index「インタラクティブ・コンテンツ」
- ICS MEDIA 「detailsとsummaryタグで作るアコーディオンUI - アニメーションのより良い実装方法」
- Katashin .info「アクションシートの実装から学ぶ <dialog> 要素を使う時の3つの落とし穴」
- YEND「アクセシブルなモーダルダイアログの実装について考える」(Zenn)