🍕

アイコンボタンのアクセシブルな名前はボタンが持つべきかアイコンが持つべきか

2023/11/20に公開
6

本記事は<svg>要素のみを持つ<button>要素(アイコンボタン)にアクセシブルな名前(accessible name)を持たせる方法について調査した結果と、WCAG 2.2のSuccess Criterion 1.1.1 Non-text Contentに関する私見をまとめたものです。

結論

  • アイコンボタンの非テキストコンテンツは装飾ではなく意味を持つ画像なので、ボタンではなくアイコン画像自体にアクセシブルな名前を持たせるべきだと考えます
  • 一方で、非テキストコンテンツの範囲をアイコンのみではなくアイコンボタン全体と捉えると、ボタンにアクセシブルな名前を持たせることも妥当に思えますが、<img>要素や<svg>要素など様々な種類のアイコン画像の実装を想定した場合、やはりボタンにアクセシブルな名前を持たせない方針に倒す方がシンプルだと思います
  • <svg>要素のみを持つ<button>要素にアクセシブルな名前を持たせる4つの方法を比較した結果、現状では<svg>要素にrole="img"aria-labelを設定する方法が妥当と考えます

もし他の解釈や観点、ご指摘等ございましたらぜひコメントをお願いします。

想定する読者

  • HTMLとWAI-ARIAの基礎知識がある方
  • アクセシビリティに興味がある方
  • WCAGについて既に固有の解釈を持つ方(ご意見いただきたいです)
  • アクセシビリティの専門家の方(ご意見いただきたいです)

きっかけ

以下の記事へのコメントについて、SVGアイコンボタンにアクセシブルな名前を付ける方法は記事中の例とコメントの例のどちらがより良いか、あるいは更に良い方法はないか、社内Slackで相談を受けて調査していました。

1つめに関しては、wozittoさんと認識はあっていると思うのですが、その上で、「SVGアイコンボタン」の例は誤りではないですが、ベストとも言えないのではないでしょうか?
SVGは画像なわけですから、以下のコードのように画像の代替テキストを提供するのがセマンティックスからは筋ではあるのかなと思うのです。

<button>
 <svg role="img" width="24" height="24" viewBox="0 0 24 24">
    <title>閉じる</title>
    <path d="M0 0h24v24H0z" fill="none"/>
    <path d="M6 6l12 12M6 18L18 6" stroke="#000" stroke-width="2"/>
  </svg>
</button>

https://zenn.dev/moneyforward/articles/b5c9b060cf9237

大前提

  • ボタンに可視テキストを持たせられるのであればそれがベスト
  • UIの名前や役割はWAI-ARIAで補完するのではなく、なるべくHTMLのセマンティクスを利用すべき(ですが、今回出すパターンはどれもWAI-ARIAによる補完を少なからず行っています)
  • アクセシブルな名前は様々なブラウザとスクリーンリーダーの組み合わせで期待通りに読み上げられるべき(ですが、今回は都合上Mac VoiceOverとGoogle Chrome, Safariのみで検証しています)

SVGアイコンボタンにアクセシブルな名前を持たせる方法

以下のような<svg>要素のみを持つ<button>要素にアクセシブルな名前(accessible name)を持たせる方法は、主に以下の4パターンあると考えます。

<button type="button">
  <svg width="24" height="24" viewBox="0 0 24 24">
    <path d="M0 0h24v24H0z" fill="none"/>
    <path d="M6 6l12 12M6 18L18 6" stroke="#000" stroke-width="2"/>
  </svg>
</button>
  1. <button>要素にaria-labelを付与し、<svg>要素にaria-hidden="true"を付与する
  2. <button>要素内にVisually Hiddenなクラスを持たせた<span>要素などでテキストを入れて、<svg>要素にaria-hidden="true"を付与する
  3. <svg>要素にrole="img"を付与し、<svg>要素内に<title>要素を入れて代替テキストを設定する
  4. <svg>要素にrole="img"aria-labelを付与する

各方法のデモは以下の通りです。

以下では、サブセクションごとに各方法の特徴や問題点、私見を記述していきます。

1. <button>要素にaria-labelを付与し、<svg>要素にaria-hidden="true"を付与する

調査のきっかけとなった記事で当初紹介されていた方法(現在は4の方法に修正済)です。

<button type="button" aria-label="閉じる">
  <svg aria-hidden="true" width="24" height="24" viewBox="0 0 24 24">
    <path d="M0 0h24v24H0z" fill="none"/>
    <path d="M6 6l12 12M6 18L18 6" stroke="#000" stroke-width="2"/>
  </svg>
</button>

ちなみに、ZennやSlackなどのSVGアイコンボタンもこの方法を採用しています。

ZennのハートマークのみのSVGアイコンボタンにフォーカスしてブラウザのDeveloper Toolsで検証した画面で、button要素にaria-label="いいね"が付与されている

2. <button>要素内にVisually Hiddenなクラスを持たせた<span>要素などでテキストを入れて、<svg>要素にaria-hidden="true"を付与する

Ameba Accessibility Guidelinesなどで推奨されている方法です。

<button type="button">
  <span class="visually-hidden">閉じる</span>
  <svg aria-hidden="true" width="24" height="24" viewBox="0 0 24 24">
    <path d="M0 0h24v24H0z" fill="none"/>
    <path d="M6 6l12 12M6 18L18 6" stroke="#000" stroke-width="2"/>
  </svg>
</button>
/* Properties are copied from https://tailwindcss.com/docs/screen-readers */
.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border-width: 0;
}

Visually Hiddenとは、display: nonevisibility: hiddenなどのAccessibility Object Model (AOM)からも無視されてしまうCSSプロパティを使わずに、要素を視覚的に隠しつつアクセシブルな名前を提供する方法の通称です。

この方法はaria-labelの使用を避ける方法ですが、<svg>要素にaria-hiddenを使用しているため、WAI-ARIAによる補完が行われています。

Visually Hiddenはブラウザによっては、widthやheightの値によっては要素を無視することがあるそうです。[1] ただ、少なくともMac VoiceOverを使用した検証においては、Google ChromeとSafariのどちらも期待通りに読み上げられました。

ちなみに、SmartHR UIでは、Iconコンポーネントにaltを設定するとVisuallyHiddenTextという<span>タグのコンポーネントとともにアイコンを描画する仕組みを採用しているようです。

3. <svg>要素にrole="img"を付与し、<svg>要素内に<title>要素を入れて代替テキストを設定する

調査のきっかけとなった記事へのコメントで提示されていた方法です。

<button type="button">
  <svg role="img" width="24" height="24" viewBox="0 0 24 24">
    <title>閉じる</title>
    <path d="M0 0h24v24H0z" fill="none"/>
    <path d="M6 6l12 12M6 18L18 6" stroke="#000" stroke-width="2"/>
  </svg>
</button>

Accessibility Object Modelにおける<svg>要素のRoleプロパティの値はブラウザによって異なるため、role="img"を付与することで、Roleプロパティをimgに固定しています。[2]

この方法は2と同様にaria-labelの使用を避けることができる方法ですが、svg要素にrole='img'を与えているためWAI-ARIAによる補完は行われています。

また、インタラクティブな要素内の<svg>要素の<title>要素は、ブラウザとスクリーンリーダーの組み合わせによっては読み上げられないことがあります。(参考:Contextually Marking up accessible images and SVGs | scottohara.me
実際にMac VoiceOverを使用した検証において、Google Chromeでは問題なく読み上げられましたが、Safariでは読み上げられませんでした。

Google Chrome Safari
Google Chromeで開いているcodepenのデモをMacのVoiceOverで読み上げて、「閉じる, button, group」と出力されている Safariで開いているcodepenのデモをMacのVoiceOverで読み上げて、「button」とだけ出力されている

4. <svg>要素にrole="img"aria-labelを付与する

調査の結果、最良だと思った方法。実際に社内での相談においてもこの方法を推奨し、きっかけとなった記事のコメントへの返信もそのように結論づけています。

<button type="button">
  <svg role="img" aria-label="閉じる" width="24" height="24" viewBox="0 0 24 24">
    <path d="M0 0h24v24H0z" fill="none"/>
    <path d="M6 6l12 12M6 18L18 6" stroke="#000" stroke-width="2"/>
  </svg>
</button>

この方法は3とは異なり、少なくともMac VoiceOverを使用した検証においては、Google ChromeとSafariのどちらも期待通りに読み上げられました。

どの方法が妥当か

  1. <button>要素にaria-labelを付与し、<svg>要素にaria-hidden="true"を付与する
  2. <button>要素内にVisually Hiddenなクラスを持たせた<span>要素などでテキストを入れて、<svg>要素にaria-hidden="true"を付与する
  3. <svg>要素にrole="img"を付与し、<svg>要素内に<title>要素を入れて代替テキストを設定する
  4. <svg>要素にrole="img"aria-labelを付与する

アイコンボタンのアイコンは単なる装飾ではなく意味を持つ画像であると解釈しているため、アイコン画像(<svg>要素)自体にアクセシブルな名前を持たせる3か4の方法が良いと考えます。その上で、3の方法では一部のブラウザとスクリーンリーダーの組み合わせによっては読み上げられないことがあるため、現状では4の方法が妥当だと考えます。

しかし、aria-label属性の値は自動翻訳が行われないなど別の観点での問題があることは留意する必要があります。

おそらく最もアクセシビリティを高く保つことができる方法は、aria-labelを付与する方法(1か4)と、aria-label以外でアクセシブルな名前を付与する方法(2か3)の両方を組み合わせることですが、1つのUIに同じ名前を2箇所持たせるのは保守性が下がるためあまり実用的ではないと考えます。


以上で、SVGアイコンボタンにアクセシブルな名前を持たせる方法についての調査と比較検討は概ね済みましたが、調査していく中でアイコンボタンというUIの解釈についていくつか疑問が出てきたので、以下に記述します。

アイコンボタンとWCAG 2.2の解釈について

アイコン画像のみを持つボタンUI単体に関連するWeb Content Accessibility Guidelines (WCAG) 2.2の達成基準はいくつか該当するかと思いますが、今回はSuccess Criterion 1.1.1 Non-text Contentとアイコンボタンにまつわる疑問点をまとめていきます。

ポイント1:アイコンボタンのアイコンは装飾なのか意味を持つ画像なのか

アイコンボタンのアイコン画像はSuccess Criterion 1.1.1 Non-text Contentの例外のうちの1つである"Decoration, Formatting, Invisible"に相当するか否かについて。

Decoration, Formatting, Invisible
If non-text content is pure decoration, is used only for visual formatting, or is not presented to users, then it is implemented in a way that it can be ignored by assistive technology.

pure decoration
serving only an aesthetic purpose, providing no information, and having no functionality

アイコンボタンが持つアイコン画像はそれ自体が情報や機能をユーザーに伝えているため、"providing no information, and having no functionality"には当てはまらないのではないかと考えます。なので、アイコン画像は代替テキストを持つべきだと考えますが、アイコンボタンとしてはもう1つの例外についても考える必要があります。

ポイント2:非テキストコンテンツの範囲はアイコン画像のみなのかアイコンボタンまで含めるのか

アイコンボタンは1.1.1 Non-text Contentの例外のうち"Controls, Input"に該当するか否かについて。

Controls, Input
If non-text content is a control or accepts user input, then it has a name that describes its purpose. (Refer to Success Criterion 4.1.2 for additional requirements for controls and content that accepts user input.)

この例外のボタンにおける具体例は、WAIの提示する達成方法の1つであるH36: Using alt attributes on images used as submit buttons[3]にあるような、<input type="image">などで画像データを直接送信ボタンなどに使用している場合が挙げられます。

アイコンボタン内のアイコン画像のみを非テキストコンテンツと捉えていましたが、アイコンボタンそのものが非テキストコンテンツと捉えることができる場合、上記の例外に当てはまるのではないかと思いました。いずれにせよ、アイコンボタンにアクセシブルな名前を持たせることに変わりはありませんが、非テキストコンテンツの粒度の解釈によっては、「SVGアイコンボタンにアクセシブルな名前を持たせる方法」のセクションで挙げた方法のうち1と2の方法も妥当になる可能性があります。

ここで、WCAGにおけるcontentnon-text content, programmatically determined の定義を見てみます。

content
information and sensory experience to be communicated to the user by means of a user agent, including code or markup that defines the content's structure, presentation, and interactions

non-text content
any content that is not a sequence of characters that can be programmatically determined or where the sequence is not expressing something in human language

programmatically determined (programmatically determinable)
determined by software from author-supplied data provided in a way that different user agents, including assistive technologies, can extract and present this information to users in different modalities

EXAMPLE 1
Determined in a markup language from elements and attributes that are accessed directly by commonly available assistive technology.

EXAMPLE 2
Determined from technology-specific data structures in a non-markup language and exposed to assistive technology via an accessibility API that is supported by commonly available assistive technology.

これらの定義から非テキストコンテンツは、知覚的に伝わる情報とも機械的に判断可能な情報とも捉えることができると思います。前者の場合、ユーザーは通常アイコンボタンをアイコンとボタンに分けて認識しないはずなので、アイコンボタン全体が非テキストコンテンツと捉えられますが、後者の場合、ボタンとアイコン画像はそれぞれコンテンツとして区別できる要素なので、アイコン画像のみが非テキストコンテンツと捉えられます。

感覚的にはアイコンボタン全体を非テキストコンテンツとして捉えて、1.1.1の例外のうち"Controls, Input"に該当してボタンにアクセシブルな名前を持たせても良さそうに思いますが、これを許容するとアイコン画像の種類によって実装にブレが生じると思います。

例えば、<button>要素の中に<img>要素がある場合は、<img>要素にalt属性を付与する方法がベストであり<button>要素自体に名前を設定することはないと思います。しかし、これが<svg>や他のアイコン画像を描画する方法の場合は<button>aria-labelを付与するとしたら、アイコン画像の種類によってアクセシブルな名前をアイコンに持たせるかボタンに持たせるか分かれてしまうので、基本的にはアイコン画像のみを非テキストコンテンツと捉えてアイコンにアクセシブルな名前を持たせる方針に倒す方がシンプルだと思います。

アイコンボタンのアクセシブルな名前はボタンが持つべきかアイコンが持つべきか

アイコンボタンのアイコン画像は装飾ではなく意味を持つ画像であるため、ボタンではなくアイコン画像にアクセシブルな名前を持たせるべきだと考えます。

また、アイコンに直接名前を設定できない場合でも Visually Hiddenなクラスを持たせた<span>要素などでテキストを入れる方法を使うことで、全てのケースにおいて<button>要素にaria-labelを持たせない実装方針に倒す方がシンプルだと思います。

  • <img>要素 → alt属性を付与する
  • <svg>要素 → role="img"aria-label属性を付与する
  • 外部のアイコンライブラリなど直接名前をつけられない場合 → Visually Hiddenなクラスを持たせた<span>要素などでテキストを入れる

まとめ

  • アイコンボタンの非テキストコンテンツは装飾ではなく意味を持つ画像なので、ボタンではなくアイコン画像自体にアクセシブルな名前を持たせるべきだと考えます
  • 一方で、非テキストコンテンツの範囲をアイコンのみではなくアイコンボタン全体と捉えると、ボタンにアクセシブルな名前を持たせることも妥当に思えますが、<img>要素や<svg>要素など様々な種類のアイコン画像の実装を想定した場合、やはりボタンにアクセシブルな名前を持たせない方針に倒す方がシンプルだと思います
  • <svg>要素のみを持つ<button>要素にアクセシブルな名前を持たせる4つの方法を比較した結果、現状では<svg>要素にrole="img"aria-labelを設定する方法が妥当と考えます

もし他の解釈や観点、ご指摘等ございましたらぜひコメントをお願いします。

関連URL

脚注
  1. 書籍『Webアプリケーションアクセシビリティ―今日から始める現場からの改善』 第2章 Webアクセシビリティの基礎 > 2.3 非テキストコンテンツのマシンリーダビリティ > よくある事例を改善する > [事例2の改善]UIにアクセシブルな名前を付与する 内の記述を参照。 ↩︎

  2. 前掲書 > 同章 > 同節 > 同項 > [事例1の改善①]画像に代替テキストを付与する 内の記述を参照。 ↩︎

  3. Understanding Success Criterion 1.1.1: Non-text Content | WAI | W3C > Techniques > Sufficient Techniques > Situation C: If non-text content is a control or accepts user input: に記載 ↩︎

GitHubで編集を提案

Discussion

_H_R_⓿ᴥ⓿_H_R_⓿ᴥ⓿

おそらく最もアクセシビリティを高く保つことができる方法は、aria-labelを付与する方法(1か4)と、aria-label以外でアクセシブルな名前を付与する方法(2か3)の両方を組み合わせることですが、1つのUIに同じ名前を2箇所持たせるのは保守性が下がるためあまり実用的ではないと考えます。

こちらの部分、1,4と2,3をそれぞれ組み合わせて利用してもアクセシビリティを高く保つことは難しいのではないでしょうか。
1の場合、aria-labelを使用した要素の中のテキストは通常読み上げが行われない認識です。
4で例えばsvgの上にVisually Hiddenを置く場合、button内ラベルが重複して読み上げられます。

以下、確認に利用したcodepenです。
https://codepen.io/_H_R_/full/eYxVMWj

Taiga KIYOKAWATaiga KIYOKAWA

コメントありがとうございます!仰る通りですね...確認不足でした。該当箇所に追記して訂正しておきます。

kajirikajirikajirikajiri

関係性が少ないかもしれませんが、muiではsvgにdata-testidが振られています。
私は<svg>要素のみを持つ<button>要素(アイコンボタン)をtestする場合は、data-testidで要素を特定しています。
また、文字を含むボタンの場合はその文字で要素を特定しています。
https://mui.com/material-ui/icons/#testing

Taiga KIYOKAWATaiga KIYOKAWA

コメントありがとうございます!muiのIconコンポーネントにdata-testidが振られているのは知らなかったので勉強になります。たしかにテキストを含まないアイコンボタンなどにはdata-testidを割り振って、テスト時に特定しやすくするのも良さそうですね。今回テスタビリティの観点での比較はできていなかったので今後考慮していきたいです。

misukenmisuken

記事を興味深く拝見いたしました。
そこまで詳しくないものの、アクセシビリティは意識しつつ、ボタンのテキストの扱いに関しても色々と考えたことがあったので、もう一つ視点を加えさせてください。

CSSとの絡みに関してです。
例えばメディアクエリを使い、画面幅が広いときは [🎨 パレットを開く] のように表示され、画面幅に狭いときは [🎨] のように表示される且つラベルはホバー時にツールチップで現れるボタンがあるとします。
この場合、1はCSSの疑似要素の content: attr(aria-label) 、2はVisually Hiddenを外すで対応できますが、3と4の場合はCSSで <svg> 内のテキストを視覚化することができるのでしょうか?

たしか、以前 <svg>aria-labelcontent: attr(aria-label) で表示しようとしてできなかった記憶があったので気になりました。

この例のように、HTMLの情報としては変わらないものの、状況に応じてボタンの見え方を柔軟に変化させたい場合を考慮すると、1や2に優位性が出てくる可能性はないでしょうか?

ボタンのアイコンはシンボルとして意味を持つものではあるので、単体で見る際は装飾という扱いにするべきではないという点は仰る通りです。
しかし、上述のボタンの場合はまず "パレットを開く" ボタンが存在し、それをどう視覚表現するかという段階でアイコンが付与されていると捉えることもできます。

パレットを開くボタンのドキュメント的な情報はアイコンの有無に左右されず不変でよく、目印になったり画面幅が狭い等の視覚的な都合が無ければアイコンを表示する意味はありません。
つまりこのように考えると "アイコンはボタンのテキストの代わりの役割を果たしている" のではなく、視覚的な補助という位置付けに存在しそうな気もします。
もしも装飾が単なる飾りではなく、情報を見やすくするための装飾という概念が有効なのであれば、その方向性になります。

また、最近は mask-image を使用したアイコン表現 を行う環境が整ってきており、自身も smart-svg というライブラリを公開しております。
この場合 <svg><img> の使い分けは必要なく、それでいてCSSでアイコンテキストボタンとアイコンボタンを柔軟に制御できます。(ボタン内のアイコン役の <span> に描画するか、ボタンの疑似要素に描画するかの焦点も出てきますが)

背景画像をマスクして表示する方法は若干ハック気味ではありますが、情報を見やすくするための装飾という位置付けであればアリかな?と思うと同時に、実装やパフォーマンス面でメリットがあるので、安定したアクセシビリティとの両立が確立されるとありがたいなと思っております。

Taiga KIYOKAWATaiga KIYOKAWA

コメントありがとうございます!仰る通り、ボタンにテキストを持っていて画面幅などの変化によってそのテキストの表示方法がツールチップなどに変化するようなケースの場合は、可視テキストが存在しているのでそのボタンのアイコンは装飾として捉えても良さそうです。

そして、実装としてはたしかに 3や4のように<svg>要素にアクセシブルな名前を持たせてしまうとツールチップとしてテキストを視覚化することは難しそうです。なので、1や2、特に2のVisually Hiddenのような方法の方が、CSSでレスポンシブにテキストの表示を切り替える場合には適していそうですね。

mask-image は初めて知りました。たしかにこれを使うとCSSでかなり柔軟にアイコンボタンの表現ができて便利そうですね。教えていただきありがとうございます。