:has() 擬似クラスはユーザースタイルシートで大きな利点を発揮する

2023年12月リリースの Firefox 121 で :has() 擬似クラスがサポート(www.mozilla.org)され、全ブラウザで使えるようになりましたが、長年待ち望んできた :has() を実際に使ってみると、ユーザースタイルシートで Web ページをカスタマイズする際に大きな利点があることに気付きました。そして、このことは制作者としてのマークアップ思想の変化(class 属性の設計思想の変化)にもつながっています。

ユーザースタイルシートにおける :has() 擬似クラスの効用

§

多くのサイトに存在するグローバルナビゲーションを例に挙げます。グロナビのマークアップはたいてい次のようになっています。

<nav class="gnav">
	<ul>
		<li><a href="/page1">ページ1</a></li>
		<li><a href="/page2">ページ2</a></li>
		<li><a href="/page3">ページ3</a></li>
	</ul>
</nav>

<nav> 要素あるいは <ul> 要素にはグローバルナビゲーションであることを示すクラス名ないし ID が付与されていることでしょう。<li> 要素にもリストアイテムであることを示すクラス名(e.g. class="gnav__item")が付いているかもしれません。しかしこのようなマークアップでは、ユーザースタイルシートで一部のリストアイテムを非表示にしたり見た目を変えたりするのが従来は困難でした。

「2番目のリンクは自分にとって必要ないので非表示にしたい」と思ったとしても、.gnav a[href="…"] { display: none } の指定ではリンクは消えるものの <li> 要素は表示されたままとなってしまいます。セレクターを .gnav > ul > li:nth-child(2) にすれば解決しそうですが、動的ページの場合はログアウト時とログイン時でナビゲーションのアイテム数が変わることも珍しくありません。そもそも「リストの N 番目」という不確定な情報を元にユーザーに判断させる(不安を与える)ようなことは誠実なマークアップとは思えません。

そのため、従来の考え方では <li> 要素にも各リストアイテムを区別できるクラス名を付与するのがより良いマークアップであったといえます。

<!-- ユーザースタイルシートでのカスタマイズ性を考慮したマークアップ例 -->
<nav class="gnav">
	<ul>
		<li class="gnav__item -page1"><a href="/page1">ページ1</a></li>
		<li class="gnav__item -page2"><a href="/page2">ページ2</a></li>
		<li class="gnav__item -page3"><a href="/page3">ページ3</a></li>
	</ul>
</nav>

しかし今となっては ユーザースタイルシートで .gnav > ul > li:has(a[href="…"]) のようなセレクター指定ができるため、このような考慮をする必要性は薄まりました。

:has() 擬似クラスで対応できない事例

§

:has() 擬似クラスでは対応できないケースも存在します。

  • 子孫要素に各アイテムを区別できる要素や属性が存在しない場合(e.g. <li>foo</li> <li>bar</li>
  • 表の列(<colgroup> 要素と <col> 要素)
  • rowspan 属性により行方向のセル結合がなされた表の行

このような場合は引き続き各アイテムを区別できるようにするのが望ましいでしょう。

ユーザースタイルシートの考慮は難しい

§

とはいえ過度な考慮は必要ありません。ユーザーはマニアックなカスタマイズをしたければユーザースクリプトで DOM を任意に書き換えることができます。

それに制作者スタイルシートで使われないクラス名を設定すると管理上のコストが増大しますし、なにより後の改修時に「内容が書き変わったのにクラス名の変更が忘れられてる(制作側が誰も気づかない)」となればそれはユーザーに事実に反した情報を提供することにもなり得ます。見た目や挙動に影響を与えないクラス名を設定することにはリスクもあるため、よほどしっかりした運用体制が確立されていない限り導入がためらわれるのも事実です。

このように「ユーザースタイルシートへの考慮をしたい(すべき)」という理想と、なかなかそうもいかない現実とのギャップに頭を悩まされるものですが、:has() 擬似クラスの登場で完全ではないにしろある程度考慮不要なケースが増えたのは喜ばしいところです。

制作者の立場でのジレンマは低減された一方で、ユーザーとしての利便性は大きく向上した :has() 擬似クラスは CSS の歴史の中でもとくに偉大な進化だと思っています。