🕸️

【React】useIdの使い方を調べていたらアクセシビリティの世界に片足突っ込んだ話

2023/07/20に公開2

概要

普段同じようなhooksを使っていてマンネリ化してきているため、今一度、Reactにはどんなhooksがあるのか調べているとReact v18から導入されたuseId()というものを見つけた。
コンポーネントごとに一意のIdを生成できるhooksらしいが、イマイチ使い所がわからなかったので、調べていくとアクセシビリティの世界に踏み入ることになりました。

深くて軽く溺れかけたので、ざっくりメモがてらまとめます。

useIdの構文について

https://react.dev/reference/react/useId#generating-unique-ids-for-accessibility-attributes

構文

const id = useId();

引数はなく、戻り値は一意のID文字列である。

注意事項として、useId()はコンポーネントレベルで一意のIdを生成するため、mapなどのリスト表示の際のキーの生成には使えない。
公式にも、キーはデータから生成される必要があると記述されている。
https://react.dev/learn/rendering-lists#where-to-get-your-key

const Test = () => {
  const id = useId();
  return (
    ...
    ...
    ...
    {list.map((data) => {
      <div key={id}>
      ...
      ...
      </div>
    })}
  )
}

WEBアクセシビリティについて

useIdの使い所を理解するには、まずアクセシビリティについての理解しなくてはいけないと感じたため少し寄り道します。

WEBアクセシビリティとは?(簡単に)

WEBアクセシビリティとは、ウェブ上の情報がすべての人々にとって利用可能で、理解しやすく、操作しやすく設計、開発されており、視覚障害、聴覚障害、運動障害、認知障害など、様々な障害を持つ人々や、高齢者や一時的な障害を持つ人々も考慮に入れて開発されることを意味している。

また、WEB関連の標準化の開発と推進を行う団体、W3C(World Wide Web Consortium)は、WEBアクセシビリティについてこのように定義している。
https://www.w3.org/WAI/fundamentals/accessibility-intro/#what

Web アクセシビリティとは、障害を持つ人々が使用できるように Web サイト、ツール、テクノロジーが設計および開発されることを意味します。

mdn Accessibility guidesでは、障害などのハンディキャップを抱えている人々だけでなく、モバイルデバイスや、遅いネットワーク環境にいる人々のためのものでもあると定義付けている。

アクセシビリティというのはあなたのウェブサイトを可能な限り多くの人に利用してもらうようにすることです。かつては我々はアクセシビリティのことをハンディキャップを持つ人々のためのものだと考えていましたが、現在はモバイルデバイスや遅いネットワークを利用している人々のためのものでもあると考えられています。

WEBアクセシビリティがなぜ重要か?

他にもありそうだが、以下の観点でWEBアクセシビリティが重要だと考えられる。

  1. 情報の公平性
    インターネットはすべての人に公平に開かれたリソースであるべきという考え方。

  2. ユーザー基盤の拡大
    アクセシビリティを確保することで障害を持つユーザーや高齢者など、様々な状況のユーザーに対応することができるためサイトの潜在的なユーザー基盤が拡大することが望める。

  3. SEOの向上
    高品質なサイトより、有益なサイトというところから、アクセシビリティを考慮してよりマシンリーダブルなマークアップをすることによって、ユーザー基盤の拡大とSEOの向上も見込める。

  4. 社会的責任
    様々な状況の人に公平に情報を届けるという社会的責任を果たすことにより、より良い社会作りに貢献するとともに、企業、組織のブランド価値の向上も見込める。

アクセシビリティを向上させるマークアップ技術について

アクセシビリティを向上させるマークアップの方法を調べていくと、アクセシビリティ属性WAI-ARIA(Web Accessibility Initiative - Accessible Rich Internet Applications)というものがあるみたい。

・mdn
https://developer.mozilla.org/ja/docs/Learn/Accessibility/WAI-ARIA_basics

・WAI-ARIA
https://www.w3.org/TR/wai-aria-1.1/

どちらもウェブページができるだけ多くのユーザーにとって利用しやすくなるようにHTMLタグに設定する属性である。 というものであるが、WAI-ARIAは、動的なWebアプリケーションや、高度なUIなどで標準的なHTML属性(アクセシビリティ属性)では提供できないような、より高度なアクセシビリティ情報を提供するための属性を定義している。

アクセシビリティ属性について

よく使うアクセシビリティ属性には以下のようなものがある。
alt
imgタグで使用され、画像の内容をテキスト形式で説明する。視覚障害のあるユーザーがスクリーンリーダーを使用してサイトの内容を理解する際や、なんらかの理由で画像が読み込まれない場合に重要となる。
装飾などで使用されている画像などにはaltを空にすることがよくあるが、その場合、スクリーンリーダーでは無視される。

<img src="image.jpg" alt="画像の説明">

labelfor

<label for="name">お名前</label>
<input type="text" id="name">

ラベルとフォームフィールドの関連付けを行う。
これにより、ユーザーがラベルをクリックした際にフォームにフォーカスが当たり、アクティブになる。

tabindex
要素のタブ順序を制御する。ユーザーが Tab キーを使用してフォーカス可能な要素(リンク(<a>)、フォームの入力(input)、ボタン(button)など)を順番に移動する順序を決定する。
tabindex="0"
指定されたタグは、ブラウザ上でフォーカスを受けることが可能になる。
通常フォーカスが可能でない要素(<div><span>など)をフォーカス可能にしたい場合にこの値を設定する。

<div tabindex="0">フォーカス可能</div>

tabindex="-1"
負の数を指定すると、タブオーダーから除外される。
フォーカスを当てるためにはJavaScriptから制御することはできる。

<div id="myDiv" tabindex="-1">フォーカスされない</div>
<script>
document.getElementById('myDiv').focus();//jsからは可能
</script>

tabindex="1"
tabindex の値が正の数の場合、タブオーダーは、tabindex が 0、または負の数の要素よりも前となり、tabindexの値順となる

WAI-ARIAについて

WAI-ARIAには以下の3つの機能があり、これによりスクリーンリーダーや他の支援技術に要素を適切に解釈させることができる。

  1. 役割(role)
    コンピューターに要素の役割を伝えることができる。 例えば、<div>などの要素でナビゲーションを作った場合、コンピューターは意味を理解することができないため
<div role="navigation">
  <ul>
   <li><a href="#">HOME</a></li>
   <li><a href="#">ABOUT</a></li>
   <li><a href="#">CONTACT</a></li>
  </ul>
</div>

このようにrole="navigation"としてナビゲーションであることを伝えることができる。

他にもrole="banner"role="search"role="tablist"など数十個もの種類がある。

  1. プロパティ(property)
    要素の特性、性質を支援技術に伝えるためのもので、支援技術にどのように扱われるかを指定できる。
    aria-required
    aria-required="true"とすることでフォーム入力が必須であることを伝えることができる。
<input type="text" aria-required="true">

aria-labelledby
一意のidを付与して、1つまたは複数の要素が他の要素のラベルとして関連する(フォーカスする)ことを指定できる。

<span id="label">ユーザー名</span>
<input type="text" aria-labelledby="label">
<span id="label1">名前</span>
<span id="label2">苗字</span>
<input type="text" aria-labelledby="label1 label2">

aria-describedby
一意のidを付与して、要素とその要素の説明を関連づけることができる。

<button aria-describedby="deleteDescription">削除する</button>
<p id="deleteDescription">このボタンを押すとコンテンツが削除されます。</p>

これにより

<p id="deleteDescription">このボタンを押すとコンテンツが削除されます。</p>

がボタンの説明として機能する。

  1. 状態(state)
    要素の現在の状態(state) を支援技術に伝えることができる。これは通常、動的に変更されるものである。
    aria-disabled
    要素が現在非活性状態となっていることを伝える。
<button disabled aria-disabled="true">Click Me</button>

aria-disabled="true"として支援技術に現在非活性であることを伝えているが、注意したいのは、状態を支援技術に伝えているだけであって、非活性にするにはdisabledの指定が必要である。

aria-expanded
ドロップダウンメニューやアコーディオンなどが現在開いているか閉じているかなどを伝えるためのもの。

<button aria-expanded="false" onclick="toggle()">ボタン</button>
<div id="content" style="display: none;">メニュー1</div>

<script>
function toggle() {
  var content = document.getElementById('content');
  var button = document.querySelector('button');

  if (content.style.display === 'none') {
    content.style.display = 'block';
    button.setAttribute('aria-expanded', 'true');
  } else {
    content.style.display = 'none';
    button.setAttribute('aria-expanded', 'false');
  }
}
</script>

WAI-ARIA使用の注意点

mdn記載の注意点を引用させていただくと、

一点忘れてはいけないのが、 WAI-ARIA は必要な場合のみ使用するという点です。 理想的には、スクリーンリーダーのユーザーの理解に必要となる意味論の提供は、常に ネイティブの HTML 機能 を使用して行うべきです。 しかし、コードの制御が限定されていたり、 HTML 要素への実装が容易ではない複雑なものを作っているなどの理由で、これが困難となるケースがあります。 そのような場合、 WAI-ARIA はアクセシビリティを向上させる上で価値のあるツールとなります。

もう一度言いますが、必要な時だけ使ってください!

とあるように、HTML5ではデフォルトでアクセシビリティ機能を備えているため、セマンティックなHTML(<header>, <footer>, <nav>, <main>など意味論的なもの)を活用し、そちらで対応できない場合にのみ使うべきであるとしている。
動的にコンテンツを更新する場合や、高度なUI、キーボードのアクセシビリティ向上など必要な場合にのみ使用を検討するといい。

useIdの使い方

useIdの基本的な使い道は、アクセシビリティ属性で使用する一意のIDの生成である。

const PasswordField = () => {
  const passwordDescId = useId();
  return (
    <>
      <input type="password" aria-describedby={passwordDescId} />
      <p id={passwordDescId}>
      パスワードの入力フォームです。
      </p>
    </>
  )
}

passwordDescIdinputpタグに付与して関連させているが、通常のHTMLでの記述のように、IDをハードコーディングするのはReactではコンポーネントは複数回レンダリングされる可能性があり、その度に一意のIDが必要であるため、推奨されない。

<input type="password" aria-describedby="passwordDesc" />
<p id="passwordDesc">
パスワードの入力フォームです。
</p>

公式引用

このような ID のハードコーディングは React では推奨されません。コンポーネントはページ上で複数回レンダリングできますが、ID は一意である必要があります。

複数の関連要素のIDが必要な場合

複数の関連要素にIDが必要な場合は、useIdを呼び出してプレフィックスを付与することで対応できる。

const Form = () => {
  const formId = useId();
  return (
    <form>
     <label htmlFor={`${id}-firstName`}>名前</label>
     <input id={`${id}-firstName`} type="text" />
     <label htmlFor={`${id}-lastName`}>苗字</label>
     <input id={`${id}-lastName`} type="text" />
    </form>
  )
}

参考
React 公式
mdn Accessibility guides
W3C アクセスビリティ概要
WAI-ARIAのrole属性一覧
アクセシビリティとSEOについて
【アクセシビリティ】WAI-ARIAを完全に理解した。
mdn ARIA states and properties
Webアクセシビリティとは?Web制作においてWebデザイナーが意識すべきこと

Discussion