こちらの記事は アソビュー! Advent Calendar 2024 の23日目(裏面) です。
みなさんこんにちは。
アソビュー株式会社の櫻井と申します。社内では主にフロントエンドの開発に携わっています。
今回は新規プロダクトでアクセシビリティを意識して開発したことについて書きたいと思います。
はじめに
アクセシビリティ改善活動の取り組み
弊社のフロントエンドエンジニアチームでは、昨年のアドベントカレンダーで紹介させていただいたように、アクセシビリティに関する輪読会を開催し、継続的な改善活動を行ってきました。
過去のブログでもチームの改善活動についてもブログで紹介していますのでご興味がありましたらぜひご覧ください。
tech.asoview.co.jp tech.asoview.co.jp
背景
先日、弊社では海外のレジャーやアクティビティのチケット予約サービス「アソビュー!海外」をリリースしました。このサービスは、海外旅行に出かける方々が気軽に現地のアクティビティを予約できることを目指しています。 新プロダクトのフロントエンド開発にあたり、年齢や言語、文化的背景が異なる様々なユーザーにとって使いやすいサービスとなるよう、アクセシビリティを意識した実装・開発に取り組みました。 本記事ではその中からいくつか実装の詳細をご紹介させていただきます。
アクセシビリティ対応の実装詳細
1. ページ構造とナビゲーション
アクティビティ紹介画面や購入画面では、スクリーンリーダーユーザーが効率的にコンテンツを把握できるよう、適切なランドマーク要素であるheader
、main
、section
、footer
と ARIA
属性を使用しました。ランドマークを明確に区分することによってページの領域に到達しやすくなります。
また、各セクションには aria-labelledby
を用いて見出しと内容を関連付け、スクリーンリーダーがコンテンツを把握しやすいようにしています。
<!-- ヘッダー領域 --> <header> ・・・ </header> <!-- メインコンテンツ --> <main> <section aria-labelledby="title"> <h1 id="title"> オーストラリア </h1> <!-- セクションコンテンツ --> </section> <section aria-labelledby="popular-city-list"> <h2 id="popular-city-list" >オーストラリアで人気の都市</h2> <!-- セクションコンテンツ --> </section> </main> <!-- フッター領域 --> <footer> ・・・ </footer>
2. フォーム要素とインタラクション
プラン選択のUIでは、details/summary
要素を活用し、キーボード操作への対応と排他制御を実現しています。標準の HTML 要素を使用することで、キーボード操作のサポートが組み込まれており、スペースキーやエンターキーでの開閉操作が行えます。
また、同一の name
属性を持つ details 要素は排他制御が可能で、一度に1つのプランのみが開かれる状態を維持できます。このように、HTML の標準要素を活用することで、アクセシブルな UI を比較的シンプルに実現することができます。
<details>: The Details disclosure element - HTML: HyperText Markup Language | MDN
<details name="package"> <summary> <span>平日</span> </summary> <div> <!-- コンテンツ --> </div> </details> <details name="package"> <summary> <span>週末</span> </summary> <div> <!-- コンテンツ --> </div> </details>
3. 画像とメディア
img 要素
画像には目的に応じて適切な代替テキストを設定しています。
下記の例では、画像の alt 属性に具体的な商品名を設定しています。そのようにすることで、画像が表示されない場合や画像が見えない場合にどのような画像が表示されているのかテキストで分かるようにします。
<img src="/image/package.png" alt="<ブルーマウンテンズ・世界遺産ウォークツアー・ロープウェイ>シーニックワールドパスの写真が表示されています" loading="lazy" />
一方、下の画面の飛行機アイコンのような装飾的な画像には alt=""
を設定します。
装飾的な画像の alt に空を設定することで、スクリーンリーダーの不要な読み上げを防ぐことができます。
<!-- 装飾的な画像 --> <img src="/icons/airplane.svg" alt="" />
button + svg 要素
下記のような購入数選択などのインタラクティブな要素にもアクセシビリティ対応を行っています。特に重要なのが、テキストを持たないアイコンのみのボタンへの対応です。
アイコンのみのボタンは視覚的に理解はできても、スクリーンリーダーのユーザーは情報が不足するため、 aria-label
属性を使用してボタンの目的を明確に伝える説明を追加しています。
アイコンの svg 画像には aria-hidden="true"
属性を追加し、装飾的な要素がスクリーンリーダーに読み上げられることを防いでいます。
Voice Over でそれぞれのボタンにフォーカスが当たると、「大人の人数を減らします・ボタン」「大人の人数を増やします・ボタン」と発話されます。また、ボタン要素を囲む div の role="group"
と人数の span
要素に aria-live="polite"
と aria-atomic="true"
の属性を付与していることで2回タップして人数を2に増やした場合、値の変更が通知と文脈も含めて読まれ「大人の人数を増やします・2」と発話されます。
<div> <div> <p id="adult-label">大人</p> <div> <span>40円</span> <span>/ 枚</span> </div> </div> <div role="group" aria-labelledby="adult-label"> <button type="button" aria-label="大人の人数を減らします"> <svg aria-hidden="true"> <path>…</path> </svg> </button> <span aria-live="polite" aria-atomic="true">2</span> <button type="button" aria-label="大人の人数を増やします"> <svg aria-hidden="true"> <path>…</path> </svg> </button> </div> </div>
4. フォーム入力
フォーム部分では、以下のような実装をしています。
label と input 要素の紐づけ
label 要素の for 属性と input 要素の id 属性を関連付けることで、スクリーンリーダーがラベルとフォームの関係を正しく認識可能です。また、クリックできる領域が拡大するため、運動機能に制限のあるユーザーにも役立ちます。
必須項目の表示
必須事項の視覚的な赤文字表示と aria-required
属性を併用することで、視覚・非視覚の両方のユーザーに必須項目であるということが伝えられます。
<label for="surname"> <span> 姓 / Surname <span>必須</span> </span> </label> <input type="text" id="surname" required pattern="[A-Z]+" name="lastName" aria-required="true" /> <label for="givenname"> <span> 名 / Given Name <span>必須</span> </span> </label> <input type="text" id="givenname" required pattern="[A-Z]+" name="firstName" aria-required="true" />
5. 決済選択フォームのグループ化
クレジットカードの決済フォームは fieldset
と legend
を使用して関連する入力フィールドをグループ化しています。
fieldset
と legend
を使用することで、スクリーンリーダーのユーザーにとって legend 要素に到達すると、「支払い方法の選択・フォームの開始位置です」と発話されるのでフォームの開始位置などフォームの全体像を把握しやすくなります。
またキーボード操作によるフォーカスの移動も可能です。ただし、 legend 要素は fieldset 要素の最初の子の要素でなければならないので注意が必要です。
<section aria-labelledby="payment"> <h2 id="payment">お支払い方法</h2> <p>このプランはクレジットカードでお支払いいただけます。</p> <fieldset> <legend>支払い方法の選択</legend> <!-- 登録済みカード選択 --> <div> <label> <input name="cardName" type="radio"/> <div> <p>visa ****4242</p> <p>有効期限 1 / 2026</p> </div> </label> </div> <!-- 新規カード入力 --> <div> <label> <input name="cardName" type="radio"/> 新しいクレジットカードで払う </label> </div> </fieldset> </section>
実装の成果
今回ご紹介した実装は一部に過ぎませんが、WAI-ARIA の使用、適切な HTML 構造、フォーム要素の最適化など、様々な工夫をしたことで、下記画像のように Lighthouse のアクセシビリティスコアで90点前後を達成することができています。(1つ目の画面は alt が設定されていない箇所があるため若干スコアが下がっています。まだまだ改善の余地はありそうです。)
前述の通り、現状の実装にもまだ改善の余地があります。Lighthouse のスコアという定量的な指標は一つの目安に過ぎず、本質的には様々なバックグラウンドを持つユーザーが快適にサービスを使用できることが私たちの目指すべきゴールです。 そのために、アクセシビリティ観点でのレビュー、開発初期段階からのアクセシビリティ考慮、実装ガイドラインの継続的な改善と共有などの取り組みを進めていきたいと考えています。
おわりに
アクセシビリティ対応は、特別な対応ではなく、Web サイト開発における基本的な品質要件の一つです。今回の実装事例が、みなさんのアクセシビリティ対応の参考になれば幸いです。
引き続き、より良いユーザー体験の提供を目指して改善を続けていきます。
We're hiring!
アソビューでは、より良いプロダクトを世の中に届けるため、共に挑戦していただけるエンジニアを募集しています。カジュアル面談も実施していますので、興味を持たれた方はぜひエントリーをお願いいたします!
技術情報を発信する公式アカウントもございます。ぜひフォローをお願いします! https://twitter.com/Asoview_dev