新しい擬似クラス:isと:whereの使い方

今回は、CSS Selectors Level 4で仕様策定された比較的新しい擬似クラスの:is
と:where
の使い方についてご紹介したいと思います。どちらも複数のセレクタをまとめることができるので便利なんですが、違いがよく分からなかったのでちょっと調べてみました。
擬似クラスとは
MDNの解説を引用すると、
CSSの擬似クラス (pseudo-classes) は、セレクターに付加するキーワードであり、選択された要素に対して特定の状態を指定します。
と書いてあります。よく分からないのでもう少し読んでみると、
例えば、擬似クラス
:hover
を使用すると、ユーザーのポインターがボタンの上に乗ったときにボタンを選択し、この選択されたボタンをスタイル設定することができます。
と続き、
/* ユーザーのポインターが乗っているすべてのボタン */
button:hover {
color: blue;
}
というコード例が掲載されています。これでなんとなく分かりました。特定の状態を表すのが擬似クラスで、擬似クラスにスタイルを指定することでその状態の時だけにそのスタイルが適用されるということでしょう。
擬似クラスには他にも:active
とか:visited
、:checked
とか:first-child
などたくさん種類があります。
擬似クラス - CSS: カスケーディングスタイルシート | MDN
CSS の擬似クラス (pseudo-classes) は、セレクターに付加するキーワードであり、選択された要素に対して特定の状態を指定します。例えば、擬似クラス :hover を使用すると、ユーザーのポインターがボタンの上に乗ったときにボタンを選択し、この選択されたボタンをスタイル設定することができます。
https://developer.mozilla.org/ja/docs/Web/CSS/Pseudo-classes

今回ご紹介する:is
もwhere
もこの擬似クラスに分類されます。
:is
:is
は擬似クラスの中でも関数擬似クラスと言われているようです。これは:where
も同じですね。
:is
はCSSの擬似クラス関数で、セレクターのリストを引数に取り、リスト中のセレクターの何れか一つに当てはまる要素をすべて選択します。数多くのセレクターを小さくまとめて書くのに便利です。
ということで、複数のセレクターを一つにまとめることができるので、コードの見通しがよくなるというメリットがあります。具体的に見ていきましょう。
<article>
<h2>...</h2>
...
<h3>...</h3>
...
<h4>...</h4>
</article>
article
要素以下にh2
、h3
、h4
があり、これらに共通のスタイルを指定したい時、通常であれば
article h2,
article h3,
article h4 {
line-height: 1.5;
}
このようなコードを書くと思いますが、:is
を使うと以下のように書くことができます。
article :is(h2, h3, h4) {
line-height: 1.5;
}
セレクタ部分が一行で済むようになりますので、見やすく整理しやすくなります。
ちなみにこれをCSSネストで書くと、
article {
:is(h2, h3, h4) {
line-height: 1.5;
}
}
こうなります。こうするとさらに親子関係が明確になって分かりやすいかと思います。
もう一つ例を挙げます。
<nav>
<a>...</a>
</nav>
<aside>
<a>...</a>
</aside>
<section class="sidebar">
<a>...</a>
</section>
nav
要素の子要素a
、aside
要素の子要素a
、.sidebar
要素の子要素a
にそれぞれ同じスタイルを当てたい時の通常のコードは、
nav a,
aside a,
.sidebar a {
text-decoration: none;
}
こうなりますが、:is
を使うと、
:is(nav, aside, .sidebar) a {
text-decoration: none;
}
このように簡潔になります。そしてしつこいですがこれもネストして書くと、
:is(nav, aside, .sidebar) {
& a {
text-decoration: none;
}
}
こうなります。覚えてしまえば簡単なので無駄に使いたくなりますね。
:is() - CSS: カスケーディングスタイルシート | MDN
:is() は CSS の擬似クラス関数で、セレクターのリストを引数に取り、リスト中のセレクターのいずれか一つに当てはまる要素をすべて選択します。数多くのセレクターを小さくまとめて書くのに便利です。
https://developer.mozilla.org/ja/docs/Web/CSS/:is

:where
続いては:where
ですが、基本的な使い方は:is
と全く同じです。そのため、先ほどの2つの例を:where
で書き換えてみると、
/* 最初の例 */
article {
:where(h2, h3, h4) {
line-height: 1.5;
}
}
/* 次の例 */
:where(nav, aside, .sidebar) {
& a {
text-decoration: none;
}
}
このようになり、全く同じ使い方になることがお分かりいただけるかと思います。
:where
はCSSの擬似クラス関数で、セレクターリストを引数として取り、列挙されたセレクターのうちの何れかに当てはまるすべての要素を選択します。
MDNの解説文もほぼ同じ内容です。
:isと:whereの違い
書き方も使い方も全く同じであるならば、両者の違いは一体何なのでしょうか。MDNの解説を読み進めていくと、
:where()
と:is()
の違いは、:where()
は詳細度が常に0であるのに対して、:is()
は引数内で最も詳細度の高いセレクターの詳細度を取ります。
とのことです。
:where() - CSS: カスケーディングスタイルシート | MDN
:where() は CSS の擬似クラス関数で、セレクターリストを引数として取り、列挙されたセレクターのうちの何れかに当てはまるすべての要素を選択します。
https://developer.mozilla.org/ja/docs/Web/CSS/:where

詳細度とは
詳しくはMDNのサイトをお読みいただくとして、語弊を恐れず簡潔に言うと詳細度が大きい方のスタイルが適用されるということです。指定しているセレクタの数や種類によって詳細度は変わり、スタイルの衝突が起きると両者のうち詳細度が大きい方のスタイルが適用されることになります。
詳細度 - CSS: カスケーディングスタイルシート | MDN
詳細度 (Specificity) は、ある要素に最も関連性の高い CSS 宣言を決定するためにブラウザーが使用するアルゴリズムで、これによって、その要素に使用するプロパティ値が決定されます。詳細度のアルゴリズムは、CSS セレクターの重みを計算し、競合する CSS 宣言の中からどのルールを要素に適用するかを決定します。
https://developer.mozilla.org/ja/docs/Web/CSS/CSS_cascade/Specificity

:where
の詳細度は常にゼロなので、引数に指定したセレクタがどんなに大きかろうが全体の詳細度はゼロとして処理されます。
:whereの使いどころ
では、詳細度が常にゼロになることのメリットは何なのでしょうか。これがよく分かりませんでした。詳細度が常にゼロなら使う意味ないと思っていましたが、ある時、普段よく見ているサイトで読んだ内容が目から鱗でした。
日本語Webサイトを意識したモダンリセットCSS「kiso.css」をリリースしました – TAKLOG
単なるスタイルのリセットを超えて「より良いデフォルト」を提供することを目指したリセットCSS「kiso.css」をリリースしました。その名が示すように、Webサイト構築の「基礎」として機能します。
https://www.tak-dcxi.com/article/introduce-kiso-css/

この中で詳細度を低く抑えるという項目があるのですが、リセットCSSの中で使うことで後から容易に上書きできるように敢えて:where
を使って指定している、ということが書かれてありました。これにはなるほどねと感心させられました。リセットCSSって、UAスタイルシートで指定されているブラウザの初期値を一旦リセットして再度スタイリングし直すためのもの、要するに後から必ず上書きするものなんですね。なので上書きする時に余計な詳細度について考える必要をなくすため、:where
を使っているという内容でした。頭いいですね。
当ブログのリセット部分
これを知ってから、当ブログのリセット部分には:where
を使うようにしました。
/* generic block-level elements */
:where(p) {
margin-block-start: unset;
margin-block-end: unset;
}
:where(blockquote) {
margin-block-start: unset;
margin-block-end: unset;
margin-inline-start: unset;
margin-inline-end: unset;
}
:where(figure) {
margin-block-start: unset;
margin-block-end: unset;
margin-inline-start: unset;
margin-inline-end: unset;
}
:where(img) {
max-inline-size: 100%;
block-size: auto;
vertical-align: bottom;
}
/* heading elements */
:where(h1, h2, h3, h4, h5, h6) {
margin-block-start: unset;
margin-block-end: unset;
}
/* lists */
:where(ul, ol) {
margin-block-start: unset;
margin-block-end: unset;
padding-inline-start: unset;
}
:where(dd) {
margin-inline-start: unset;
}
:where(dl) {
margin-block-start: unset;
margin-block-end: unset;
}
こちらの詳細はまた別の機会に投稿したいと思います。
最後に
擬似クラスは他にもたくさん種類があり、使わないことはないくらいお世話になっています。まだまだ他の擬似クラスも勉強していきたいなと思います。