Svelte Accessibility warnings 補足資料(2024-02-21)
https://gyazo.com/c33e46be44807f962107a5696515bd99
この資料は Svelte Japan Offline Meetup #2 のLT資料における補足資料です。
SvelteコンパイラにおけるWAI-ARIAの仕様のどこを基準としているのか?
ARIA仕様を扱えるaria-queryは現在のSvelteでは最新版になってる
Programmatic access to the WAI-ARIA 1.2 Roles Model. This package tracks the W3C Recommendation (last update: 6 June 2023).
GitHub - A11yance/aria-query: Programmatic access to the ARIA specification
よりWAI-ARIA 1.2準拠(W3C勧告)していると言えそう
バージョンによってはルールで非推奨になったり禁止になったりすることもあるので注意
role=genericでaria-labelつけられなくなったみたいに
妥当+1.iconだと思うルール群
a11y-accesskey
accessKey属性を使用しない
<a href="https://example.com/" accessKey="U">Example Domain</a>
WindowsだとAlt(+Shift)と特定のキー
MacOSだとControl+Optionと特定のキー
妥当+1.icon
支援技術のショートカットキーと被る場合を考慮できなそう
a11y-aria-activedescendant-has-tabindex
aria-activedescendantを使用する場合はtabindex属性を使用する
今どこにフォーカス要素があるかを支援技術へ通知するもの
使用パターン:Actions Menu Button Example Using aria-activedescendant | APG | WAI | W3C
妥当+1.icon
高度なARIAなので万一のケアレスミス対策?な印象
特にtabindexの値は関係なさそう
code:js
// aria-activedescendant-has-tabindex
if (
name === 'aria-activedescendant' &&
!is_dynamic_element &&
!is_interactive_element(node.name, attribute_map) &&
!attribute_map.has('tabindex')
) {
push_warning(attribute, 'a11y-aria-activedescendant-has-tabindex');
}
https://github.com/sveltejs/svelte/blob/1a721e5916bb71639f8fcc2cdffe3e99988ff578/packages/svelte/src/compiler/phases/2-analyze/a11y.js#L756-L764
a11y-aria-attributes
要素によって許可されていないWAI-ARIAのroleを使用しない
<meta aria-hidden="false" /> <!-- aria属性は使えない -->
妥当+1.icon
ありがたいですね
a11y-autofocus
autofocusを使用しない
基本的にページを読み込んだ時にフォーカスされる
ブラウザによっては挙動が異なる、支援技術を使っている場合読み上げで混乱する可能性がある
妥当+1.icon
意図せぬキーボードの操作を防ぐという意味でよさそう
ただし<dialog>においてのみ例外として捉えていきたい
ブラウザは自動的に<dialog>内の最初にフォーカスされるインタラクティブ要素にフォーカスされる
もし先にフォーカスさせたい要素がある場合はautofocusを提示しておく
参考
Focus  |  web.dev
<dialog>内でautofocus属性がほぼ必須になる話
<dialog>の中だけは警告を出さないようにルール変えられないかなyamanoku.icon
a11y-click-events-have-key-events
非インタラクティブな要素にon:click イベントがある場合、キーボードイベントハンドラをつけるようにする
クリック以外の操作ができないため
on:keypressやon:keydownなどを併せて使用する
妥当+1.icon
そうなる前にインタラクティブな要素で検討できるかを考えましょう
<button type="button">
妥当なHTML要素を使用するようにしましょう
a11y-distracting-elements
気が散るような要素が使われていないかを確認する
含まれる要素
<marquee>...横移動する
<blink>...点滅する
妥当+1.icon
こうした表現はCSSでできるけど、気が散る可能性はありますね
見た目表現をHTMLで表現するのは避けていきたいyamanoku.icon
a11y-incorrect-aria-attribute-type
WAI-ARIA属性に正しい値が含まれているかを確認する
妥当+1.icon
JavaScript操作だけしてると抜けがちな部分
true/falseが文字列としてなのかBoolean値なのかの違い
HTMLで扱う場合は前者、JSから操作する場合は後者(ARIA IDL)
論理属性(Boolean attribute)という属性そのもので値自体不要なものもある
WAI-ARIAではないけどdisabledは論理属性
値が存在しない場合は空文字("")ではなくundefinedにしておく必要もある
列挙型属性といってtrue/false以外の値を含む場合もあったりする
a11y-interactive-supports-focus
インタラクティブなroleやインタラクティブなハンドラを保つ場合はフォーカス可能な状態でなければならない
code:html
<div role="button" on:keypress={() => {}} />
妥当+1.icon
a11y-click-events-have-key-eventsと同じ感想
a11y-misplaced-role
roleを指定できない要素にrole指定された場合に指摘してくれる
対象
const invisible_elements = ['meta', 'html', 'script', 'style'];
妥当+1.icon
<svelte:head>の中で書く場合に間違わないようにアリかも
https://svelte.dev/docs/special-elements#svelte-head
<link>も含めて良さそう
変数としての対象に含めていいかは別ですが<label>とかもダメだったりします
参考:https://momdo.github.io/html-aria/#docconformance
a11y-misplaced-scope
scope 属性は、<th> 要素でのみ使用する
かなり珍しいルール
見出しの対象範囲を指定する
col, row, colgroup, rowgroup
HTML[th要素]見出しの対象範囲を指定する - TAG index
かつては<td>でも使用できていた
現仕様では非推奨扱い
妥当+1.icon
細かいルールだなという印象
a11y-missing-content
見出し要素(h1、h2 など)とリンク要素に対し、コンテンツを持つように強制する
code:html
<!-- A11y: <a> element should have child content (<a>要素は子コンテンツを持つべきです) -->
<a href="/foo" />
<!-- A11y: <h1> element should have child content (<h1>要素は子コンテンツを持つべきです) -->
<h1 />
妥当+1.icon
Webアクセシビリティの基本として「テキスト情報を与える」は大前提としたい
見出しやリンクをaria-labelで意味づけしていると辿れないことがある
a11y-mouse-events-have-key-events
on:mouseover や on:mouseout に対し、それぞれ on:focus と on:blur を付けることを強制する
キーボードユーザーも操作できるようにする
ツールチップのようなものがイメージしやすいかも
妥当+1.icon
ルールとしては真っ当
この手のはアクセシブルにする難易度は高いかなと感じる
ツールチップはpopoverで表現してみてもいいかも(要挙動チェック)
code:html
<button type="button" popovertarget="popover-content" popovertargetaction="show">
Show popover
</button>
<div id="popover-content" popover>Popover content</div>
a11y-no-interactive-element-to-noninteractive-role
インタラクティブな要素を非インタラクティブな要素に変換するために使用してはいけない
非インタラクティブな role:article、banner、complementary、img、listitem、main、region、tooltip
非対話型コンテンツとも言われる
妥当+1.icon
そもそも妥当なHTML要素を使用するようにしましょう
一時的にそうせざるを得ない状況もあるかもですが、TODOコメントよろしくいずれは直していったほうがいい
a11y-no-noninteractive-element-interactions
非インタラクティブ要素にイベントハンドラー(マウスやキーハンドラー)を使用してはいけない
妥当+1.icon
期待されうる挙動ではない
インタラクティブな要素と同じように実現するには徒労感が半端ない
参考:非対話型コンテンツにclickイベントを設定してはいけない理由 | きるこの日記帳
そもそも妥当なHTML要素を使用するようにしましょう
a11y-no-noninteractive-element-to-interactive-role
role を非インタラクティブな要素をインタラクティブな要素に変換するために使用してはいけない
インタラクティブな role
button、link、checkbox、menuitem、menuitemcheckbox、menuitemradio、option、radio、searchbox、switch、textbox
妥当+1.icon
そもそも妥当なHTML要素を使用するようにしましょう
a11y-no-noninteractive-tabindex
非インタラクティブな要素にtabindexを付けている場合警告する
妥当+1.icon
インタラクティブなroleであれば問題なし
それよりも妥当なHTML要素を使用するようにしましょう
ちなみに<dialog>では使用禁止です
https://html.spec.whatwg.org/multipage/interactive-elements.html#the-dialog-element
a11y-no-static-element-interactions
clickをもつ静的な要素にはroleが必要になる
<div on:click={() => ''} />
妥当+1.icon
押せるかどうかが見た目以外で判断できないので
やっぱり妥当なHTML要素を使用するようにしましょう
a11y-positive-tabindex
tabIndex属性に1以上の数値を入れないようにする
妥当+1.icon
要素が期待されるタブの順序から外れてしまい、キーボードユーザーに混乱を招くことになります。
書いてあるとおり
コンポーネントで意図的にフォーカスさせる用途がある場合は0あるいは-1といった負の値を用いましょう
0 ... フォーカス可能なだけでフォーカス順序は指定されていない
-1 ... フォーカス可能だが、一度外れるとJS操作以外でしか戻すことは出来ない
a11y-role-has-required-aria-props
role を持つ要素は、その role に必要な属性をすべて持つ必要がある
妥当+1.icon
忘れないように教えてくれて助かります
a11y-role-supports-aria-props
明示的または暗黙的な、定義された role を持つ要素は、その role がサポートする aria-* プロパティのみ使用する
妥当+1.icon
誤った指定を避けられるので助かります
a11y-unknown-aria-attribute
タイポされたARIA属性を使用していないか確認する
Fuzzy Match(あいまい検索)のような機構がある
妥当+1.icon
助かります
a11y-unknown-role
タイポされたroleを使用していないか確認する
Fuzzy Match(あいまい検索)のような機構がある
妥当+1.icon
助かります
疑問?.iconだと思うルール群
a11y-hidden
一部の要素ではaria-hiddenの使用しないようにする
具体的にどういうのがダメなのか?
見出しだけっぽい
code:js
if (name === 'aria-hidden' && regex_heading_tags.test(node.name)) {
push_warning(attribute, 'a11y-hidden', node.name);
}
https://github.com/sveltejs/svelte/blob/1a721e5916bb71639f8fcc2cdffe3e99988ff578/packages/svelte/src/compiler/phases/2-analyze/a11y.js#L741C1-L743C5
疑問?.icon
見出しは基本あるべきだとは思うけど…
ARIAの操作自体をコンパイラから強制されるべきだと思っていないyamanoku.icon
併せて使ってはいけないroleやARIAと対応する分には良いと思う
a11y-img-redundant-alt
img の alt 属性に、image、picture、photo という単語を含めない
JSXのルールを参考?
https://github.com/jsx-eslint/eslint-plugin-jsx-a11y/blob/7566e13531f09a040daee4c16d0cba0c28d321c4/docs/rules/img-redundant-alt.md
疑問?.icon
適当に付けられるのを避けたいのはわかるけど…
そういう表現をしたい人にとっては別に含めてもいいでしょうyamanoku.icon
むしろ避けてaltを空にしてしまっているほうが問題があったりする
日本語含めた英語以外の他言語は対応してないですね
code:js
if (/\b(image|picture|photo)\b/i.test(alt_attribute)) {
push_warning(node, 'a11y-img-redundant-alt');
}
a11y-invalid-attribute
<a>要素のhrefの値が空、#、javascript:にしてはならない
疑問?.icon
javascript:は良いと思う
これだけは絶許
<button>でやれ案件
他は厳しすぎないだろうか?
#をトップ先へのアンカーリンクとして用いる場合もある
空の場合は現在のページ自体に飛ぶ
a11y-label-has-associated-control
ラベルとコントロールの関連付けがされているかをチェック
疑問?.icon
<label>C <input type="text" /></label>はわかる
問題は<label for="id">B</label>としているパターン
値のないfor属性だけでもコンパイルは通ってしまう
<label for>B</label>でもOK
以前この状態についてを指摘したことがあります
a11y-label-has-associated-control : Check if for attribute of label matches the id attribute of the control. · Issue #6515 · sveltejs/svelte · GitHub
a11y-media-has-caption
メディアにキャプションを提供するようにする
mutedでミュート指定しているなら不要
疑問?.icon
疑問というか、惜しいというか…
ヒントが足りてない
code:html
<video>
<source src="movie.mp4" type="video/mp4">
<track kind="captions" srclang="ja" lang="ja" src="movie-ja.vtt" label="日本語">
<track kind="captions" srclang="en" lang="ja" src="movie-en.vtt" label="English">
</video>
字幕データ自体も必要
もちろん中身が正しいかもあるが、trackを入れて終わりにだけはしたくない
映像に関するガイドラインもあるMaking Audio and Video Media Accessible | Web Accessibility Initiative (WAI) | W3C
a11y-missing-attribute
要素にとって必要な属性が入っていることを強制させる
妥当+1.icon
<html> には lang が必要
読み上げや翻訳変換で影響
<img> には alt が必要
<iframe> には title が必要
WCAG 2.2における達成基準2.4.1「ブロックスキップ」を満たすための方法
H64: Using the title attribute of the iframe element | WAI | W3C
<object> には title、aria-label または aria-labelledby が必要
どの属性が読み上げで優先されるかアクセシブルな名前の解決の理解をしないと危うい
参考:Accessible Name and Description Computation 1.1
疑問?.icon
<a> には href が必要
リンク要素として扱わない場合、この形にしておくのは問題ない
spanと同じようなものだと思っていただけるとよいかも
パンくずやページネーションで現在位置を示す場合、扱うこともできる
code:html
<ul>
<li><a href="/">TOP</a></li>
<li><a aria-current="page">現在のページ</a></li>
</ul>
<area>にはalt、aria-label または aria-labelledby
hrefがないものには名前付け禁止(aria-label または aria-labelledbyをつけてはならない)
WAI-ARIA 1.2のrole=genericの存在が適応された影響?
a11y-no-redundant-roles
すでに要素に存在するroleに冗長にroleをつけるのを禁止する
code:ng.html
<button type="button" role="button" />
対象要素一覧
https://github.com/sveltejs/svelte/blob/a2014809ec4ea9e20bace548bca44f9d4d37db96/packages/svelte/tests/validator/samples/a11y-no-redundant-roles/input.svelte
疑問?.icon
支援技術によってはそのroleがないと対応できない場合もある
かつては<li>のスタイルをイジるとリストアイテムと認識できないこともあったので意図的につけていたこともある
その旨を受けて<ul>, <ol>, <li>については非対応にはなっている
False Positive for a11y-no-redundant-roles on List Items · Issue #8854 · sveltejs/svelte · GitHub
必要かどうかを議論しつつ都度対応が必要になってくる
現状<search role="search">は特に対象になってない(支援技術へのサポートがまだ)
仕様に従う意味では正しいが、ユーザーのことを想定すると過剰な気もする
a11y-structure
DOMの構造が誤っていないかを確認する
特定の子要素を使用する場合にその中に含まれているか
疑問?.icon
code:js
if (node.name === 'figcaption') {
if (!is_parent(node.parent, 'figure')) {
push_warning(node, 'a11y-structure', true);
}
}
code:js
if (node.name === 'figure') {
const children = node.fragment.nodes.filter((node) => {
if (node.type === 'Comment') return false;
if (node.type === 'Text') return regex_not_whitespace.test(node.data);
return true;
});
const index = children.findIndex(
(child) => child.type === 'RegularElement' && child.name === 'figcaption'
);
if (index !== -1 && index !== 0 && index !== children.length - 1) {
push_warning(childrenindex, 'a11y-structure', false);
}
}
figcaptionだけじゃねーか!!!!!!
ul > li とか…そういうのも...
#2024 #Svelte