コハム

Webクリエイターのコハムが提供する、Web制作に役立つ情報をまとめたブログです。

もうCSSの特異性に悩まない!:where()でimportantからの卒業

CSS :where() Selector: The Zero-Specificity Superpower

記事は上記記事を意訳したものです。

※当ブログでの翻訳記事は元サイト様に許可を得て掲載しています。


CSSの固有性と格闘しているような、デジタル相撲の選手があなたの美しいスタイルに座り込んでしまうような瞬間を経験したことはありますか?私もあります。実際、先週の火曜日には約16回ほど経験しました。

でも、もしあなたに、Twitterを開いた瞬間に私のモチベーションが消えるよりも速く、あなたの固有性の悩みを消し去ることができるCSSセレクタが目の前に隠れていると言ったら?それが:where()疑似クラスです - マント​​とテーマ音楽に値する、現代CSSの無名のヒーローです。

固有性の問題(または:なぜ私がコーヒーを飲み始めたのか)

もし1週間以上ウェブサイトを構築したことがあれば、おそらく自分自身とこのような会話をした経験があるでしょう:

「なぜスタイルが適用されないんだ?」
別のクラスを追加
「まだ何も変わらない?!」
IDを投入
「もう勘弁してくれ—」
深く恥ずかしく感じながら!importantをタイプ

これが私たち全員が戦ってきたCSS固有性の戦いです。セレクタが固有性の階層における「重み」に基づいて競合する複雑な戦闘システムです。IDはクラスより上位に立ち、クラスは要素より上位に立ちます。そして疑似クラスや属性をその中に投げ込むと…まあ、それが私がいつも別のコーヒーを淹れるタイミングです。

「CSSでは、固有性は複数のルールが同じ要素にスタイルを適用できる場合に、どのCSSルールが適用されるかを決定するアルゴリズムです。これは基本的に、競合するスタイルが存在する場合にブラウザが「誰がボスか」を決定する方法です。」 - MDN Web Docs

長年にわたり、私たちの選択肢は限られていました:すべてをリファクタリングするか、さらに具体的なセレクタを使用するか(こんにちは、.sidebar .widget .widget-content p span.highlighted)、または暗黒面に降伏して!importantを使用するか。これらのどれも特に優雅な解決策ではありません。

:where() - 固有性の忍者の登場

:where()を緊張した状況を解消するのが得意な友人だと考えてください。CSS Selectors Level 4仕様の一部としてリリースされ、現在はすべての最新ブラウザでサポートされているこの疑似クラスには、それを絶対に素晴らしいものにする一つのスーパーパワーがあります:

固有性への影響はゼロです。まったくありません。ゼロです。

仕組みはこうです:

:whereセレクタ

/* これは固有性が0,0,0です */
:where(.header, .footer) a {
  color: blue;
}

/* そのため、このシンプルなセレクタでもそれを上書きできます */
a {
  color: red;
}

お分かりですか?私たちが合理的に具体的なセレクタ(ヘッダーとフッターのリンクを対象とする)に見えるものを書いたにもかかわらず、固有性は:where()によって完全に中和されています。これは、他のどんなセレクタも、単純な要素セレクタでさえも、それを上書きできることを意味します。

そしてそれが魔法です。

現実世界の魔法(がっかりする種類ではない)

これが実際のCSSシナリオをどのように変えるかを見てみましょう。「サイト構造と内部リンクの究極ガイド」で言及されているようにサイト構造を改善しようとしている場合、クリーンなCSS構成も戦略の一部であるべきです。

:where()以前 - 固有性の悪夢

/* 基本スタイル - 固有性: 0,0,1 */
button {
  padding: 8px 16px;
  border-radius: 4px;
}

/* プライマリボタン - 固有性: 0,1,1 */
.button-primary {
  background: blue;
  color: white;
}

/* セカンダリボタン - 固有性: 0,1,1 */
.button-secondary {
  background: grey;
  color: black;
}

/* 危険ボタン - 固有性: 0,1,1 */
.button-danger {
  background: red;
  color: white;
}

/* 後で「small」のようなグローバル修飾子を適用する必要がある場合... */
/* これはボタンタイプを上書きするためにより高い固有性が必要です */
.button-primary.small,
.button-secondary.small,
.button-danger.small {
  padding: 4px 8px;
  font-size: 12px;
}

このアプローチは機能しますが、うまくスケーリングしません。別のサイズや無効状態、読み込み状態が必要になったらどうなりますか?ますます複雑で具体的なセレクタに終わってしまいます。

:where()の後 - 禅の庭

/* ゼロ固有性のボタンタイプ */
:where(.button-primary, .button-secondary, .button-danger) {
  padding: 8px 16px;
  border-radius: 4px;
}

/* 最小限の固有性のボタンスタイル */
.button-primary {
  background: blue;
  color: white;
}

.button-secondary {
  background: grey;
  color: black;
}

.button-danger {
  background: red;
  color: white;
}

/* 修飾子は最小限の固有性でも上書きできます */
.small {
  padding: 4px 8px;
  font-size: 12px;
}

何が起こったか見ましたか?固有性の階層を平坦化し、固有性の競合なしで上書きを適用することがはるかに簡単になりました。このアプローチは、ユーティリティファーストCSSのような最新のCSS方法論と完全に一致しています。

:where()を使用するタイミング(使用しないタイミング)

ウェブ開発におけるすべてのことと同様に、いつも晴れて虹が出ているわけではありません。:where()が輝く場面は次のとおりです:

最適な用途: - 基本コンポーネントスタイル:後で固有性の戦いに巻き込まれることなく、基盤となるスタイルを定義します。 - テーマシステム:簡単に上書きできるテーマバリエーションを作成します。 - ユーティリティクラス:ユーティリティクラスがどこに適用されても機能することを確認します。 - デフォルト状態:より具体的な意図に譲るべきデフォルトスタイルを設定します。

あなたがウェブサイトを再設計し、「2025年のウェブデザイン顧客のための5つの大きな選択肢」に関するアドバイスに従っている場合、CSSストラテジーに:where()を採用することは間違いなく検討事項6であるべきです。

避けるべき場合: - 絶対に上書きしてはならない重要なスタイル:全体的な目的は上書きを容易にすることなので、強固であるべきスタイルには使用しないでください。 - カスケードが重要なスタイル:固有性が自然にその魔法を発揮するようにしたい場合もあります。

「最高のCSSは、うまく設計された建物のようなものです - 絶えず構造的な変更を必要とせず、多くの異なる構成をサポートする堅固な基盤があります。」 - CSS-Tricks

:where()と:is()の違い

「待って」と言っているのが聞こえますね、「これは:is()と同じではないの?」

ほぼ同じです!:is()疑似クラスもセレクタをグループ化しますが、重要なことに、最も具体的な引数の固有性を引き継ぎます。これは、それぞれをどのように使用するかに影響を与える重要な違いです。

:where()と:is()の違い

/* これは.sidebarの固有性(0,1,0)を取ります */
:is(.header, .sidebar, footer) a {
  color: blue;
}

/* これは中身に関係なく、ゼロ固有性(0,0,0)を持ちます */
:where(.header, .sidebar, footer) a {
  color: blue;
}

「WordPressですべてのビジネスオーナーが知っておくべきトップ5ウェブサイトデザイントレンド」で強調されているようなプロジェクトに取り組んだ経験から、これらのニュアンスを理解することでデバッグや挫折の何時間もの時間を節約できることがあります。

野生の:where():ケーススタディ

先月、私はクライアントのeコマースサイトを更新していました。それは…有機的に成長したと言いましょうか。CSSは単純な変更に考古学的発掘が必要となる、固有性の泥沼のようなもつれた混乱でした。

チェックアウトボタンのスタイルは特に問題でした:

野生の:where()

/* オリジナルCSS */
#checkout-section .product-list .item .button.buy-now {
  background: green;
  color: white;
  /* 特定のスタイリングがさらに50行 */
}

/* 無効状態を追加する必要がありましたが、これは上書きされませんでした */
#checkout-section .product-list .item .button.buy-now.disabled {
  background: grey;
  opacity: 0.7;
  cursor: not-allowed;
}

同等の固有性であるはずなのに、他のスタイルがカスケードする方法のために、無効状態は適切に適用されませんでした。解決策は?:where()を使用したリファクタリング:

:where()を使用したリファクタリング

/* リファクタリングされた基本スタイル */
:where(#checkout-section .product-list .item) .button.buy-now {
  background: green;
  color: white;
  /* その他のスタイル */
}

/* これで無効クラスが完璧に機能します */
.button.disabled {
  background: grey !important; /* リファクタリング後に削除できました */
  opacity: 0.7;
  cursor: not-allowed;
}

結果は?より保守可能なコードベースと、謎のスタイリングバグを修正するために私に支払い続ける必要のないクライアントでした。(ただ、おそらくその利点は自分自身に秘めておくべきでしょう…)

ブラウザサポート:実際にこれを使用できますか?

どんな刺激的なCSS機能に対しても永遠の質問です!良いニュースは、:where()は最新のブラウザ全体で優れたサポートを持っていることです。サポートされているのは:

  • Chrome 88+(2021年1月)
  • Firefox 78+(2020年6月)
  • Safari 14+(2020年9月)
  • Edge 88+(2021年1月)

まだInternet Explorerをサポートしている場合...まず、お悔やみ申し上げます、そして第二に、フォールバック戦略が必要になります。しかし、ほとんどの最新プロジェクトでは、問題なく使用できます。

このようなより最新のCSS機能を使用するためにサイトを更新することを検討している場合は、これらの新しいテクニックでパフォーマンスを最適化するための「WordPress Turboホスティングでウェブサイトをスーパーチャージ」に関する記事が役立つかもしれません。

今日から:where()を使い始める方法

この魔法のようなセレクタをツールキットに追加する準備はできましたか?以下は簡単なアップグレードパスです:

  1. 固有性の痛点を特定する:CSSで固有性の戦いをしている場所を探します(ヒント:!importantを検索)。
  2. 基本コンポーネントをリファクタリングする:バリアントクラスを持つ再利用可能なコンポーネントから始めます。
  3. ゼロ固有性の基盤を作成する:基本スタイルを:where()セレクタに移動し、バリアントをよりクリーンに保ちます。
  4. 徹底的にテストする:これはスタイルがカスケードする基本的な方法を変更するため、ページやシナリオ全体でテストします。

CSSをより保守可能にする方法についてもっと学びたい場合は、「Elementorで流動的なフォントにClamp()を使用する」ガイドでは、:where()と美しくペアになる別の現代的なCSSテクニックを紹介しています。

最終的な考え

結論:あなたのCSSに値する謙虚なヒーロー

CSSのトリックやテクニックの絶えず拡大する宇宙の中で、:where()は派手だからではなく、CSSの作者を最初から悩ませてきた根本的な問題(固有性の管理)を解決するからこそ際立っています。

固有性の荷物なしでセレクタをグループ化するツールを提供することで、:where()はより保守可能でスケーラブルなCSSアーキテクチャを可能にします。「WordPressメンテナンスの7つの大罪」で議論しているように、長期にわたってサイトを維持することを考えている場合に特に価値があります。

ですから、次に!important宣言に手を伸ばそうとするとき、:where()がより優雅な解決策を提供するかどうかを検討してください。あなたの未来の自分 - そしてあなたのコードを継承する人 - があなたに感謝するでしょう。

:is() vs :where() FAQ:尋ねるのが怖すぎることすべて

わかります。ここまで読んで、あなたの脳はクールな新しいCSSトリックに興奮しながら、同時に「でも待って、質問があります!」と叫んでいることでしょう。開発者フォーラムで見かける(そして時々午前2時に自分でつぶやいている)燃えるような質問に対応しましょう。

:is()と:where()の正確な違いは何ですか?

簡単な答え?固有性です。少し長い答え:

  • :is()はその最も具体的な引数の固有性を引き継ぎます
  • :where()は「固有性?知らないね」と言って、固有性の計算にゼロを貢献します

それだけです。彼らは一方(:is())が家族の評判を担い、もう一方(:where())が名前を変えて別の都市に移動した以外は同一の双子です。

/* これは#sidebar(1,0,0)の固有性を持ちます - ヘビー級 */
:is(#sidebar, .widget) p { color: red; }

/* これは(0,0,0)の固有性を持ちます - フェザー級 */
:where(#sidebar, .widget) p { color: blue; }

:where()と:is()はどのような場合に使い分ければいいですか?

これは「ハンマーとメスのどちらを使うべきか」と尋ねるようなものです。どちらもツールですが、目的が異なります:

  • 簡単に上書きできるべき基本スタイルが欲しい場合は:where()を使用します
  • スタイルが誤って上書きされないようにするために固有性が必要な場合は:is()を使用します

私は、コンポーネントの基盤には:where()を、特定の状態やバリエーションには:is()を使うことが多いです。

:where()は:is()と異なり、無効なセレクタを処理しますか?

おっ、微妙な違いに気づきましたね!金星⭐

  • :is()は寛容です - 1つのセレクタが無効であれば、それを無視して残りを適用します
  • :where()も同様に寛容です - 同じように機能します

この動作は、これらの疑似クラスなしのカンマ区切りセレクタとは異なります。カンマ区切りセレクタでは、1つの無効なセレクタがルール全体を無効にします。

:where()を:is()の中にネストしたり、その逆もできますか?

ああ、CSSインセプションの質問ですね!はい、絶対にできますし、それほど複雑でもありません:

/* 完全に有効 - 固有性は.importantによって決定されます */
:is(:where(.header, .footer), .important) p {
  color: purple;
}

内側の:where()は固有性にゼロを貢献し、:is()は.importantの固有性を引き継ぎます。それはロシアの人形をネストするようなものですが、内側のものは何も重さがありません。

:where()は本当に!importantの必要性を置き換えることができますか?

場合によっては!魔法の万能薬ではありません(CSSが私たちにそれを与えることはありませんが)、しかしより合理的な固有性階層を構築するのを助けることで、!importantの必要性を劇的に減らすことができます。

固有性戦争に対して核兵器(!important)で戦うのではなく、:where()は固有性があなたに対してではなく、あなたのために機能するシステムを設計することを可能にします。

:where()はメディアクエリとどのように相互作用しますか?

ありがとうございます、とても良い質問です!予想通り、メディアクエリ内で:where()を使用できます:

@media (max-width: 768px) {
  :where(.card, .panel) {
    padding: 10px;
  }
}

ゼロ固有性の動作は、メディアクエリの内部にあっても、まったく同じように機能します。

:where()と従来のセレクタを使用することのパフォーマンスへの影響は何ですか?

ブラウザベンダーのドキュメントとテストに基づくと、大きなパフォーマンスの違いはありません。最新のブラウザは複雑なセレクタに最適化されており、:where()は同等のカンマ区切りセレクタと比較して意味のあるオーバーヘッドを追加しません。

もしあるとすれば、固有性の戦いが発生したときに冗長なスタイル計算の数を減らすことで、複雑なスタイルシートのパフォーマンスを向上させる可能性があります。

SCSS/SASS/Lessで:where()を使用できますか?

はい!プリプロセッサは有効なCSSなので、これらを問題なく通過させます。SCSSでは次のようになります:

// :where()を使用したSCSS
.container {
  :where(h1, h2, h3) {
    margin-bottom: 1rem;

    &:hover {
      color: blue;
    }
  }
}

これは以下のように美しくコンパイルされます:

.container :where(h1, h2, h3) {
  margin-bottom: 1rem;
}
.container :where(h1, h2, h3):hover {
  color: blue;
}

:where()のブラウザサポートはフォールバックが必要ということですか?

最新のブラウザ向けに構築している場合(そして正直に言って、2025年には恐らくそうすべきです)、問題ありません。前述のように、:where()は2021年初頭以降、すべての主要なブラウザでサポートされています。

絶対にIE11のような古代のブラウザをサポートする必要がある場合は、フォールバックが必要です。1つのアプローチは、どこでも機能する基本スタイルを提供し、次に最新のブラウザ向けに:where()で拡張することです:

/* すべてのブラウザ向けの基本スタイル */
.card, .panel {
  padding: 16px;
}

/* 最新のブラウザ向けの強化された制御 */
:where(.card, .panel) {
  padding: 16px;
}

最新のブラウザは両方のルールを適用しますが、同じ順序で来て、2番目はゼロ固有性を持つため、競合を引き起こしません。

©コハム