アクセシビリティチェックってどうやってるの?ということで、実際にやってみた。(その1)

ツイッターアクセシビリティ向上日誌2【目視試験編】‐Akira Tsuda Portfolio and Blogというのを見かけて、そういえばアクセシビリティチェックって何をどうしているのかという話をウェブ上でほとんど見かけない(というか自分は知らない)ので、思い切ってチェックの過程や考え方を書いてみようかなと。

チェック対象のサイトを作った@HeldaForStudy氏に尋ねたところ、題材として使ってよいという返事をいただいたので、「アトリエ金工やまぐち」のサイト1ページをチェックしてみることにします。

対象ページはBasic認証がかかっているので、アクセシビリティ向上日誌1【各種ツール評価編】からたどってください。 @HeldaForStudy氏はレベルはA*1でチェックしたとのことなので、チェック基準はWCAG 2.1レベルAでチェックすることにしましょう。 わたしは普段はCOB-CHAを使っていないので、ページの頭から問題点を挙げていくスタイルで。

なお、このページの目的はアクセシビリティチェックにどう取り組んでいるのかを説明するためであって、氏の制作したサイトやチェック結果にネガティブなことを言いたいわけではないので、そこは念のため記載しておきます。 また、チェックに抜け漏れ、誤りがある可能性があることもお断りしておきます。

機械検証(ツールによる検証)について

業務ではNu Html Checker*2axe-coreを利用した社内ツールでぱぱーっとチェックしますが*3、今回はアクセシビリティ向上日誌1【各種ツール評価編】で既にチェックされているので結果を改めてここで書く必要はないでしょう。

なお、axe DevtoolsLighthouseも中身はaxe-coreなので、どちらかお好きな方を使えばよいかと(axe-coreのバージョンが違ったりするかもなので、違う結果が返ってくるかもしれないですけど)

ところで、よくある誤解としてNu Html Checkerのエラーは全部SC 4.1.1「構文解析*4の問題とすることがあるみたいですが、それは誤りです。SC 4.1.1*5

達成基準 4.1.1 構文解析 (レベル A): マークアップ言語を用いて実装されているコンテンツにおいては、要素には完全な開始タグ及び終了タグがあり、要素は仕様に準じて入れ子になっていて、要素には重複した属性がなく、どの ID も一意的である。ただし、仕様で認められているものを除く。

とあるように、タグが壊れてない、要素が誤った入れ子になっていない、開始タグ内に同一属性が存在しない、IDが一意(重複したID値がない)の4つです。…まあ、達成基準の注記にあるとおり、

HTML 又は XML を使用するすべてのコンテンツでは、この達成基準は常に満たされているとみなすべきである。

というように(WCAG 2.2を契機に)明示されたので、SC 4.1.1は常に満たされているものとして扱います。Nu Html Checkerの報告は概ね達成基準の問題にならないことの方が多いですが*6、達成基準上の問題となるのであれば、ほとんどの場合はSC 1.3.1「情報及び関係性」かSC 4.1.2「名前 (name)・役割 (role)・値 (value)」で判断することになるので、(特にこれからアクセシビリティチェックをするという人は)忘れて大丈夫でしょう*7

HTMLのコンテンツモデルの違反は、情報構造の問題ですから、SC 1.3.1としてマークしていきます。

達成基準 1.3.1 情報及び関係性 (レベル A): 何らかの形で提示されている情報、構造、及び関係性は、プログラムによる解釈が可能である、又はテキストで提供されている。

目視検証について

本題に入る前に、COB-CHA、いいかえるなら実装チェックリストをどうして使っていないのか、という話をしておきましょう。WCAG 2.1解説書のWCAG 達成基準の達成方法を理解するでは

達成方法は、参考情報である。つまり、達成方法は必須要件ではない。WCAG 2.1 への適合を判断する根拠は、WCAG 2.1 で規定している達成基準であり、達成方法ではない。

とあります。つまりTechniques for WCAG 2(日本語訳名でいう「WCAG達成方法集」)は、参考情報であって、WCAG 2の達成基準を満たしているかどうかだけが判断基準です。また「その他の達成方法」にあるように、

W3C の WCAG 2.1 達成方法集文書にある達成方法に加えて、WCAG 達成基準を満たすその他の方法がある。W3C の達成方法は包括的なものではなく、より新しい技術や状況をカバーしていないかもしれない。

つまり、必ずしも達成方法(Techniques)を使ってWCAG 2を満たす必要はなく、Techniquesにはない別の方法で達成基準(SC)を満たしてしまっても問題ない、ということになります。実際にアクセシビリティチェックをこなしていけばわかりますが、チェックリストで判断するのはものすごく窮屈です。

前置きはさておき、中身を見ていきましょう。

ルーセルの問題

まず目に付くのは、カルーセルが止まらない問題ですね。SC 2.2.2「一時停止、停止、非表示」の問題ですが、これは@HeldaForStudy氏も問題点としてあげているとおりです。ちなみにSC 2.2.2は非干渉の達成基準ですので、WCAG2の観点では致命的…と評価されることになります。

swiperの評価をするのが面倒ですが、一応中身を見ておきましょう…

<ul class="swiper-wrapper section-top__swiper-wrapper" id="swiper-wrapper-309340565d92446a" aria-live="off" style="transition-duration: 0ms;">
<li class="swiper-slide section-top__swiper-slide swiper-slide-prev" role="group" aria-label="1 / 5" data-swiper-slide-index="0" style="width: 1648px; opacity: 0; transform: translate3d(0px, 0px, 0px); transition-duration: 0ms;">
...
</li>
<ul>

となっているわけですが、axeが指摘するとおり、

リスト要素内に許可されていない直接の子要素が存在します: [role=group]

ということですが、仕様に沿って何がどうダメなのかを確認しましょう。

HTMLのネイティブロールは、ARIA in HTML仕様で定義されています。これによると、ullistlilistitemを持ちます。ところが、ここでのコードは<li ... role="group">となっており、ロールが上書きされています。つまり親子関係としては

  • list
    • group

というようになっているわけですが、WAI-ARIA仕様 *8によればlistロールは、listitemロールしか持てません(これは、HTMLの<ol><ul><li>しか持てないのと同じ)。ですから、ARIA仕様違反、つまり情報構造としての誤りとなるため、SC 1.3.1の問題となります(axeの報告どおり)。

パッチ的な修正方法としてはrole="group"を削除すればそれでよいでしょう。根本的にswiperが悪いので、別のライブラリを使用するのを推奨します(@HeldaForStudy氏が言及しているとおり、Splideを使うのも一つの手かもしれません。ただ、実装にも依るのでアクセシビリティ上の問題が解決するのかは、チェックしてみないと何ともいえません)。個人的にはカルーセルは単なるリストの亜種でしかないと思っているので、あれそれとrole属性を付けなくといいのではと思いますけど。

ルーセルのスライドの中身としては、

<img class="section-top__swiper-img-pc" src="assets/img/michiyo/vase19-pc.jpg" alt="山口みちよ「(作品名)」">

というように提示されていますが、これは代替テキストが視覚以上の情報を含んでしまう(言いかえると、画像だけでは作品名は伝わらない)ので、作品名を提供したい(情報を持つ画像である)というのであれば、目で見えるようにテキストで提供するのが適切でしょう。SC 1.1.1「非テキストコンテンツ」の問題としてマークします。ただまあ、(後述する)「日常に火を灯す」とロゴの画像の背景となっていると捉えると、単なる装飾画像と捉えるのが適当かもしれません。

余談ですが、W3C WAIのaltディシジョンツリーの日本語訳が最近公開されたので、代替テキストに悩んだら初手としてこちらを当たるのもよいでしょう。

ルーセルのボタンのコントラスト比(SC 1.4.11)であったり、フォーカスが見えなかったり(SC 2.4.7)、リードテキスト「日常に火を灯す」のコントラスト比(SC 1.4.3)が気になるところですが、いずれもレベルAAの問題です(今はレベルAで見ているのでスルー)。ちなみに、ロゴはコントラスト比の達成基準の例外となることに注意しておきましょう。

ああでも、カルーセルのボタンとしては、

<span class="swiper-pagination-bullet" tabindex="0" role="button" aria-label="Go to slide 1"></span>

日本語のページなのにaria-label属性によるアクセシブルな名前が英語なのは微妙なので、SC 4.1.2「名前 (name)・役割 (role)・値 (value)」の問題としてマークしちゃいましょう(まあ<span>の中身が空っぽなのも気にはなりますが、目をつむっておきましょう)。

h1見出しの検討

h1見出しのテキスト(HTMLソースは改)はこういう格好ですが

<h1 class="section-top__lead">
  日常に火を灯す
  <img src="assets/img/logo1.png" alt="アトリエ金工やまぐち" loading="lazy">
</h1>

キャッチコピー「日常に火を灯す」は構造的には見出しではないのでh1は不適当

と@HeldaForStudy氏は評価しているものの、リードテキストをh1に含めること自体は不自然ではないと思います。どうしても見出し要素に入れたくないということであれば、

<header>
 <p>日常に火を灯す</p>
 <h1><img src="assets/img/logo1.png" alt="アトリエ金工やまぐち" loading="lazy"></h1>
</header>

というのもなくはないですが、スクリーンリーダーの見出しジャンプを考えると上策だとは思えず、今のテキストの入れ方のままでよいと個人的には思います。

あと、h1がある時点でSC 2.4.1ブロックスキップ」は自動的に満たされます。説明はブロックスキップを考える | Accessible & Usableあたりを読んでもらうとよいかなと。

メニュー

HTMLソース順という意味ではちょっと前後しますが、PC幅だと下スクロールしてからはじめて(画面左上の)サイドバーが出現する一方で、HTMLソースとしては<header>として<body>先頭に記述されているために、順方向のキーボードフォーカス移動でたどり着けない、という問題があります。SC 2.4.3「フォーカス順序」というよりかはむしろ、根本的にはHTMLソース上の提示順に問題があるので、SC 1.3.2「意味のあるシーケンス」の問題としてマークしておきましょう。(ワンソースで記述することを考えると、構造として破綻してしまうか…)

スクリーンショット
Firefoxの開発者ツールでタブ順序を表示させて雑に取ったスクリーンショット。フォーカス順序として難しいものが…。

SP(スマートフォン)幅ですと、ハンバーガーメニューとなりますが(下記ソース)、

<div id="open-button" class="open-button">
<span class="open-button__line1"></span>
<span class="open-button__line2"></span>
<span class="open-button__menu">menu</span>
</div>

マウスでクリックできてもキーボードで押下できない(SC 2.1.1「キーボード」の問題)、「ボタン」であることがスクリーンリーダーのような支援技術に伝わらない(SC 4.1.2「名前 (name)・役割 (role)・値 (value)」の問題)という状況です。まあネイティブの<button>なりを使ってくださいということですね。実装例は純粋なハンバーガーメニューってわけでもないのでなんともですが、Webアプリケーションアクセシビリティ──今日から始める現場からの改善 (WEB+DB PRESS plus)の「5.7 ハンバーガーメニュー」に任せます。

「閉じる」ボタンも、ボタンの役割と名前がない(SC 4.1.2)のと、表示上はメニューの末尾にある一方で、HTMLソース上はメニューよりも前にあるのでキーボードで移動できません(SC 1.3.2)。

いずれにせよ

1. メニューをサイドバーではなくヘッダとして設置

と最初の改善点として挙げられているので、改修で改善されるでしょう。。

Instagramの項目

<li class="sidebar__navigation--instagram">Instagram
  <p><a href="https://www.instagram.com/kenzo_kennkenn/" rel="noopener" target="_blank"><img src="assets/img/icon-insta.png" alt="Instagram-堅造" loading="lazy"><span class="sidebar__navigation-yugothic"></span>堅造</a></p>
  <p><a href="https://www.instagram.com/kinkou_yamaguchi/" rel="noopener" target="_blank"><img src="assets/img/icon-insta.png" alt="Instagram-みちよ" loading="lazy"><span class="sidebar__navigation-yugothic"></span>みちよ</a></p>
</li>

altは適切ではないと言えそうです(SC 1.1.1「非テキストコンテンツ」)。既に先行してInstagramというテキストがあって、人名も<img>の後で提供されているために、「大事なことなので2回言いました」になってしまうことから、画像は装飾的として代替テキストを空にする(alt="")のがよいかと。

リスト構造は趣味が入ってきますので、達成基準上(SC 1.3.1「情報及び関係性」)の問題とまではならない理解ですが、リストの入れ子にするとすっきりするとは思います。こういう感じでしょうか。(わかりやすさのため構造だけ記載)

<li class="...">Instagram
  <ul>
    <li><a href="..."><img src="..." alt=""><span aria-hidden="true" class="..."></span>堅造</a></li>
    <li><a href="..."><img src="..." alt=""><span aria-hidden="true" class="..."></span>みちよ</a></li>
  </ul>
</li>

リスト項目内で先行する画像を装飾とするならば、3点リーダーも装飾と捉えるのが自然かと思いますので、付け焼き刃的(あんまり好きじゃない)ですが、aria-hidden="true"とするのがよいでしょうか(SC 1.3.1の観点から)。いずれにせよ、(コロン「:」的な)3点リーダーの意味を伝えるのはこの文脈だとナシになるかと思いますので。

画面右上バナー

SC 2.4.4「リンクの目的 (コンテキスト内)」として挙げられていますが、特に問題ないという認識です。むしろ、キーボードのフォーカス移動のことを考えるとHTMLソース上でどこで提供すればよいのかに頭を抱える感じでしょうか…(SC 1.3.2として)。

小まとめ

まあ予想はしていましたが、ヘッダーとファーストビューで力尽きましたね(実際の業務にアクセシビリティチェックでもトップページは作業量として「重い」です)。その2に続きます

*1:WCAG 2.0なのか2.1なのかがわかりませんが、まあどっちでも大差ないといえばないので

*2:勤務先では社内にサーバーが立てられています

*3:miCheckerは使っていません

*4:Success Criteria、達成基準のこと。漢字を打つのが面倒なので、SCとします

*5:W3Cの文書は基本的に日本語訳があればそれを張っていきます

*6:HTMLの品質を測るものであって、アクセシビリティチェックのためのツールではないですので

*7:WCAG 2 FAQ How and why is success criteria 4.1.1 Parsing obsolete?がそう言っているように

*8:なんでリンクがWAI-ARIA 1.3なの?というツッコミがありそうですが、訳者の都合です