CSS 属性セレクターの Case-sensitivity 識別子は明記すべきか否か

CSS セレクターでは属性セレクターにて input[type='text' i] のように末尾に識別子を付けて大文字・小文字を区別するかどうかを指定できます。

Selectors Level 4 の仕様(W3C)では従来から大文字・小文字を区別しない i 識別子が定義されており、2018年11月21日版にて逆に区別することを明示する s 識別子が追加されているのですが[1]、後者は Firefox しか対応していない(Can I use...)ため実用はできず、現状は i を付けるか否かの2択の状況です。

ところでこの識別子を付けるべきか否かというベストプラクティス的な観点での議論はあまり行われていないように感じます。世の中が HTML 4.01 から XHTML 1.0 へ移行して以来、HTML5 で text/html 前提に戻って以降も、要素名や属性名ともども小文字に統一するのがスタンダードとなり、今どき <DIV ROLE="TAB"> などとは書かないでしょうから、セレクター側も classid などの一部属性を除けば小文字前提で大きな問題は発生しません。

とはいえ CSS はユーザーにスタイルの意図を伝えるものですから、実際に問題が発生するか否かに関わらず、識別子を明示することでユーザースタイルシートを書く場合などへのアクセシビリティがより高まります。

ところがいざそうしようとすると、どの属性に付けるかのルール決めが難しいことが分かりました。langtype などのいくつかの属性は HTML 仕様書の 4.16.2 Case-sensitivity of selectors(WHATWG) にて大文字・小文字を区別しないと定められているので、わざわざ i 識別子を付けずとも同様に働きます。

<style>
  input[type='text'] {
    /* `i` 識別子を付けていないが `<input type="TEXT">` にも適用される */
  }
</style>

<label>Keyword <input type="TEXT" name="q" /></label>

これに対して CSS セレクター側で i 識別子を明記すべきか、それとも不要かは判断に迷うところですね……。付けた方がユーザーに対してより意図が明確になるとは思うのですが、仕様で定められているのだから必要ないだろうと言われればそれはそのとおりです。

role 属性と aria-* 属性はどうでしょう。これらは ARIA in HTML 仕様で定義されているのですが、以下のような Note があるのです。

While modern browsers treat the role or aria-* attribute values as ASCII case-insensitive, not all assistive technologies will correctly parse these values.

To reduce interoperability issues, authors are strongly encouraged to use ASCII lowercase for aria-* and role attribute values. Further, authors are encouraged to rigorously test with different browser and assistive technology combinations to ensure that their content will be correctly exposed to their users.

4.4 Case requirements for ARIA role, state and property attributes(W3C)

「著者は属性値に小文字を使用することを強く勧める(strongly encouraged)」とあるので、基本的に大文字で書くのは避けるべきです。

しかしそれはあくまで HTML の表記上のルールの話であり、セレクター側に求められているものではありません。「大文字で書くべきではない」と「大文字表記の要素があった場合にどう処理するか」は別問題です。私の考えとしては、大文字の属性値であってもセレクター側は適用させるのが良いと思います。

<style>
  [role='main' i] {
    /* `role=MAIN` にも適用させる */
  }

  [aria-current='page' i] {
    /* `aria-current=Page` にも適用させる */
  }
</style>

<div role="MAIN">...</div>

<a href="..." aria-current="Page">...</a>

もちろん aria-labelledby のように ID reference ないし ID reference list 型を持つ属性は大文字・小文字を区別する必要があります。もっともそれらを CSS セレクターで使うことは稀でしょうけれど。

<style>
  [aria-labelledby~='label1'] {
    /* 本当は `s` 識別子を付けたいが、未対応ブラウザが多いのでやむを得ず識別子なしとする */
  }
</style>

<button aria-labelledby="label1 label2"></button>
<span id="label1">...</span>
<span id="label2">...</span>

本ブログでは先日よりこれらの考えを取り入れており、input[type]role, aria-* に対して i 識別子を明記するようにしています。人力でのチェック作業は厳しいため Stylelint のルールやプラグインを探したのですが、それらしきものが見つからなかったため自作(GitHub)をしました。将来的には設定変更のみで s 識別子の強制化にも対応できるようにしています。

脚注