Closed32

Webアクセシビリティの門前

hajimismhajimism

アクセシビリティにまつわるよくある誤解

...つまり、ホームページ等の提供者に求められるアクセシビリティ対応とは、ホームペー ジ等においてそのような支援機能を提供することではなく、ホームページ等の個々のペ ージを JIS X 8341-3:2016 の要件に則り作成し提供することにより、利用者がそのペー ジを閲覧できるようにすることです。

hajimismhajimism

できるところから始める

現状〇〇ができないからダメ、といった減点方式で考えるのではなく、改善をするたびにできることが増えていく、という加点方式で進めていくのがいいでしょう。

hajimismhajimism

正しいHTMLを書く

...ネイティブの HTML 要素にはデフォルトでアクセシビリティの機能が備わっています。適切な HTML を適切な場所で選択して使用することが大切になります。

<button> のようなインタラクティブではない要素であっても、意味のある HTML 要素を使うことで支援技術を使用するユーザーが良い体験を得ることができます。このように HTML で正しく意味を伝えることはセマンティック HTML と呼ばれていたりします。

とはいえ、HTML は仕様に違反していてもエラーを出さないようになっていますので、普段の開発で仕様に違反しているかどうか確かめながらコードを書くのはなかなか大変です。仕様に従った HTML を書いているかどうか調べるためには、Markuplint を使って検査するのがおすすめです。

hajimismhajimism

UI コンポーネントのアクセシビリティ

ブラウザはまずマークアップを DOM ツリーに変換します。その後 DOM ツリーに基づいてアクセシビリティツリーが生成されます。アクセシビリティツリーは読み上げソフトなどの支援技術のために、アクセシビリティ API から使用されます。

アクセシビリティツリーには以下の4つの要素が含まれています。

  • role
  • name
  • description
  • state

↑inputの例がわかりやすかった

アクセシビリティツリーはブラウザのデベロッパーツールから確認できます。Element タブで要素を選択した後、Accessibility タブを選択することで確認できます。

hajimismhajimism

例えば、タブやトグルスイッチのような UI は今日の Web では広く使われていますが、対応する HTML 要素は存在しません。また、セレクトボックスのような要素は CSS でのカスタマイズが難しいので独自の実装がされることが多いかと思います。

このような複雑な UI を使用する場合には、WAI-ARIA の仕様に従って適切な設定が必要です。

WAI-ARIA はロール、プロパティ、ステートの3の機能から構成されています。

プロパティとステートの違いは、ライフサイクルを通じて変化するかどうかです。例えば要素に付けられた名前が変化することは基本的にありません。一方で、サブミットボタンが無効かどうかはフォームの入力状態によって変化するかと思います。

ステートが変更される状況では、JavaScript を用いて適切に属性を変更する必要があります。

hajimismhajimism

WAI-ARIA を使用する上で注意すべき点が存在します。WAI-ARIA は本当に必要な場合のみ使用してください。

「No ARIA is better than Bad ARIA(ARIA無しのほうが、悪いARIAよりも良い)」

hajimismhajimism

アクセシビリティに考慮した複雑なUIを実装するのはめちゃめちゃ大変
https://azukiazusa.dev/blog/react-accessible-tab/

必要な機能を十分に実装できる余裕がないのであれば、何もロールを宣言せずにタブ UI を作成するほうが好ましいです。タブの UI であることは伝わらないですが、嘘をつくよりもましと考えられるでしょう。

実装例がここに
https://www.w3.org/WAI/ARIA/apg/patterns/

Radix UIみたいにアクセシビリティ対応しているUIライブラリを使うのがベター

hajimismhajimism
hajimismhajimism

WebAIMが行ったアンケートによると、スクリーンリーダー利用者の70%近くがランドマークよりも見出しを使うという結果が出ている

見出しやラベルを置きたいけれど、デザイン上意味が自明で置くと返って邪魔になってしまうという場合は、aria-label の他にCSSで画面からは隠しつつ支援技術には伝えるテクニックがあります。

このようなテクニックは visually-hidden (見た目は隠されている=セマンティック上は存在する)と呼ばれており、次のようなCSSで実装されることが多いです。

.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border-width: 0;
}

display: none や visibility: hidden を使うと スクリーンリーダーからも読み上げられなくなる ので注意してください。

hajimismhajimism

従来のウェブサイトはブラウザの機能でページを遷移すればフォーカスやスクロールが元に戻りましたが、 SPA では実際のページ遷移ではなく History API を使って擬似的に複数ページを実現しているため、ページが遷移していてもフォーカス位置がリセットされません

先に述べた問題を解決するために、まずここにページが変更された際に子要素にフォーカスする処理を実装します。Reach Router では同様の機能がデフォルトで実装されています。

  const handleRouteChange = useCallback(() => {
    const main = document.getElementById('main');
    main.focus();
  });
  
  useEffect(() => {
    router.events.on('routeChangeComplete', handleRouteChange);
    return () => router.events.off('routeChangeComplete', handleRouteChange);
  }, []);
hajimismhajimism

tabindex 属性はちょうどCSSの z-index プロパティーのように、Tabキーを押した際にフォーカスの順序を指定できる属性です。一方で -1 を指定するとユーザーはフォーカスができなくなり、JavaScriptの Element#focus などでのみフォーカス可能になります。今日ではこの -1 とフォーカスを当てるための 0 しか使いません。

hajimismhajimism

さて、ここではモーダルの具体例について紹介しましたが aria-* や role といった見慣れない属性が出てきて当惑されている方も多いかと思います。しかし、個人的にはひとつひとつ調べるよりも W3C の WAI-ARIA Authoring Practice (日本語版)を参考に実装することをおすすめします。

実際、ARIA属性は全部で50種類、Role属性値は80種類近くあり、すべてを把握し使いこなすのは至難の業です。Authoring Practice にはチェックボックス、カルーセル、さらに読むボタン、アコーディオンなどの例が数十種類あり大抵のユースケースはカバーできるので、最初はそれを参考にしつつ、慣れたら自分で調べ始めるのが良いかもしれません。

hajimismhajimism

アクセシビリティーを改善することでマシーンリーダビリティーも向上し、結果としてテストが書きやすくなるという例を見ていただきます。

これいいよね

hajimismhajimism

さらに、React Testing Library のセレクターはテストの対象となる要素がアクセシブルな名前を持っている前提になっており、アクセシビリティーを考えた開発と非常に相性が良いです。画像やランドマークを対象にテストを行うときも getByAlt や getByLabelText で要素を探す必要があり、自ずとスクリーンリーダーなどの支援技術が要素を見つけるのと全く同じ方法でテストも記述することになります。すなわち、アクセシビリティーを良くするとテストが書きやすくなります[3]。(その逆も然り)

hajimismhajimism

ここまでのコードサンプルをご覧になった方であればお解りかと思いますが、フォーカスマネージメントを正しく行うためには ref や document.querySelector といった、 Reactが本来隠蔽している直の DOM 操作を大量に使わなければなりません。このままでは React が提唱している宣言的UIが台無しになってしまっています。

hajimismhajimism
hajimismhajimism

「ユーザビリティ」という言葉が使われる場合、しばしば「特定の状況で」「特定のユーザーにとって」という条件をつけて「使いやすい」という説明がされます。一方、「アクセシビリティ」という言葉が使われる場合は「どんな状況でも」「誰でも」と条件を付けた上で、「使える」という説明がされます。

つまり、「ユーザビリティ」は狭く深く使いやすさを追求していくのに対し、「アクセシビリティ」は浅く広く、使える人の範囲の広さを追求していく概念なのです。

hajimismhajimism

Web開発者にとってのWebアクセシビリティは、Webの品質を支える要素のひとつとして捉えるべきだと私は考えています。すなわち、バグの少ないプログラムを書いてユーザーがエラー画面を見る頻度を減らしたり、SPA化やパフォーマンスチューニングによってユーザーが待たされる時間を減らしたり、美しく機能的なユーザーインターフェース提供したりするのと同種のものなのです。

hajimismhajimism

このようにさまざまな種類のハンディキャップが意識されているわけですが、これらひとつひとつをいちいち意識してくのは大変です。人や状態によって程度の重い軽いもありますし、スクリーンリーダーや展示ディスプレイなどは軽い気持ちで体験してみるのも難しいものです。

そこで参照するべきなのが WCAG (Web Content Accessibility Guidelines) です。これはW3Cによって標準化されているガイドラインで、...

hajimismhajimism

ガイドラインではないのですが、WAI-ARIAについてもここで少し触れておきます。WAI-ARIAはW3Cによって定められているHTMLの仕様で、HTMLの要素に対してUIのセマンティクスを付与する属性を追加したものです。

hajimismhajimism

多くの組織(行政機関や一般企業など)では、「Webアクセシビリティ方針」と呼ばれる「Webアクセシビリティをウチはここまでやります」という文書が公開されています。たとえば、内閣府の場合は「JIS X 8341-3:2016 (WCAG 2.0) の適合レベルAAに準拠」を目標として、さらに一部のレベルAAAの達成基準について「可能な範囲で対応を行って参ります」とし、外部から提供されたものや古いPDFなどは例外としています。

さらに、WCAGをベースにして組織にフィットするかたちのガイドラインを定義している企業もあります。国内ではサイバーエージェントとfreeeのガイドラインが公開されています。WCAGの文章は難解で現場レベルで使用するには難しい側面があります。これらのガイドラインはWCAGから自社のWebアクセシビリティ方針にあわせて達成目標を抽出し、平易な書き方でわかりやすく目的・達成方法・チェック方法が記述されています。

hajimismhajimism

Reactであればeslint-plugin-jsx-a11y、Vueであればeslint-plugin-vue-a11yを使えば、コーディング時にWebアクセシビリティ観点のlintをかけることができます。HTMLを書く人向けにはmarkuplintというものも開発されています。

ESLintルールがあったか

hajimismhajimism

手作業でアクセシビリティチェックをやっているとわかるのですが、こうしたツールを活用していくことで、格段に問題を発見しやすくなります。Pull Requestする前に1度aXeやLighthouseを実行して修正できそうなものを修正しておく習慣をつけるだけで、致命的な問題が起きる確立をかなり減らせるはずです。特にLighthouseは他の指標(SEO, Best Practice, PWA) と同時に検証でき、点数として数値化されることによって、継続して総合的な品質を高めていくことができるはずです。

hajimismhajimism
hajimismhajimism

aria属性は「プロパティ」もしくは「ステート」と呼ばれるものを設定する属性だ。たとえばaria-labelはプロパティで、aria-selectedはステートだ。雑に分けると、静的な情報がプロパティで、動的な情報がステートになる。

そしてここからが重要で、そのプロパティとステートは、ロールに対してのプロパティとステートということだ。

hajimismhajimism

ロール?突然だな?と思うかもしれないが、ロールが超重要。なぜかというとプロパティ/ステートは、ロールに基づいて設定できるもの・できないものが決まっている。buttonというロールにはaria-labelが指定できるので、例のコードが成り立つのだ。

hajimismhajimism
  1. aria属性が使いたくなったら…
  2. その要素の暗黙のロールを調べる。
  3. ロールが適切であればそのまま。他に最適なロールがあればそれを暗黙的にもつ要素に変える。要
  4. がなければrole属性でロールを上書きする(上書きできない場合があるので注意)。
  5. ロールの必須プロパティ/ステートを設定する。
  6. 他に必要なプロパティ/ステートを設定する。
  7. 必要な構造関係があれば、暗黙ロールをもつ要素や任意にロールをもたせた要素を追加する。
  8. プロパティ/ステートの値が正しいか確認する。
このスクラップは2023/02/23にクローズされました