自社サービスで必ず達成しておきたいウェブアクセシビリティ対応についてまとめました

こんにちは。株式会社エニグモにてフロントエンドエンジニアを務めています新井です。

こちらは Enigmo Advent Calendar 2023 および アクセシビリティ Advent Calendar 2023 の 14 日目の記事です。

はじめに:ウェブアクセシビリティとは?

X(旧 Twitter)や各種テック記事などを見ていると、多くの人がウェブアクセシビリティを、障がいを持つ人々、特に視覚障がいのある人々への配慮として認識しています。

これは正しい認識の一部ではありますが、ウェブアクセシビリティの範囲はもっと広いです。実際には、全ての年齢層や様々な身体的、精神的条件を持つ人々が、多種多様な環境下でウェブサイトやアプリケーションを利用可能にすることを目指しています。

障がいがない人も、健康問題、マウスやトラックパッドの故障、音声再生の不可能な環境など、様々な状況に直面することがあります。ウェブアクセシビリティとは、これら多様なユーザーがウェブサイトやアプリケーションを容易に利用できるように設計することを意味します。

この記事では、ウェブアクセシビリティを向上させるための重要な10の項目を紹介します。私たちが運営するショッピングサイト「BUYMA」も今年からアクセシビリティへの取り組みを進めており、これらの項目を今後達成することを目指しています。

1. ページ内の見出しの最適化

具体的にはページ内で使用されている h1〜h6 タグを、階層構造の深さに応じて最適化します。

多くの方が見出しの最適化と聞いて SEO の改善を連想するのではないでしょうか?実際に、見出しの整理が SEO に好影響を及ぼすことはあるかもしれません。しかし、もっと注目すべきなのは、この最適化が SEO のみならずアクセシビリティにも大きく貢献するということです。

スクリーンリーダーなどの支援技術を利用するユーザーは、見出しを通してページ全体の構造を理解します。また、見出しジャンプ機能を使って必要な情報に素早くアクセスすることもあります。WebAIM のアンケートによると、約 7 割のユーザーが見出しジャンプ機能を使用しているとのことです。

webaim.org

見出しを用いてウェブサイトの情報構造を適切に整理することで、ユーザーが情報を迅速に理解できるようになります。

Google Chrome拡張機能Web Developer」を使用することでページの見出しのアウトラインを可視化することができます。見出しレベルを視覚化して検証することで、ページの構造が明確になり、改善点を見つけやすくなります。見出しの正確さや一貫性を確認するために、見出しレベルは可視化して検証することをおすすめします。

参考:達成基準 2.4.6: 見出し及びラベルを理解する

2. リンクや状態の判別を色の変化だけで行わないようにする

リンクや状態(フォーカスやホバーなど)の判別を色のみでデザインしてしまうと、色覚異常を持つユーザーにとって判別が困難になる可能性があります。 この問題に対処するためには、色だけでなく形状やその他のビジュアル変化を用いて区別ができるようにすると良いでしょう。

具体的な対策としては次のようなものがあります。

  • テキストリンクは色の変更に加えて、下線を表示する
  • 状態が変化する際に、アイコンを変更する
  • ボタンがホバーされた時は、背景色を変えるだけでなく枠線も表示する

特にホバーの状態は、透明度の変更のみでデザインされることが多いので、デザイナーと協力して状態の変化が明確に分かるデザインを作成することが望ましいです。

また、以下の CSS コードをデベロッパーツールで適用することで、ウェブサイトをグレースケールに変換し、色に頼った UI がないかどうかを検証するのに役立ちます。

body {
  filter: grayscale(1);
}

BUYMAのトップページをデベロッパーツールでグレースケールにしたサンプルです

ただし、濃い赤のような色はグレースケールへの変換では確認しにくい場合があるため、検証のコストとのバランスを考慮しながら、色覚特性のタイプごとに見え方をシミュレーションすることも有効かもしれません。

参考: 達成基準 1.4.11: 非テキストのコントラストを理解する

3. キーボード操作時のフォーカスインジケーターは非表示にしない

リンクやボタンがクリックされた際に表示されるフォーカスインジケーターは、しばしばデザイン上の理由で非表示にされがちです。しかし、フォーカスインジケーターを非表示にしてしまうと、キーボードでウェブサイトを操作するユーザーはコンテンツの利用が困難になってしまいます。

クリック時のフォーカスインジケーターを非表示にしつつ、キーボード操作時のフォーカスインジケーターを維持するには、以下のような CSS をベースで指定しておくと良いでしょう。

:focus:not(:focus-visible) {
  outline: none;
}

:focus-visible 擬似クラスはキーボード操作によりフォーカスされた場合に適用され、マウス操作によりフォーカスした場合には適用されません。

もしサイト内に複数の CSS が読み込まれ、各要素に outline:none が適用されているような場合、以下のような CSS を指定することでキーボード操作時のフォーカスインジケーターを復活させることができます。

@layer focus {
  :focus-visible {
    outline: revert;
  }
}

この方法では、Cascade Layers という機能を用いて優先度を高めています。 このスタイルは ID セレクタで定義されたスタイルよりも優先されますが、インラインスタイル( style 属性)や !important 指定されたスタイルよりは優先度が低いため、 !important を使用するよりも安全です。 ただし、 outline プロパティを上書きする場合は新たに @layer を設けて上書きする手間があるので、あくまで応急処置として扱うのが賢明です。 Cascade Layers は、現在すべてのモダンブラウザで使用可能です。

さらに、フォーカス時の状態をより分かりやすくするために、インジケーターに加えてホバー時のスタイルも適用しておくとフォーカスの判別がよりわかりやすくなります。

.button:focus-visible {
  /* ホバー時のスタイル */
}

@media (hover: hover) and (pointer: fine) {
  .button:where(:any-link, :enabled, summary):hover {
    /* ホバー時のスタイル */
  }
}

タッチデバイスでのホバーは動作に支障が出る場合があるため、hover メディアクエリを利用してタッチデバイスでのホバーは無効化しておくと良いでしょう。

参考:達成基準 2.4.7: フォーカスの可視化を理解する

4. クリッカブルな要素の実装には a タグまたは button タグを使用する

クリッカブルな要素を div タグや span タグを用いて実装すると、支援技術を使用するユーザーやキーボード操作に依存するユーザーがその要素を選択できなくなることがあり、結果としてコンテンツのアクセシビリティが損なわれる可能性があります。

リンクは a タグを、ボタンは button タグを使用して、適切にマークアップしましょう。支援技術は a 要素を「リンク」、button 要素を「ボタン」と読み上げます。

既存のコードで a タグや button タグに変更するのが難しい場合、tabindex 属性と keydown イベントを使ってクリッカブルな振る舞いを実装することができます。以下は、BUYMA で使用されている React のコードの一例です。

<tr
  key={item.id}
  className="catalogs-table__row"
  onClick={() => handleSelectItem(item)}
  onKeyDown={(e) => {
    if (e.key === "Enter" || e.key === " ") {
      // Enter or Space で実行
      handleSelectItem(item);
    }
  }}
  tabIndex={0}
>
  ...
</tr>

このコードでは、tr要素に onClick イベントと onKeyDown イベントが設定されており、tabIndex={0}によってキーボード操作で選択可能になっています。

5. 画像の代替テキストの指定と装飾的な画像の取り扱い

原則的にコンテンツ上意味のある画像には必ず代替テキストを指定し、装飾的な画像は読み上げしないようにします。

img 要素の代替テキストは alt 属性で指定します。

<img alt="BUYMA" src="logo.png" width="130" height="25" decoding="async" />

アイコンフォントや SVG 要素に代替テキストを指定する際には、role="img" と aria-label 属性を併用して指定します。span や div、svgaria-label を指定すると、支援技術によっては適切に読み上げられない場合があります。また、 span や div の暗黙のロールが WAI-ARIA 1.2 より generic となったことで aria-label を付与するのは仕様違反となっています。安定した読み上げを確保するためにも、必ず role="img" を指定しましょう。

<span role="img" class="fab-icon fab-icon-facebook" aria-label="Facebook"></span>
<svg role="img" aria-label="X" viewBox="0 0 20 20"></svg>

装飾的な画像として img 要素を使用する場合は、alt 属性を空にします。alt 属性を省略すると、支援技術は URL を読み上げる可能性があります。

<img alt="" src="crown.svg" width="20" height="20" decoding="async" loading="lazy" />

装飾的なアイコンフォントや SVG 要素は、読み上げから除外するために aria-hidden="true" を指定します。

<span class="fab-icon fab-icon-information" aria-hidden="true"></span>
<svg aria-hidden="true" viewBox="0 0 20 20"></svg>

代替テキストの作成時には、画像が伝える情報を正確かつ適切にユーザーに届けることを心掛けるようにします。また、画像に関連しない情報をSEOのために加える行為はNGです。代替テキストはコンテンツの重要部分であり、他のテキストと同様、コンテンツ設計の初期段階で検討すると良いでしょう。

さらに、意味を持つ画像は CSS の background-image を使って背景として設定しないようにします。背景画像として設定してしまうと、支援技術を使用するユーザーが画像を認識できなくなります。また、background-image は loading 属性や decoding 属性を指定できないため、ウェブサイトのパフォーマンスに影響を及ぼす可能性があります。

参考:達成基準 1.1.1: 非テキストコンテンツを理解する

6. フォームコントロールには必ずラベルを付与する

フォーム内の入力欄やテキストエリア、セレクトボックス、チェックボックスラジオボタンなどのフォームコントロールには、label 要素を使って明確にラベルを付けるようにします。

label 要素の使用にはアクセシビリティの観点から大きな利点があります。フォームコントロールにフォーカスが合わされた際に要素の名前を読み上げることが可能になり、またクリックやタップの範囲が広がり、利用しやすくなります。

フォームコントロールとラベルを関連付ける方法としては、ラベルとフォームコントロールを label 要素で包むか、フォームコントロールの id 属性を label 要素の for 属性に紐付ける方法があります。

<label>
  ニックネーム
  <input type="text" name="nickname" />
</label>
<label for="nickname">ニックネーム</label>
<input id="nickname" type="text" name="nickname" />

デザイン上、フォームコントロールのラベルが UI 上に表示されていない場合でも、支援技術利用者のために aria-labelledby 属性や aria-label 属性を使用してラベル付けを行うことが望ましいです。

aria-labelledby 属性を使用した例

<form>
  <p id="keyword_label" style="display: none">検索キーワード</p>
  <input type="search" name="keyword" aria-labelledby="keyword_label" />
  <button>検索</button>
</form>

aria-label 属性を使用した例

<form>
  <input type="search" name="keyword" aria-label="検索キーワード" />
  <button>検索</button>
</form>

また、ラジオボタンチェックボックスなど、複数のフォームコントロールが意味的に一つのグループを形成する場合、fieldset 要素を使用してこれらをグループ化するようにします。この方法を採用すると、支援技術を使用してフォームコントロールを操作する際に、そのコントロールが属するグループの名前も読み上げられるようになります。

以下は、fieldset 要素を使用してラジオボタンをグループ化した例です。性別のコードは ISO 5218 で定められたものに従っています。

<fieldset>
  <legend>性別</legend>
  <ul>
    <li><label><input type="radio" name="gender" value="1" /></label></li>
    <li><label><input type="radio" name="gender" value="2" /></label></li>
    <li><label><input type="radio" name="gender" value="9" />その他</label></li>
    <li><label><input type="radio" name="gender" value="0" />不明・回答しない</label></li>
  </ul>
</fieldset>

また、ラベルをプレースホルダーで代用するUIも時折見かけますが、以下の観点から避けたほうがいいでしょう。

  • 記入時にラベルが消えてしまうためユーザーの短期記憶に負荷をかけることとなる
  • フォームの送信前にユーザーがどの項目に何を書いたのか判別できなくなる可能性がある
  • キーボード操作でフォーカスが当たるとラベルが消えることによる利便性の低下

参考:達成基準 2.4.6: 見出し及びラベルを理解する

7. フォームの入力欄のオートコンプリートを有効にする

オートコンプリートを有効にすることにより、ブラウザに保存された補完機能を利用できるようになり、結果としてユーザーは素早くフォームの入力を完了することができます。

<label>
  電話番号
  <input type="tel" name="tel" placeholder="(例) 090-0000-0000" autocomplete="tel" pattern="\d{2,4}-?\d{2,4}-?\d{3,4}" title="電話番号は正しく記入してください" />
</label>

サービス内でよく使用する autocomplete 属性の値は以下の通りです。

補完する要素
on autocompleteを許可する
off autocompleteを許可しない
name 氏名
given-name ファーストネーム(名前)
additional-name ミドルネーム
family-name ラストネーム(名字)
tel 電話番号
email メールアドレス
username ユーザー名orアカウント名
postal-code 郵便番号
address-level1 都道府県
address-level2 市区町村
address-level3 町域
address-level4 番地など
organization 企業・団体・組織名
cc-name クレジットカード登録名
cc-number カード番号
cc-exp カードの有効期限

多くのサービスでは、電話番号やクレジットカード番号などでオートコンプリート属性を活用します。一部の UI デザインでは、電話番号やクレジット番号の各部分を別々の入力欄で要求することがありますが、これは単に使いにくいだけでなく、オートコンプリートの利用を妨げる可能性があるため、デザイナーはこれらの情報を一つの入力欄で収めるよう配慮すると良いでしょう。

8. ラジオボタンチェックボックスを装飾する際は元の input の隠し方に気をつける

display:nonevisibility:hiddenされている input はキーボード操作でのフォーカスが不可能になってしまうため、ラジオボタンチェックボックスを装飾する際は別のアプローチで隠すようにしましょう。

<label class="checkbox">
  <input class="checkbox__input" type="checkbox" />
  <span class="checkbox__icon" aria-hidden="true"></span>
  <span class="checkbox__text">規約に同意する</span>
</label>
.checkbox__input {
  height: 1px;
  opacity: 0;
  position: absolute;
  width: 1px;
  z-index: -1;
}

さらに、装飾されたチェックボックスラジオボタンがフォーカスされた際には、その状態がユーザーに明確に認識できるようにしましょう。 以下の CSS は、フォーカスされた際にチェックボックスの周りにアウトラインを表示し、関連するテキストを下線で強調する方法の例です。

.checkbox__input {
  height: 1px;
  opacity: 0;
  position: absolute;
  width: 1px;
  z-index: -1;
}

.checkbox:has(.checkbox__input:focus-visible) {
  outline: 1px solid blue;
}

.checkbox__input:focus-visible ~ .checkbox__text {
  text-decoration: underline;
}

9. アコーディオン UI は可能な限り details 要素を利用する

アコーディオン UI を details 要素で実装すると、非アクティブな要素内のテキストもページ内検索で見つけることができ、キーボード操作や支援技術によるナビゲーションも可能になります。さらに、JavaScript を使わないためパフォーマンスが向上し、JavaScript が動作しない場合でもアコーディオンが利用可能になります。

<details>
  <summary>見出し</summary>
  <p>コンテンツ</p>
</details>

例えば、BUYMA ではスマートフォン表示のフッターメニューに details 要素が使用されています。

BUYMAでdetails要素が取り入れられているサンプルです

ただし、jQuery の slideToggle のようなアニメーション効果を追加したい場合、閉じる際に CSS の transition が効かないため別途 JavaScript が必要になります。

余談ですが、jQuery の slideToggle のようなアニメーションを実装する際、現代では折りたたむ要素に display: gridtransition: grid-template-rows .3s ease-out (所要時間とイージングはお好みで調整してください) を指定し、 開閉時に grid-template-rows の値を 0fr ↔ 1fr に変更するだけで対応が可能です。

参考:detailsとsummaryタグで作るアコーディオンUI - アニメーションのより良い実装方法 - ICS MEDIA

10. モーダル表示時は背面のコンテンツを読み上げ・選択不可能にする

モーダルコンテンツ(ダイアログやドロワーメニューなど)が表示されている際には、背後にあるコンテンツがキーボードや支援技術からアクセスされないよう設定します。

背面コンテンツに inert 属性を付与することで実現できます。inert 属性があると、ブラウザはその要素に対するユーザーの入力イベント(フォーカスイベントや支援技術からのイベントを含む)を無視します。

<body>
  <div id="wrapper" inert>
    <header>...</header>
    <main>...</main>
    <footer>...</footer>
  </div>
  <div role="dialog" aria-modal="true" aria-labelledby="modal_1_title" tabindex="-1">
    <h2 id="modal_1_title">モーダルタイトル</h2>
    <p>モーダルコンテンツ</p>
  </div>
</body>

inert 属性は多くの現代のブラウザで利用可能ですが、未対応の環境での使用を検討している場合は、Polyfill が利用可能です。

www.npmjs.com

参考:HTMLElement: inert プロパティ

おわりに:BUYMA でウェブアクセシビリティを促進する理由

ウェブアクセシビリティを考慮することにより、サービスをより広範なユーザーに提供することが可能になります。これにより、アクセシビリティの問題からサービスの利用を諦めていた人々を取り戻し、機会損失を防ぐことができます。

さらに、使いやすいサービスを提供することで、全体的なユーザー体験を向上させることにもつながります。

弊社では、アクセシビリティチャンネルを Slack で設立し、アクセシビリティに関する Redmine チケットを作成するなど、取り組みを進めています。まだ完全に対応しきれていない部分は多いですが、近い将来、これらの「必ず達成しておきたいアクセシビリティ対応」を実現する目標に向けて努力を続けています。

明日の記事の担当は SELL チームの後藤さんです。お楽しみに!


株式会社エニグモ すべての求人一覧

hrmos.co