【個人用】Accessible Vue 読書メモ
Marcus Herrmann 氏による Web アプリにおけるアクセシビリティについて、ヒントやコツ、ベストプラクティスな手法、Vue2, 3 でのインクルーシプなコンポーネントを作成する方法について書かれた本。
About this book
読了
- 開口第一でVueはとってもアクセシブルとのご意見
- ReactやAngularよりもカンタンに書ける(筆者談)
- 別の点ではVueはアクセシブルではないとのご意見(どっちやねん)
- ただこれはVueに限らず他のフレームワークでも同様の問題を抱えている
- もともと各種フレームワークはユーザがアクセシビリティを意識したコードを書けるはず
- 問題なのはアクセシビリティの認識や教育ができていないことである
- Webサイトやアプリは開発者が想定した環境で皆が使えるわけではない
- 多様なユーザがWebを使っている現実があるが、そこへの配慮が忘れ去られている
- これは「多様性への非認識」であるとも言える
- JavaScriptでつくられたWebアプリ以外にも昔ながらのWebサイト(アクセシビリティ配慮がされている)もインターネット上にはいくつもあるが、その事自体に注目がいっていない
- Web開発がプロ化してきたことで様々な職務の人が参加できるようになった
- デザイン性のあるものやDRYなバックエンドへの興味は高くなる一方でアウトプットされるもの(HTML、CSS)への敬意はうすれていっている
- Eric氏の言うように「フレームワークファースト」から「ユーザーファースト」への考えを移行する必要がある
What are the contents of this book?
- 各章についての説明
- チャプター1:Webアクセシビリティの基本的な理解ができるような説明
- チャプター2:アクセシブルなWebアプリをつくるためのbuilding blocksについての説明
- チャプター3:アクセシブルなWebアプリにするためにVueのコアコンセプトとアーキテクチャの強みをどう利用するか
- チャプター4:典型的なコンポーネント群をアクセシブルにする方法について
- チャプター5:状態や非同期的なものを支援技術で理解できるようにする方法について
- チャプター6:テストと自動化チェックによりアクセシビリティ改善をする方法について
- チャプター7:よりアクセシビリティについてを学んでいくための教材の紹介
What are the limits of this book?
- この本ではあくまでもVueにおけるアクセシビリティについてを取り扱う
- アクセシビリティも多岐にわたる内容があるため表層的にしか取り扱えない
- eslint や jest や Cypress といったものはそれ自体で1冊の本を書けてしまうくらい膨大
- あくまでもこの本はそれらの有用なツールを使ってアクセシビリティを実践する最初の一歩を教えるだけにとどめている
Who is this book for?
- 対象者について
- Vueでアプリケーションを作って、Vueを使って作ったページやアプリからユーザーを排除したくない人向け
- 本書の読者はWebアクセシビリティについて基本的な知識があると理想的である
- そうでなければチャプター1をしっかり読んだほうがいい
- アクセシビリティについて詳しく知ることは、それだけで価値のあるスキルだから
- WEBアプリに関するアクセシビリティ、特にVueのアクセシビリティはドキュメントが不足している
- ただそれ自体とは関係なくアクセシビリティは重要である
How do code examples in this book work?
- Vueアプリを作成する方法はいくつもあるので古典的な方法で紹介する
- シングル・ファイル・コンポーネント(SFC)
- プロジェクトで扱うものはVue CLIで作成されたものと仮定
- その結果、オプションAPIを使用している
- コードサンプルは、Vue 3とVue 2の両方の構文で存在して、コードサンプルごとにCodeSandboxで提供
Who wrote this book?
- 筆者についての紹介項目
- Marcus Herrmann
- Webアクセシビリティのスペシャリスト(IAAP)の資格がある
- ドイツ・ベルリン出身のフリーランスのWeb開発者
- Vue.jsを好むがすべての案件でVue.jsを使用するわけではない
- しかしクライアントサイド駆動型のフレームワークを用いたほうがいい
- WEBアクセシビリティとVueが両方交わる領域に魅力を感じているので本書をつくった
チャプター3:Using Vue’s strengths
読了
Conveying context with props
- heading を例にした props に関する説明
context is king, and useful heading levels their crown.
- そうなんだ…(感想)
- HeadingComponent.vue は以下のように実装すると良い
<component :is="headline">見出し</component>
computed: {
headline() {
return "h" + this.headlineLevel;
},
},
-
<h1>
みたいな形でハードコーディングしてしまうと特定の位置でのコンポーネントとしてでしか活用できない - 妥当な位置に妥当な見出しレベルでおけるようにすることが肝要
Requiring props
- 必須propsを使うことでアクセシビリティを向上させる方法
- 下記のような形で実装するのではなく(labelがないのでそもそもNG)
<template>
<input type="text">
</template>
- 下記のような形でマークアップしてpropsを必須にすることでアクセシビリティを維持しつつ、labelが入っていない場合はブラウザコンソールで警告に気づける
<label for="name"> {{ label }} </label>
<input id="name" name="name" type="text" >
props: {
label: {
type: String,
required: true,
}
}
- その他の活用事例
- 画像コンポーネントへのalt属性
- インラインSVGへのaria-label、title
- アイコンボタンへのaria-label
- fieldsetでのlegendタグ
- 多言語対応のときのlang属性
- tableでのsummaryタグ
Circumventing Vue 2’s one-root-element rule
- Vue2でのrootエレメントは1つでないといけない問題への対処
- Vue3では複数エレメントがおけるようになった
- table の実装では以下のような実装ではエラーになる問題点がある
<template>
<tr><th>行1</th><td>内容1</td></tr>
<tr><th>行2</th><td>内容2</td></tr>
<tr><th>行3</th><td>内容3</td></tr>
</template>
- そのためdivで挟みがちになりそうだが、以下はVueではコンパイル成功できるがHTML構文上はエラーになる
<template>
<div>
<tr><th>行1</th><td>内容1</td></tr>
<tr><th>行2</th><td>内容2</td></tr>
<tr><th>行3</th><td>内容3</td></tr>
</div>
</template>
- vue-fragment を使用して回避する
<template>
<fragment>
<tr><th>行1</th><td>内容1</td></tr>
<tr><th>行2</th><td>内容2</td></tr>
<tr><th>行3</th><td>内容3</td></tr>
</fragment>
</template>
import { Fragment } from 'vue-fragment'
Facilitate focus management with $refs
- $refs を使うと
.querySelector
などのDOMクエリを使用せずともDOMノードへの参照ができるようになる - アプリのフォーカス制御とかで便利になる
- refsで要素を指定して
.focus()
で要素にフォーカスできる
- refsで要素を指定して
Controlling where attributes get applied to
- コンポーネントに属性を付与するとコンテナにそのまま追加される
- たとえば以下のコンポーネントに指定すると意図せぬ属性が付与されたりする
- 本来であればbutton に
disabled
が付与されてほしい
- 本来であればbutton に
<HogeButton label="name" disabled />
<template>
<div disabled>
<button>{{name}}</button>
</div>
</template>
- これを防ぐために
inheritAttrs
をfalseにして付与したい対象にv-bind="$attrs"
を書くとよい- Vue3では
inheritAttrs
を明示的に書かなくても省略して指定できる
- Vue3では
Visibility helper components
- 視覚的に隠すテクニックとしてvisually-hiddenという選択肢がある(前章参考)
- <VisuallyHidden />というヘルパーコンポーネントを作ってみる
<VisuallyHidden tag="h2">私はここにいるよ</VisuallyHidden>
- focusableというboolean propを渡すことでフォーカス可能なものにできる
- スキップリンクという手法で使われている
Accessible Base Components
- コンポーネントをアクセシブルにすることも大事だけどそれを使いやすくすることも大事
- グローバルなコンポーネントとして使えるようにしておくと良い
- 何度も使うものや、アクセシブルな方法で提供したいものが適している
- テキスト入力やボタン、SVGアイコン、テーブルなど
- 1つめの手段はベースとなる
App.vue
にコンポーネントを登録しておくこと - 2つめの手段はエントリーファイルに
Vue.component
でコンポーネントを登録しておくこと - チーム内で使うように促進しておくことが大事
- アクセシブルなコンポーネントが曖昧なフォルダにあってインポートもされない場合、ユーザにとっては何の意味もないものになる
Action Steps for this chapter
- 作成したVueアプリケーションにおける見出しをチェックしてみる
- 作ったコンポーネントがアクセシブルなものかをチェックしてみる
- W3Cの公式バリデータを使用してみてチェックしてみる
チャプター5:Convey changes of state to screen-readers
読了
Live regions
- Webアプリケーション上の変化をユーザーに通知するのは自然なこと
- ただしそれは視覚的にはすぐわかることでスクリーンリーダーユーザーからすると気づけないことがある
- スクリーンリーダー通常のモードでは基本的にはドキュメントを上から下に読んでいく
- アクセシブルな通知ができていないと何が起きているかを探す羽目になる
Best Practice
- そうした自体を防ぐために ARIA live regions というものがある
- 簡単に言うと
aria-live
を付与することで要素が通知コンテナとなる。 - スクリーンリーダーユーザーだけに通知することができる
Accessible routing
- SPA にとってはルーティングという技術は必要
- ウェブアプリケーションをよりデスクトップアプリケーションのように動作させることができる
- location hash や HTML5 History API を扱って非同期的にページ内を変更している
- しかしスクリーンリーダーユーザーにとっては何が変更されたかが気づかないことがある
- 画面を拡大表示して使っている人たちにも状況に気づかないこともある
- 参考:What we learned from user testing of accessible client-side routing techniques with Fable Tech Labs | Gatsby
- SPAのルーティング概念はまだ新しいものでアクセシビリティとしての確実な解決策はまだ出てきていないが、いくつかの対処方法はある
Announcement / live regions
前述したようにスクリーンリーダーユーザーのための手法。「About us」に遷移したときに「現在は About us」のように読み上げさせることができる。しかしこれはスクリーンリーダーユーザーのためだけの体験向上でしかない。
Skip links
チャプター4でもあったように内部リンクを使うことでコンテンツにすぐ移動できる手段。これ単体ではルーティングのアクセシビリティ向上には役に立たないが、次の手段と掛け合わせることで有用性が増す。
Focus management
今のところ、フォーカス管理するのはアクセシブルなルーティングをつくる上でのもっとも一般的なアドバイス。
ルーティングの変更が成功した瞬間をフックして、新しく読み込まれたコンテンツの一部、コンテナ、見出しにフォーカスさせる手法。
掛け合わせる
以上の手段を掛け合わせて、
- メインコンテンツにスキップリンクを設定する(クライアントサイドのルーティングを扱わない場合でも行うべきこと)。可能であれば、そのスキップリンクをメインコンテンツに視覚的に近い場所に設置する。
- ルーティング変更したら、スキップリンクにフォーカスを当てる
- スクリーンリーダーにルート変更を知らせるために、ライブリージョンを使用する
スクリーンリーダーのユーザー、キーボードを入力デバイスとしている人、および画面拡大を使っている人々に、よりよい体験を提供することが出来る。
まとめ
GatsbyJS の研究結果としては多くのユーザーをサポートする手法として確立されていると思う。しかし常にスキップリンクを表示するように提案するのは苦労するのではないだろうか。
現状アクセシビリティなルーティングのベストプラクティスはない。よりよいものに近づくために特定のユーザーを対象としたリサーチを行う徹底的に行う必要があると思う。もしこの領域で何かしら手法を生み出せたなら、ぜひ共有してもらいたい。
Action Steps
- ルート変更でドキュメントのタイトルも変更されているか
- ルート変更後にフォーカスが管理されるか、またはアクティブなリンクのままか
- アプリや Vue.js を使用したサイトで状態が変化すると、何が起こったかについてアクセス可能な通知があるか。DOM内のトリガーの位置の上で更新が行われているか
- もしそうなら、ライブリージョンを使用することを検討する
チャプター6:Testing for accessibility
読了
- アクセシビリティのテスト手法
- シナリオAはアクセシブルなテストスイートを書く
- シナリオBはビジネスロジックを書く前にアクセシビリティテストを書く
- 自動テストについてはすべてグリーンになったとしてもアクセシブルなものを保証できるとは限らない
- 自動検出されるものは限られている
- アクセシビリティとは多様性とこれまでの経験に関わる部分である
Automated tests are no miracle workers
- 自動テストは開発の助けにはなりますが、確実にアクセシブルになるものかというと違う
- 例えば alt について
- alt属性が付与されているかどうかというのは機械的に自動テストをかけることができます
- 挿入されたaltテキストが適しているかどうかの判定まではテストできない
- アクセシブルデザインについてのチェックは機械ではなく人間がするものである
- 自動的にアクセシビリティの問題点を抽出するのは全体で見て30%しかできない
- その結果を知ってがっかりするかもしれないけど、それでもテストをしないよりはマシ
Accessibility Testing as education
- アクセシビリティテストは2つの点において通常のテストと同様貢献するものがある
- 重要なコンテンツをカバーできていると個人やチームが拡張したりリファクタリングするときにも壊すことがない
- ドキュメントとしての機能
- axe-core テストランナーはWCAGのリファレンスにもつながっている
- アクセシビリティテストに失敗した面々はそこから学ぶきっかけを得られる
- とはいっても自動テストが完璧ではないことは否定できないし、したくない
- 偽陽性や偽陰性が発生する可能性がある
- しかしそれを上回るメリットがあると信じてる
- アクセシビリティがチームにとって重要であることを伝えることができる
- プロジェクトのアクセシビリティレベルが維持される価値がある証拠になる
- さらに良い方向へ拡大することもできる?
- 初めて触れるチームメンバーに出発点を提示することができる
Linting
- アクセシビリティリンターの紹介
- 一般的なものは ESLint
- 有用なものとして eslint-plugin-vuejs-accessibility があげられる
-
.vue
ファイルのアクセシビリティチェックをしてくれる
-
npm install eslint-plugin-vuejs-accessibility --save-dev
Checks in your browser
- このセクションについては Vue に特化した話ではない
Unit Tests
- もし jest を使っている環境であれば、jest-axe を使ってみる
- (1) jest-axe を読み込んで
- (2) expect で
toHaveNoViolations
で使用できるか確認する
const { axe , toHaveNoViolations } = require('jest-axe'); // (1)
expect . extend ( toHaveNoViolations ); // (2)
img に alt が入っていないかをチェックするテスト
it ('matcher の使い方を示す' , async () => {
const render = () => '<img src="#"/>' ;
const html = render();
expect(await axe(html)).toHaveNoViolations();
})
上記はエラーが出て、解決方法として deque のリンクが提示される。
Vue のテスト方法については Vue Test Utils Edd Yerburgh 氏の Testing Vue.js Applications を参考にされたし。
フォーカスのチェックをするテストは以下参考
expect(document.activeElement).toBe(container.querySelector('input'))
フォーカス可能な要素かの一覧:Focusable Elements - Browser Compatibility Table
End-to-end, or inter-component testing
- ユニットテストだけでは不十分なことがある
- 単一のコンポーネントに限定されるわけではない
- パーツの相互作用がインクルーシブを担保することもある
- モーダルにおいてはモーダル内でのフォーカスだけではなく、モーダルから外れた後のフォーカス管理のテストも必要である
- コンポーネントレベルに限定するテストツールでは相互作用でアクセシビリティが守られているかをテストはできない
- その代わりにブラウザ上でエミュレートする Cypress テストが適している
- vue-a11y-dialog をテストするにあたり以下のような spec を記述する
describe('最初のテスト', () => {
it('モーダルの開閉時にフォーカスが管理されるかどうかをチェック', () => {
cy.visit('/');
cy.get('button#modaltrigger').click();
// 閉じるボタンをクリック
cy.get('[data-a11y-dialog-hide]').focused().click();
cy.get('button#modaltrigger').focused();
})
})
上記テストは以下の流れをテストしている
- ホームページにアクセス
-
modaltrigger
というIDのボタン要素を探す - そのボタンをクリックする
- モーダルのクローズボタンがフォーカスされることか確認
- 閉じるボタンをクリック
-
modaltrigger
の id を持つ button 要素にフォーカスが戻るか確認
上記例は単純なものなので、Cypress でテストするほどのものではないが、コンポーネントテストで実施できなかったテストのギャップや不足分を埋めるためのものである。
The most important testing of it all
- 半自動的にテストでオールグリーンになったとしてアクセシビリティなアプリであるわけではない
- 自動化するために自分で書いたテストコードであるということ
- 自動化されたものは問題の30%しか抽出できない
- 自動テストはアクセシビリティの教育目的でもリファクタリング目的でも有用ではあるが
- それによってアクセシビリティの問題すべてを理解・解決できるものではない
- より確実なテストというのは当事者にユーザテストしてもらうことである
- 実際に支援技術を使ってもらってアクセシビリティが担保できているか
- 自分のスクリーンリーダーの使い方が標準だと思ってはいけない
- 実際に支援技術を使ってもらってアクセシビリティが担保できているか
- 自動テストの結果を自身のプロダクトへの利点として活かせるようにすることが大事
- 決して Lighthouse などで100点であることを誇らない
-
Building the most inaccessible site possible with a perfect Lighthouse score - Manuel Matuzović
- 100点を出すがまったくアクセシブルでないサイトをつくって騙すことが出来る
-
Building the most inaccessible site possible with a perfect Lighthouse score - Manuel Matuzović
- 多様なユーザーを集めてのアプリテストを実行する
- コンセプトの段階からアクセシビリティ関連のテストを補完していく
- UXとアクセシビリティの関係も忘れてはならない
- デザインと機能が理解されてない場合、両方が損なわれている
- 認知障害がある人には根本的な問題となる
- 障害者テスターを専門に探してくれるサービスも増えている
Action Steps
- Chrome または Firefox に aXe ブラウザ拡張機能をインストールするか、Lighthouse の Accessibility を参考にする
- eslint をすでに使っている場合は、eslint-plugin-vuejs-accessibility を追加してみる
- Jest を使っているなら、jest-axe をセットアップすることを検討する
- 最も重要なテストは、既存のユーザと接触すること。どこで障壁があるかに遭遇しているかを聞いてみてください。次の段階として、障害当事者に対してユーザーテストを実施する