Accessibility

アクセシビリティ

はじめに

カルーセルUIは、Webサイトのコンテンツを少ないスペースで魅力的に表現できる一方、致命的なアクセシビリティの問題を抱えています。例えば「スライドを変更して新しく表示された内容を読む」という基本的な動作でさえ、スクリーンリーダのみを通して行おうとすると、途端に容易なものではなくなります。

Splideは、すべてのユーザが快適にスライダーを使えるよう、アクセシビリティの向上に努めています。バージョン4からは、W3CのCarousel Design Patternに準拠したうえで、スクリーンリーダーが動的にコンテンツを読み上げられるようライブリージョンも導入しました。

ただし残念ながら、W3Cのデザインパターンも完全無欠とは言えません。

  • 矛盾点がある
  • すべてのスライダーに適用できるわけではない

たとえば、彼らのサンプルは「フェード」タイプに限定されていますが、これをそのまま「スライド」タイプのスライダーに適用すると壊れてしまいます。この例をそのまま採用してしまっているスライダーも多く、逆にアクセシビリティを損ねてしまっています。

以上を踏まえながら、このページでは、Splideがアクセシビリティ向上のために実装している機能などについて、くわしく解説していきます。

「基本型」と「タブ型」

W3Cのオーサリングプラクティスにおいて、スライダーはBasic(基本型)、Tabbed(タブ型)、およびGrouped(グループ型)の3つのタイプに分けられています。Splideのスライダーはこのうち、ページネーションが存在するかどうかを基準に、BasicかTabbedのいずれかの型を用いて実装されます。

説明
Basicページネーションを持たないスライダーに対して適用される
Tabbedページネーションを持つスライダーに対して適用される。ページネーション自体はタブパターンに準拠して実装される
Grouped該当なし

タブ型は、スライダーをタブUIのように見なすため、それぞれのスライドをtabpanel(タブパネル)、ページネーションをtab(タブ)として実装する必要があります。さらにタブUIの仕様は、以下で説明するような、複雑なキーボード管理を要求します。

ランドマーク化

多くの場合、スライダーの中にはメインのコンテンツと関連性の高いものが含まれています。たとえば、バナー、ギャラリー、おすすめ記事、商品の一覧などがあげられ、基本的にはその中だけで情報が完結します。Splideは初期設定でスライダーをランドマークにすることにより、支援技術に対してそれが主要なコンテンツの一部であることを伝え、たとえばランドマーク間をジャンプするなどといった機能の恩恵をユーザが受けられるようにしています。

ただし、メインのコンテンツと関連性のないものが含まれていたり、あるいは単に装飾目的のスライダーだったりする場合は、ランドマーク化することがふさわしくない場合もあります。そのようなときは、代わりにgroupロールを使用してください。

<div class="splide" role="group" aria-label="...">
</div>
HTML

HTMLではなく、オプションからでも変更できます。

new Splide( '.splide', { role: 'group' } );
JavaScript

キーボード操作

バージョン4からは、独自のキーボード操作(例えば矢印キーでスライダーを移動するなど)を有効にするオプションは、デフォルトで無効になりました。代わりに、タブのデザインパターンに必要なショートカットが有効化されています。

ショートカット

ページネーションがフォーカスを保有しているとき、Splideは次のようなキーボード操作を受け付けるようになります。

キー説明
横並びのページネーションにおいて、フォーカスを前のボタン (RTLでは次のボタン)に移動すると同時に、対応するスライドを表示する
横並びのページネーションにおいて、フォーカスを次のボタン (RTLでは前のボタン)に移動すると同時に、対応するスライドを表示する
縦並びのページネーションにおいて、フォーカスを前のボタンに移動すると同時に、対応するスライドを表示する
縦並びのページネーションにおいて、フォーカスを次のボタンに移動すると同時に、対応するスライドを表示する
Space対応するスライドを表示する
Enter対応するスライドを表示する
Homeフォーカスを最初のボタンに移動し、最初のスライドを表示する
Endフォーカスを最後のボタンに移動し、最後のスライドを表示する

基本的に、ページネーションのボタンがキーによってフォーカスされると、対応するスライドは自動的に表示されます。これはHaving selection follow focusと呼ばれ、「フォーカス追随の選択」と和訳されていますが、この訳ではなんのことだか分かりませんね。直訳すると「選択をフォーカスに追随させる」となりますので、すなわち「フォーカスと同時に対象を選択状態にする」ということです。

ただし、waitForTransitionが有効な場合は、フォーカスだけが先行する場合があります。この場合でも、Spaceなどで選択できますので、特に問題にはなりません。

keyboardおよびpaginationKeyboardオプションによって、ショートカットを有効にするかどうかを変更できます。

ページネーションの方向

ページネーションの方向は、スライダーの方向を決めるdirectionオプションで決まります。例えば、direction'ttb'の場合、実際にどう見えているかにかかわらず、Splideはページネーションを縦並びとして認識します。結果、aria-orientationにはvertical(縦)が設定され、フォーカスを移動するためにキーを監視します。

仮に、水平のスライダーに縦並びのページネーションを、あるいはその逆を実装したい場合は、paginationDirectionオプションで明示的に方向を指定してください。

new Splide( '.splide', {
direction : 'ttb',
paginationDirection: 'ltr',
} );
JavaScript

ロービング・タブインデックス

Splideは、ページネーションのフォーカスをロービング・タブインデックス(roving tabindex)を用いて管理します。これは、タブ化されたインターフェースに求められる機能で、アクティブなボタンにはtabindex="0"を、それ以外の非アクティブなボタンにはすべてtabindex="-1"を割り当てることで実現されます。タブインデックスが状況に応じて変化するため、roving(さまよう)という名称がついたのだと思います(ラジオボタンなど、ほかのUIにも採用されます)。

これにより、例えば次のような利便性を得ることができます。

  • ページネーションから離れた後、再度戻ってきた際、以前選択した位置から操作を再開できる

  • Tabを一度押すだけでページネーションを抜けられる。スライドが10枚ある場合、この機能がないページネーションでは10回押さないと抜けられない

次のスライダーを用いて、実際に試してみてください。次に進む矢印をクリックしてからTabを押すと、アクティブなボタンにフォーカスが移動します。そこから、でページネーション内を移動できます。

  • 01
  • 02
  • 03
  • 04
  • 05
  • 06
  • 07
  • 08
  • 09

ライブリージョン

非表示のスライドが表示された際、新たに表示されたコンテンツをスクリーンリーダーを通して読み取るのは、決して容易なことではありません。我々はそれを視覚情報を頼りに認識できますが、聴覚あるいは点字による情報に頼っているユーザにとって、ボタンとは別の場所が動的に更新されてしまうスライダーは、とても扱いにくい存在です。

これを解決するための最大の武器が、ほかならぬライブリージョンなのですが、厄介なことにaria-live="polite"属性を割り当てるだけではうまく動作しません。なぜならば、一般的なスライダーの実装では、支援技術がコンテンツの変更を検出するためのaria-relevant条件を満たすことができないからです。

詳細は割愛しますが、長い試行錯誤の結果、なんとか動かすことに成功しました🎉

ただし、自動再生が有効な場合、ライブリージョンはとても邪魔になります(ページを表示している限り、延々としゃべり続けます)ので、自動再生中はこの機能が無効化されます。停止すると、再度有効化されます。

なお、isNavigationtureにした場合、ライブリージョンは有効になりません。また、liveオプションを用いると、手動で無効にできます。

Windows Narrator、JAWS、NVDA、並びにVoice Overを用いてテストしましたが、すべてのスクリーンリーダで正しく動作することを保証することはできません(全リーダを全ブラウザでテストするのは、個人では不可能です😱)。

自動再生の制御(WCAG 2.2.1, 2.2.2)

制限時間があるコンテンツを利用する前に、利用者がその制限時間を解除できる — Timing Adjustable

自動更新する情報が、自動的に開始し、その他のコンテンツと並行して提示される場合、利用者がそれを一時停止、停止、もしくは非表示にする、又はその更新頻度を調整することのできるメカニズムがある。 — Pause, Stop, Hide

W3Cによると、自動再生するスライダーは、以下の3つの機能を持つ必要があります。

  • 自動再生を再生・停止するボタンを提供する
  • スライダー内の要素がフォーカスした際は、自動再生を停止する
  • スライダー上にマウスがある間は、自動再生を停止する

全盲、ロービジョン(光をまぶしく感じるなど)、巧緻性障害などのユーザは、コンテンツを読むのに十分な時間が必要です。自動再生を有効にする場合は、再生・停止用のボタンを同時に提供することを検討してください。Splideでは、マークアップを張り付けるだけで簡単にトグルボタンを実装できます。

他2点に関しては、pauseOnHoverpauseOnFocusが初期状態で有効になっているため、特別な実装は必要ありません。

視覚効果の低減(WCAG 2.3.3)

アニメーションが、機能又は伝達されている情報に必要不可欠でない限り、インタラクションによって引き起こされるモーションアニメーションを無効にできる。 — Animation from Interactions

ユーザの中には、アニメーションやトランジションなどの効果に対して、頭痛や吐き気を感じる人もいます(たとえば前庭運動障害を患うユーザなど)。Splideは、prefers-reduced-motion: reduceを検出した際

  • speedrewindSpeed0で上書きます
  • autoplaypausedで上書き、再生ボタンを押さない限り自動再生が行われないように変更します

この結果、スライドの遷移アニメーションは再生されず、瞬時に表示されるようになります。ただし、ドラッグやスワイプ後のトランジションだけは無効になりません。これを無効にしてしまうと、何が起こっているのかわからなくなってしまうため、「必要な」モーションであると判断しました。

Chromeの開発者ツールを使うと、この機能をテストできます。ツールの中にある"Rendering"タブの中から、"Emulate CSS media feature prefers-reduced-motion"という項目を探してください。

  • 01
  • 02
  • 03
  • 04
  • 05
  • 06
  • 07
  • 08
  • 09

スライダーが単なる装飾目的で使用される場合を除き、スライドの移動を自動再生「だけ」に頼るのは避けてください。そうしないと、prefers-reduced-motionが有効になった際、ユーザがスライドを変更する手段がなくなります。

フォーカスの可視化(WCAG 2.4.7)

キーボード操作が可能なあらゆるユーザインタフェースには、フォーカスインジケータが見える操作モードがある。 — Focus Visible

キーボードを利用しているユーザのため、フォーカス可能な要素に対して、フォーカス時の特別なスタイルを付加する必要があります(例えばアウトライン)。 キーボードユーザに対してのみこのスタイルを有効にするには、:focus-visible疑似クラスを利用する必要があります。ただし

  • IEはこのクラスをサポートしていません(IE自体がもうすぐ終了しますが)
  • Safariは、最新バージョンのみこのクラスを実装しています(2022/03/15にリリース)

ゆえに、あまり気が進みませんでしたが、:focus-visibleを再現できるようなパッチを一時的に作成し、組み込むことにしました。バージョン4のテーマには、キーボードによってフォーカスを得た場合にのみ各ボタンを強調するようなスタイルが導入されています。

下の例において、前に戻るボタンを一度クリックしたのち、Tabを押してみてください。

  • Sample Slide 01
  • Sample Slide 02
  • Sample Slide 03
  • Sample Slide 04
  • Sample Slide 05
  • Sample Slide 06
  • Sample Slide 07
  • Sample Slide 08
  • Sample Slide 09
  • Sample Thumbnail 01
  • Sample Thumbnail 02
  • Sample Thumbnail 03
  • Sample Thumbnail 04
  • Sample Thumbnail 05
  • Sample Thumbnail 06
  • Sample Thumbnail 07
  • Sample Thumbnail 08
  • Sample Thumbnail 09

もし独自のスタイルを当てる場合は、ルート要素が持つ.is-focus-inクラスを利用してください。このクラスは、フォーカスがキーボードによってスライダー内に移動した際出現します。

/* :focus-visibleをサポートしているブラウザのためのCSS */
.splide__arrow:focus-visible {
outline: 3px solid tomato;
}
/* その他のブラウザのためのスタイル */
.splide.is-focus-in .splide_arrow:focus {
outline: 3px solid tomato;
}
CSS

このクラスは、フォーカスを失ってもすぐには取り除かれませんので、必ず:focus疑似クラスとともに使用してください。

フォーカス可能な要素(WCAG 1.3.1)

何らかの形で提示されている情報、構造、及び関係性は、プログラムによる解釈が可能である、又はテキストで提供されている。 — Info and Relationships

Splideは、スライダーの表示領域に完全に収まっていないスライドに対してaria-hidden属性を割り当て、スクリーンリーダ、あるいはその他の支援技術がそれらのスライドを認識しないようにしています。ところが、スライド自体に<input><textarea>などのフォーカス可能な要素が含まれている場合、Tabにより依然として、隠れた要素にフォーカスを移動できてしまいます。このとき、スクリーンリーダはそれが何なのかの説明をしてくれません。

この問題に対応するため、Splideは隠れているスライドが含む以下の要素に対してtabindex="-1"を適用し、一時的にフォーカスできないようにしています。

  • <a>
  • <button>
  • <textarea>
  • <input>
  • <select>
  • <iframe>

ほとんどの場合この対応でうまくいきますが、上記に漏れる以下の要素が含まれている場合は追加対応が必要です。

  • <area>
  • <audio controls>
  • <summary>
  • <video controls>
  • contenteditable属性が有効になっている要素
  • tabindex="0"を持った要素

どの要素に対してtabindex="-1"を適用するかは、focusableNodesオプションの値が決定しています。この値はセレクタですので、次のようにして検索対象を増やしたり、逆に不必要な要素を減らしたりできます。

{
focusableNodes: 'a, button, ..., area, [contenteditable]',
}
JavaScript

ただし、残念ながらtabindex="0"が設定された要素が含まれる場合、このオプションだけではうまく対応できません。これは、-1に設定した後に再びスライドが表示された際、0(あるいはその他の整数値)に戻す処理が必要になるためです。スライダーの中でtabindexを用いてフォーカスを制御することはないと信じたいですが、仮にそのような対処が必要になった場合は、visibleおよびhiddenイベントを利用すればインデックスの切り替えを実装できます。

ARIA属性(WCAG 4.1.2)

すべてのユーザインタフェース コンポーネント (フォームを構成する要素、リンク、スクリプトが生成するコンポーネントなど) では、名前 (name) 及び役割 (role) は、プログラムによる解釈が可能である。又、状態、プロパティ、利用者が設定可能な値はプログラムによる設定が可能である。そして、支援技術を含むユーザエージェントが、これらの項目に対する変更通知を利用できる。 — Name, Role, Value

矢印のボタンやページネーションなど、インタラクション可能なすべての要素には、適切なARIA属性が割り当てられます。ただし、ラベル等に割り当てられているテキストは英語ですので、対象ユーザに合わせて適宜翻訳してください。

基本型スライダー

ルート

role

タグが<section>でない場合にregion

aria-roledescription

carousel

トラック

aria-live

ライブリージョンが有効の場合polite。ただし、自動再生中はoff

aria-atomic

ライブリージョンが有効の場合true(^4.0.3)

aria-busy

ライブリージョンが有効の場合、トランジション終了時にtrueになり、一定時間後falseに戻る (^4.0.3).

各スライド

role

group

aria-roledescription

slide

aria-label

%s of %s、つまり"{スライド番号} of {総スライド数}"

aria-hidden

ビューポートに収まりきっていないすべてのスライドにtrue

isNavigationが有効の時は、次のように変化します。

role

slideFocusが有効な場合button、そうでない場合group

aria-roledescription

インタラクティブ要素なので割り当てられない

aria-hidden

ビューポートに収まりきっていないすべてのスライドにtrue

aria-controls

同期しているスライダーのすべてのID

aria-label

"Go to slide %s"

矢印

aria-controls

トラック要素のID

aria-label

前へ戻る矢印には"Previous slide"または"Go to last slide"、次に進む矢印には"Next slide"または"Go to first slide"

disabled

それ以上進めなくなったときに適用される

再生・停止ボタン

aria-controls

トラック要素のID

aria-label

自動再生中は"Pause autoplay"、再生中は"Start autoplay"

タブ型スライダー

基本型を踏襲しつつ、次の要素に対する属性が変わります。

各スライド

role

tabpanel

aria-roledescription

slide

aria-label

%s of %s、つまり"{スライド番号} of {総スライド数}"

aria-hidden

ビューポートに収まりきっていないすべてのスライドにtrue

ページネーション

role

tablist

aria-label

"Select slide to show".

ページネーションリスト

role

presentation

ページネーションの各ボタン

role

tab

aria-controls

対応するすべてのスライドのID

aria-label

"Go to slide %s"または"Go to page %s".

aria-selected

アクティブなボタンにtrue、それ以外にfalse