画面遷移から考えるNuxtアプリケーションをアクセシブルにする方法
https://vuefes.jp/2023/sessions/yamanoku/__og_image__/og.png
画面遷移から考える Nuxt アプリケーションをアクセシブルにする方法
ブラウザ側で画面遷移を制御するクライアントサイドルーティングという手法はサーバーからの待ち時間を無くしスムーズにブラウジングできるようにして、画面遷移における認知負荷を減らす点で活用されています。 Nuxt 3.4 からは実験的に搭載された View Transitions API の設定でスムーズな遷移アニメーションが実現できるようになりました。
しかし、これらの手法は情報が正しく伝わるかどうかのアクセシビリティ観点から考慮が必要なものです。 本セッションでは、画面遷移時にどのように情報が伝わっているのか、Nuxt アプリケーションをよりアクセシブルにするためのアプローチを実装例を元に紹介します。
話の構成
1. クライアントサイドルーティングでのアクセシビリティ上の懸念について
2. クライアントサイドルーティングなアプリケーションをアクセシブルにするには?
3. 今後の期待としての Navigation API
4. まとめ
1. クライアントサイドルーティングでのアクセシビリティ上の懸念について
スムーズな画面遷移についてのあれこれ
いわゆる「SPA的な挙動・遷移」とするものをここでは指すようにする
クライアントサイドルーティングについて紹介
サーバーサイド側からではなくJSとWeb APIを活用してクライアント側でルーティングすること
なぜクライアントサイドルーティングをするのか?
Server Routing VS Client Routing | Vike
Client Routing enables:
Faster page navigation.
早いページ遷移
Client-side state preserved across navigation.
クライアント側の状態がナビゲーションにまたがって保持される
状態の管理が複雑なアプリケーションであれば適している可能性はある
音楽プレイヤー(再生されているという状態を保持しないといけない)
Nested Layouts.
入れ子レイアウト
App Router
Custom page transition animations.
カスタマイズされた画面遷移アニメーション
NuxtのTransitionsについて
https://nuxt.com/docs/getting-started/transitions#transitions
View Transitions APIでの操作についても触れる
Nuxtにexperimentalで導入された
https://github.com/nuxt/nuxt/releases/tag/v3.4.0
対応ブラウザじゃないと動かない
View Transitions API入門 - 連続性のある画面遷移アニメーションを実現するウェブの新技術 - ICS MEDIA
https://nuxt.com/docs/getting-started/transitions
https://github.com/nuxt/nuxt/blob/edbc471583c1b609526e6e84c24184bec4fc3318/packages/nuxt/src/app/plugins/view-transitions.client.ts
How to create beautiful view transitions in Nuxt using the new View Transitions API | Michał Kuncio - Modern webdev - the cool parts
使い方
Vue SFC Playground
Vue.jsでのView Transitions API活用実装事例
Use View Transitions API for <transition> · Issue #7881 · vuejs/core · GitHub
Vue.js 本体にも提案もされている
クライアントサイドルーティングにすることで失われるものについて
クライアントサイドルーティングに限らずJavaScriptで実装することは本来のWebがもつ機能を失ってしまう可能性がある
スクリーンリーダーで現在位置が認知できない可能性
画面が切り替わったこと、変化したことが伝わらない問題
フォーカスマネジメント
操作位置が元に戻る可能性
意図せぬ場所へ移動してしまい現在位置を見失う可能性
キーボード操作における達成基準: 2.1.1
スクロール位置
拡大鏡を使ってブラウジングしている場合、元の位置がわからなくなる可能性
アニメーションがページ閲覧を阻害する可能性
注意障害や前庭障害があるユーザーへの影響
View Transitions APIのアクセシビリティ課題
https://github.com/WICG/view-transitions/blob/main/explainer.md#interactivity-and-accessibility
ページ遷移は純粋に視覚的なアフォーダンスです。インタラクティブ性という点では、transition要素は元の要素に関係なく、divのように振る舞います。例えば、tabindex属性を意図的に追加するなどして、transition要素に直接インタラクティブ性を追加することで、開発者はこの意図を破ることができます。しかし、これは推奨されません。
ページ遷移の段階は、スクリーンリーダーのような支援技術から隠されてしまいます。
Webとはアクセシブルなもので、その良さを消してしまわないようにしたい
なぜアクセシビリティを考慮しないといけないのか?
調査 | 日本視覚障害者ICTネットワーク
スクリーンリーダーなどの視覚障害者向けの支援技術の利用状況に関する調査報告
https://jbict.net/survey/at-survey-03
パソコンやスマートフォンの用途について
様々なもので使用している
作成したアプリがアクセシブルでないとユーザーの操作機会を失っていることになる
障害者雇用率制度
従業員が一定規模であれば障害当事者の割合を法定雇用率以上にする必要がある
社内で共同に使うアプリケーションが当事者にとって使えないものだとしたら?
改正障害者差別解消法について
概要について説明
来年4月より施行
現状Webアクセシビリティ対応は必須の扱いではないが
環境の整備でやる必要がることを説く
合理的配慮=個別対応
個別対応するのをずっとやっていかないといけないのか?
先に対応しておくことでコストを減らすという視点
#47「障害者差別解消法とWebアクセシビリティ」 | ミツエーテックラジオ | ミツエーリンクス
https://www.youtube.com/watch?v=2gESGUHjUCs
2. クライアントサイドルーティングなアプリケーションをアクセシブルにするには?
スクリーンリーダーで認知できない可能性
タイトルの変更を行う
definePageMetaを使う
definePageMeta
code:js
definePageMeta({
title: 'ページタイトル'
});
部分的な変化はRoute Announcerでよいの
https://github.com/elk-zone/elk/blob/b723d51786d7767510dd3eff62b72fc339d0c2cb/components/aria/AriaAnnouncer.vue
elkのAriaAnnouncer
https://github.com/elk-zone/elk/blob/b723d51786d7767510dd3eff62b72fc339d0c2cb/components/aria/AriaAnnouncer.vue#L46
512 という数字が気になる
Why 512 ms delay at AriaAnnouncer.vue ? · elk-zone/elk · Discussion #2402 · GitHub
debounce と title watcher で実装し直したいらしい
ページごとでタイトルを用意することはアクセシビリティガイドライン上でも必要
https://www.w3.org/WAI/WCAG22/quickref/#page-titled
達成基準 2.4.2: ページタイトルを理解する
フォーカスマネジメント
フォーカスの種類
主に逐次フォーカスに関する部分
bodyに移動させる、が基本形
ではあるが。。。
スクリーンリーダーによってはすべてを読み上げてしまう可能性もある
code:vue.html
<template>
<main ref="main">
<slot />
</main>
</template>
<script setup lang="ts">
import { onMounted, ref } from "vue";
const mainRef = ref(null);
onMounted(() => {
mainRef.value.focus();
});
</script>
この場合、mainにあるslot内部を全部読み上げてしまうかも知れない
場合によっては違う位置が好ましいこともあるかもしれない
Nasted Layout の場合
1ページの中で他のコンポーネントの状態を変化させる
Layout 内にフォーカスが移動する
Route Announcer に移動する、が最善かもしれない
code:html
<p aria-live="assertive" tabindex="-1" role="alert">
{{ title }}
</p>
変化・更新される部分をより小さく、影響範囲を低くする意図
スキップリンクに戻すのも1つの手段
https://gyazo.com/88fbc958740d9cfb0938073f4b7d60d0
共通のコンテンツであれば
本当にそこに戻すのでよいのか正しいのか?を疑う
余計な世話で操作の機会を奪ってないか?
autofocusで強制的にフォーカスを奪っている場合もある
<dialog>内でautofocus属性がほぼ必須になる話
ダイアログはまぁそう
アプリである場合、遷移した後にナビゲーションのリンクに移動してしまう場合
状況・コンテキストによってどの位置が正しいかはユーザーインタビューなどを通じて知りたい部分でもある(正解はない)
アニメーションがページ閲覧を阻害する
prefers-reduced-motionが設定されている場合はアニメーションさせないようにする
code:ViewTransitionsAPI_ignore.css
@media (prefers-reduced-motion) {
::view-transition-group(*),
::view-transition-old(*),
::view-transition-new(*) {
animation: none !important;
}
}
https://developer.chrome.com/docs/web-platform/view-transitions/#reacting-to-the-reduced-motion-preference
code:all_animation_ignore.css
@media (prefers-reduced-motion: reduce) {
*,
::before,
::after {
animation-delay: -1ms !important;
animation-duration: 1ms !important;
animation-iteration-count: 1 !important;
background-attachment: initial !important;
scroll-behavior: auto !important;
transition-duration: 0s !important;
transition-delay: 0s !important;
}
}
code:prefers-reduced-motionを取得する場合.js
window.matchMedia('(prefers-reduced-motion: reduce)');
https://web.dev/prefers-reduced-motion/
スクロール位置
BFCache
ブラウザ側でキャッシュを保持して位置を把握する
仕様側はどうなっているのか
If entry's scroll restoration mode is "auto", and entry's document's relevant global object's navigation API's suppress normal scroll restoration during ongoing navigation is false, then restore scroll position data given entry.
https://html.spec.whatwg.org/multipage/browsing-the-web.html#persisted-user-state-restoration
Nuxtの場合
scrollBehaviorのsavedPositionで保持
Nuxt 3 でページ遷移したら遷移後にちゃんとスクロール位置をトップにさせる | mirumi.tech
BFCacheがある場合で位置を保持しておりsavedPositionがあればそれを返す
code:ts
scrollBehavior(to, from, savedPosition) {
if (savedPosition) {
return savedPosition;
} else {
return { top: 0 };
}
},
コードリーディングしてみる
router/packages/router/src/scrollBehavior.ts at ed4573ad02dc375580c9dba7be86b60b9f21f9f4 · vuejs/router · GitHub
ScrollBehaviorHandler自体は使用されていない?
code:ts
/**
* Scroll position similar to
* {@link https://developer.mozilla.org/en-US/docs/Web/API/ScrollToOptions | ScrollToOptions}.
* Note that not all browsers support behavior.
*/
export type ScrollPositionCoordinates = {
behavior?: ScrollOptions'behavior'
left?: number
top?: number
}
3. 今後の期待としての Navigation API
Nuxt、フレームワークごとでの改善を考えるのもそうだがWeb標準でどう解決できるか考えたい
Should ARIA provide better support for routing in single-page applications? · Issue #1353 · w3c/aria · GitHub
SPAにおいてWAI-ARIAでどうルーティングのサポートができるか?の問い
History APIは使い勝手として足りない部分が多かった
https://github.com/dvoytenko/web-history-api/blob/master/problem.md
History は iframe の中の遷移も含むため Top Level Frame での履歴を管理したい場合に、意図せず履歴が壊れる場合がある。
ページ上で発生する Navigation をフックする方法がないため、全てのリンクのクリックを監視するといったことをしないといけない。それでも完璧にはインタラプトしきれない。
遷移をインデックス(history.go(-2))で管理するために、どのインデックスがどの状態か、別途マッピングを管理する必要がある。
History API とブラウザの履歴との連携はブラウザによって差異がある。
pushState で保存する state には制限が多く、かつ壊れやすい。
https://github.com/ember-a11y/ember-a11y-refocus#what-this-addon-does
pushStateは、新しいコンテンツが存在することを、ブラウザ、そして、その範囲によって、スクリーンリーダーに何も知らせないので、スクリーンリーダーのユーザーは、新しいコンテンツが存在すること、あるいは、新しいページへのナビゲーションが成功したことを知る方法がありません。さらに、フォーカスは、予測可能な方法でリセットされるのではなく、それがあった場所にとどまります。
Navigation API
History APIを包括して改善されたもの
使い方
Modern client-side routing: the Navigation API - Chrome Developers
スクロール制御
scroll: 'manual'で独自設定ができる
フォーカス制御
focusReset: 'manual'で独自設定ができる
現在はChromeのみの実装
https://caniuse.com/mdn-api_navigation
Navigation API · Issue #435 · web-platform-tests/interop · GitHub
Interopにて相互運用注力APIの1つとして投票されている
SPA遷移においては今後ここと連携していきたい
まとめ
画面遷移、アクセシビリティ考慮しようと思えば思うほどやることは多いし制御は大変
ブラウザとは偉大である
再開発することになる覚悟をもつ必要
MPAやSSRで済むような提案も考えていく
Navigation APIの到来を期待しつつWeb APIの進化とフレームワークの相互関係性、いい感じでやっていきましょう(?)
今後の Nuxt 側のアプローチは?
Accessibility roadmap · Issue #23255 · nuxt/nuxt · GitHub
Route accessibility announcer · Issue #14673 · nuxt/nuxt · GitHub
視野には入ってる
今後の取組に期待!
参考文献
Vue Announcer の使い方 | Vue A11y
vue-a11y
Marcus Herrmann
https://azukiazusa.dev/blog/do-not-compromise-html-functionality-in-your-web-front-end-implementation/#画面遷移時の実装
Webアプリケーションアクセシビリティ参考
5.8 画面遷移
Navigation API による「JS での画面遷移」と SPA の改善 | blog.jxck.io
View Transitions API と Navigation API でページ遷移アニメーションを実装してみる
Accessible JavaScript Routing Prototypes
GatsbyJSチームによる検証
Vue Fes Japan 2023
yamanoku