きままに記録箱

フロントエンドたまに他のはなし

CSSの :has()・:is()・:where()を積極的に使って隣にいる要素からスタイルを簡単に置き換えよう

CSS近年登場した:has()・:is()・:where()という3つの擬似クラスについて紹介する。

新し目のクラスということで日本語の資料はまだまだ少ないが、覚えておくと便利なので是非この機会に覚えていってほしいです。

本編前の注意書き

本記事執筆時点(2023/04/22)で、:has()擬似クラスだけFirefoxのサポート対象外です(一応設定でフラグをONにすると使える参考。)。

サポートの最新状況は以下のページを参考にしてください。

caniuse.com

:has()擬似クラス

hasの中に指定されたクラスの親要素のスタイル指定が出来ます。 特に子~子孫の要素のスタイルによって親要素のスタイルを変更できることに強みを持っています。

これまではJavaScriptを使わないと使えなかった方法がCSSで指定出来るようになりました。

サンプルコード

以下例のコードを載せました。

See the Pen Untitled by miily8310s (@miily8310s) on CodePen.

例では以下のことを行っています。

  • greenクラスのimg(緑のレゴの写真)タグを子に持っているarticle要素の背景色を緑
  • redクラスのimg(緑のレゴの写真)タグを子に持っているarticle要素の背景色を赤
  • imgタグを持たないarticle要素は中のテキストを中央寄せに

:has()擬似クラスの書き方

構造は以下の形です。

<親となるCSSセレクター>:has(<子~子孫となるCSSセレクター>) {
  スタイル指定
}

サンプルコードでは背景色を緑へ変更を以下で実現させています。

article:has(.green) {...}

ちなみに:not()擬似クラスと組み合わせて、子にいない場合のスタイル指定をしたい場合は注意が必要です。先に:not()を指定して上げる必要があります。英語と一緒ですね。

article:not(:has(img)) {...}

:is()擬似クラス / :where()擬似クラス

こちらは使い方は殆ど一緒なので、まとめて紹介します。 :has()擬似クラスが親要素のスタイルについてだったのに対して、:is()擬似クラス / :where()擬似クラスはカッコの中に指定したCSSセレクターに当てはまる要素のスタイルの指定が出来ます。コロン(:)始まりなのに慣れればそのままの意味なので簡単ですね。

サンプルコード

See the Pen Untitled by miily8310s (@miily8310s) on CodePen.

is()擬似クラス / :where()擬似クラス両方記載しています。どちらか一方をコメントアウトしてみると、どっちの書き方でも同じ意味をなしていることがわかると思います。

:is()擬似クラス / :where()擬似クラスの書き方

構造は以下の形です。

:is(<スタイルを当てたいCSSセレクター>) {
  スタイル指定
}

:where(<スタイルを当てたいCSSセレクター>) {
  スタイル指定
}

サンプルコードではheader、footer要素の文字サイズ変更を以下で実現させています。

:is(header, footer) p {
  font-weight: bold;
}

:where(header, footer) p {
  font-weight: bold;
}

複数指定することも出来ます。

サンプルコードでは、headerもしくはmain要素に内包されている、h1・p要素の文字色をピンク色に変えています。

:is(header, main) :is(h1, p) {
  color: #FF5F9E;
}

:where(header, main) :where(h1, p) {
  color: #FF5F9E;
}

2つの違い

パッと見違いがなさそうな2つですが、実は決定的な違いを持っています。 :where()擬似クラスで指定したクラスの詳細度が常に0になる点です。

一見気にすることがないような点ですが、実はフロントエンド開発で共通用のスタイル指定に密接に関係してくる問題になります。

例えば共通用のスタイルで以下のスタイル定義をした場合、詳細度は以下のようになります。 詳しい度の数え方は今回触れませんが、2つのクラスを指定している影響で「2」になってしまいます。なので、詳細度を下回るスタイル指定をしてもスタイルの上書きに失敗してしまいます。

// 詳細度が2になる
header p, footer p {
  font-weight: bold;
}

// 詳細度が1になる。なのでスタイルの上書きが出来ない。
p {
  font-weight: normal;
}

ここで:where()擬似クラスに置き換えて見ましょう。詳細度が同じなので、これでスタイルの上書きが反映されます。

// 詳細度が1になる
:where(header, footer) p {
  font-weight: bold;
}

// 詳細度が1になる
p {
  font-weight: normal;
}

これまでスタイル指定が上手くいかないときは!importantを指定して無理やり適用してしまうケースも少なくありませんでしたが、:where()擬似クラスによって詳細度を小さくしてくれるおかげでこのリスクが減ります。

とりあえずこういった問題を防ぐためにも:where()擬似クラスを優先して使っていきましょう。

参考文献

has()擬似クラス

Meet :has, A Native CSS Parent Selector (And More) — Smashing Magazine

:is() / :where()擬似クラス

New CSS functional pseudo-class selectors :is() and :where()

最近話題の静的ジェネレーターのAstroでは、前述の詳細度の観点からスタイルの上書きに:where()擬似クラスが使用されていることが公表されています。

Astro 1.4.0 Release | Astro