当エンジニアブログで実施したアクセシビリティ改善の方法についての解説

デザインエンジニアの安田(@_yuheiy)です。

先日、「アクセシビリティ学習の手引きとしての入門講座」という記事を公開しました。これに伴って、当エンジニアブログ自体のアクセシビリティの問題が気にかかったので、併せてその改善も行いました。

この記事では、そのなかで遭遇したよくある失敗例と具体的な対処方法についてご紹介します。アクセシビリティの問題の多くは似たようなパターンを有しているため、主要なケースについて知っていただくだけでも、多くの割合の問題を未然に防ぐことができるはずです。

文字やアイコンのコントラスト比

改善前は、文字色やアイコンの色が薄すぎて見えづらい箇所がいくつかありました。はっきりとした黒色ではなく、淡い灰色になっているような要素のことです。これらは、ロービジョンのユーザーや、ディスプレイの色が見えづらい状況にあるユーザーにとっては、その要素の内容を読み取れないという問題になります。一般的なユーザーの利用においても、要素の存在が目立たずに無視されてしまう可能性が高まります。

これらのような色は十分見えやすくなるように調整すべきですが、どの程度であれば十分と言えるかの感覚は人によって異なります。これについて、アクセシビリティのガイドラインでは機械的に判定できる基準が設けられています。

2つの色の輝度の差を算出した値をコントラスト比と呼びます。その計算式に当てはめたときに、文字と背景には「4.5:1」、アイコンと背景には「3:1」のコントラスト比が必要です(正確にはもう少し細かい基準が定められています。詳しくは「達成基準 1.4.3: コントラスト (最低限)を理解する」と「達成基準 1.4.11: 非テキストのコントラストを理解する」を参照してください)。コントラスト比は「WebAIM: Contrast Checker」などで算出できます。

この基準を満たすためには、背景色を#FFFFFFとしたときに、文字は#767676、アイコンは#949494よりも濃い色である必要があります。傾向として、感覚的に設定された灰色はこの基準を満たしていないことが多いです。

注意すべきは、マウスオーバーした際などにもこのコントラスト比の基準を満たす必要があることです。つまり、マウスオーバーしたときに色を薄くする手法はありがちですが、アンチパターンになり得ます。そもそもユーザーの操作の意図を考えれば、マウスオーバーされた要素は強調されてしかるべきであり、薄くなるのはチグハグです。したがって、マウスオーバーしたときには色が濃くなるように実装するのが得策です。

しかし、これらのように考慮すべき条件が増えてくると、色の組み合わせのパターンを把握して管理することが難しくなってきます。そこで、意図した意味に基づいて色の定義を行うことが重要になります。どの色を使うかだけではなく、どのような状況で利用される色なのかを最初から想定して設計するということです。たとえば、複数の背景色に対して複数の文字色や取り得る状態のバリエーションがあるとき、次のような種類の色を定義する例が考えられます。

  • background: デフォルトの背景色
  • on-background: backgroundと組み合わせて使用されるデフォルトの文字色
  • on-background-muted: backgroundと組み合わせて使用される目立たない文字色
  • on-background-muted-hover: on-background-mutedをマウスオーバーしたときの色
  • surface: 二次的な背景色
  • on-surface: surfaceと組み合わせて使用される文字色

このような色の定義については、「Human Interface Guidelines」「Material Design」「Spectrum」「Primer」「Bootstrap」なども参考にすることができます。

以上のような方針に基づいて、文字やアイコンの色を修正しました。

focusしたときに表示されないツールチップ

ホームにある「Writers」のセクションでは、並ぶアイコンにマウスオーバーするとその名前がツールチップが表示される仕組みになっています。

このツールチップは、フォーカスしたときには表示がされないという問題がありました。視覚障害や上肢障害などがあるユーザーはマウスの操作ができません。また、その他の何かしら制約が課されていて一時的にマウスが使えないこともあります。それらのユーザーは、おもにキーボードを中心に操作する必要があるため、キーボードの操作だけでマウスと同等の情報にアクセスできることが重要です。

キーボードの操作においては、ユーザーはフォーカス位置の変化に伴って注視点を移動させます。操作の意味としてはマウスオーバーに近いため、マウスオーバーで表示されるものはフォーカスしたときにも表示されるようにするのが定石です。

これに倣って、アイコンをフォーカスしたときにもツールチップが表示されるように修正しました。

フォーカスされたアイコンにはツールチップが表示されるようになっている

なお、このツールチップには本件以外の問題もありますが、今回は部分的な改善に留めることとしました。

重複する代替テキスト

画像の代替テキストが不適切に設定されている箇所もいくつかありました。特に目立ったのが、隣接するテキストと同等の内容が設定されている例です。

<a href="...">
  <img src="..." alt="エンジニアブログのアクセシビリティを改善しました">
  エンジニアブログのアクセシビリティを改善しました
</a>

スクリーンリーダーなどのユーザーにとって、このように重複する内容は冗長でノイズになります。この場合、正しくは次のように実装すべきです。

<a href="...">
  <img src="..." alt="">
  エンジニアブログのアクセシビリティを改善しました
</a>

画像の代替テキストには、とりあえず何かしらの値を入れてさえおけばよいと理解している制作者は少なくありません。しかし実際は、どのような値を設定すべきかは状況によって異なります。

HTMLの仕様書である「HTML Standard」では、代替テキストを記述するための一般的なガイドラインとして次のように説明されています。

代替テキストを記述する場合に考慮すべき最も一般的な規則は次のとおりである:すべての画像をその画像のalt属性のテキストと置換してもページの意味を変えないことを意図する。

よって、一般に、代替テキストは、もし画像を含めることができなかったならば、何が書かれていただろうかを考慮することで記述することができる。

この帰結は、alt属性の値が画像のキャプション、タイトル、凡例とみなすことができるテキストを含めるべきではないということである。ユーザーが画像の代わりに使用できる代用テキストを含むはずである。画像を補完することを意図しない。title属性は、補足情報のために使用することができる。

もう1つの帰結は、alt属性の値は既に画像隣接する文で提供される情報を繰り返すべきではないということである。

代替テキストを考える1つの方法は、画像の存在があることを言及することなく、電話で誰かに画像を含むページをどのように読むかを考えることである。通常、画像の代わりにいうものは何でも、代替テキストを記述するための良いスタートである。

出典: HTML Standard 日本語訳 § 4.8.4.4 画像に対して代替として動作するテキストを提供に対する要件

よくある誤解は、「代替テキストには画像の説明を書く」というものです。しかし、代替テキストは説明ではなく、代替です。説明はその対象について述べることですが、代替はそれに見合うほかのもので代えることです。代替テキストとして記述すべきは、画像の代わりに機能するテキストです。

とはいえ、これだけでは理解が難しいので、いくつかある具体的な例も参考にしてください。

HTML Standardでは、次のような項目について個別のガイドラインが提示されています。

加えて、Web Accessibility Initiativeの「Images Tutorial」では、もう少しわかりやすい例を取り上げた解説がされています。

場合によっては、画像がこれらのうちのどれに当てはまるかを判断しづらいこともあります。そうしたときに利用できる「altディシジョンツリー」という参考資料も提供されています。

altディシジョンツリーでは、画像がどのパターンに分類できるかについて、YesかNoに分岐して決定に到達できるガイドラインが示されている

出典: altディシジョンツリー | Web Accessibility Initiative (WAI) | W3C

「アクセシブルな名前」のないリンク

「重複する代替テキスト」とは対照的に、必要な代替テキストが一切設定されていない要素もありました。次のようなものです。

<a href="...">
  <svg aria-hidden="true">...</svg>
</a>

スクリーンリーダーでは、このsvg要素は、空のalt属性が設定されたimg要素と同じように解釈されます。その親のa要素については、リンクであることは伝えられるものの、内容は存在しないものとして扱われます。

これがimg要素であればalt属性を使って代替テキストを設定すべきですが、svg要素ではそれができません。そこで、WAI-ARIAの出番です。次のようにすることで、alt属性と同様に代替テキストが設定できます。

<a href="...">
  <svg role="img" aria-label="Xでシェア">...</svg>
</a>

WAI-ARIAは、role属性やaria-*属性によってアクセシビリティツリーに直接作用できる機能です。アクセシビリティツリーは、DOMツリーをもとにして形成される木構造であり、スクリーンリーダーなどの支援技術からUIの情報を取得するために利用されます。

HTMLがDOMツリーに変換され、DOMツリーは視覚的UIとアクセシビリティツリーに変換される。アクセシビリティツリーはネイティブAPIから読み取られ、支援技術を使うユーザーに伝達される

出典: アクセシビリティオブジェクトモデル | aom

アクセシビリティツリーは、アクセシブルオブジェクトというノードの集まりから成ります。このアクセシブルオブジェクトは、DOMツリーにおけるElementとほぼ一対一の関係にあります(例外はあります)。

WAI-ARIAでは、HTMLがどのようにアクセシビリティツリーにマッピングされるかの規則が定義されています。これに基づいて、次の2通りのHTMLはほぼ同等のアクセシブルオブジェクトに変換されます。

<img src="..." alt="Xでシェア">
<svg role="img" aria-label="Xでシェア">...</svg>

これらはそれぞれ画像とみなされ、「Xでシェア」というアクセシブルな名前(Accessible Name)が設定されます。ウェブページのアクセシビリティツリーはChromeのDevToolsなどで確認できます

DevToolsでsvg要素を選択するとそのアクセシビリティ情報が表示される

スクリーンリーダーでは、アクセシビリティツリーの情報に基づいて読み上げが行われます。この例の場合、「Xでシェア、イメージ」のように読み上げられます。

スクリーンリーダーで要素を選択すると、「Xでシェア、イメージ」と読み上げられる

注意すべきは、そのコンテンツだけではなく、セマンティクスも併せて読み上げられるということです。そのため、代替テキストに「Xの画像」のように指定せずとも、適切に実装されてさえいればそれが画像であることは伝わります。むしろ、セマンティクスで表現できることを自己言及的なラベルで表現することは冗長で、一般的にはバッドプラクティスです。

そして、画像の場合は代替テキストを空にするのが適切な場面もありますが、リンクには必ず何かしら読み上げ可能なコンテンツが存在する必要があります。そうでなければリンクの意味を理解できないからです。ここで言う読み上げ可能なコンテンツとは、テキストコンテンツであったり代替テキストの設定された画像などのことです。テキストコンテンツが存在しない場合は、何かしらの方法でその代わりになるものを設定する必要があります。そのための手段の一つが、今回ご紹介したWAI-ARIAであるというわけです。

WAI-ARIAの使い方については、次の記事も参考になります。

ターゲット領域を広げるための重複するリンク

リンクをクリックできる領域を広げるために、同じリンク先が設定されたa要素が重複して存在する箇所がありました。次のようなものです。

UIの左側と右上に分散する形でリンクのターゲット領域が設定されている

<div class="author">
  <a href="...">
    <img src="..." alt="">
  </a>
  <p><a href="...">Yuhei Yasuda</a></p>
  <p>Posted on <time datetime="2024-11-14">2024/11/14</time><p>
</div>

これはマウスやタッチデバイスでの操作を想定した実装ですが、キーボードやスクリーンリーダーでの操作においては、冗長であったり混乱をきたしたりしてしまう懸念があります。同じリンクが繰り返されていると、タブキーでフォーカス移動する際に余計な手間がかかってしまうほか、前述と同様にアクセシブルな名前のないリンクができてしまいます。

そのため、重複するリンクはキーボードやスクリーンリーダーでの操作では無視できるように修正します。次のようになります。

<div class="author">
  <a href="..." tabindex="-1" aria-hidden="true">
    <img src="..." alt="">
  </a>
  <p><a href="...">Yuhei Yasuda</a></p>
  <p>Posted on <time datetime="2024-11-14">2024/11/14</time><p>
</div>

これによって、重複するリンクはタブキーでフォーカス移動する際にスキップされることに加えて、アクセシビリティツリーからは除外されて存在しないことになります。

一見すると、ある要素が存在しないように扱われることは問題のようにも思えますが、同等の役割を果たせる要素がすぐ隣に存在しているので、片方がアクセシブルになっているだけで十分なのです。

これに似たような例として、モーダルダイアログのバックドロップ(背景)の実装が挙げられます。モーダルダイアログはバックドロップをクリックすると閉じるように実装されることがありますが、同様の機能を備えた閉じるボタンも併せて提供されることが多いでしょう。

<div class="dialog-backdrop" onclick="closeDialog()"></div>

<div class="dialog-content">
  <button type="button" onclick="closeDialog()">閉じる</div>
  <!-- ... -->
</div>

この場合も、アクセシブルなのは閉じるボタンだけで十分で、バックドロップをクリックしたときに閉じる機能はマウスやタッチデバイスでの操作専用のものとして扱うのがよいでしょう。

これらのような実装をするうえで意識すべきは、「そのUIは誰のためのものか?」という問いです。「マウスのためのUI」か、「キーボードのためのUI」か、それとも「両方のためのUI」なのか。もしくは、「目が見える人のためのUI」か、「目が見えない人のためのUI」か。

そして、このようなユーザー層のうちの誰かにとっての過不足がないかという観点も必要です。提供される情報が少なすぎるのは問題ですが、多すぎても冗長になります。

ユーザーとしての視点を切り替えながら、的確なUIを実現できるちょうどよい塩梅を探りましょう。

おわりに

「アクセシビリティ対応にはコストがかかる」という先入観を持つ方は少なくないように思います。しかし実際には、かかるコストに大きな違いはなく、制作者が適切な知識を持っているかどうかの問題でしかないというケースはよくあります。その意味では、知識の習得にはコストがかかるとも言えますが、個人にとって知識の価値は一度きりのものではなく、普遍的な資産になり得ます。

一方、最初から正しく作ることができればコストに大きな差は出ませんが、一度作ってしまったものを後から作り直すには明白なコストが発生します。もっとも、誰もが最初から「わかっている」状態で作ることは構造的に難しいですが、知識を持つ人の数が増えれば、事後的に発生するコストの総量は減っていくと考えられます。

最後に、当エンジニアブログに残る問題はまだいくつかありますが、これからも継続的に改善を続けていきたいと思います。