CSSの「聖杯」がついに手元に:`:has()` 擬似クラスを巡る、パフォーマンスとアーキテクチャの真実
長年、CSSアーキテクトとしての我々が喉から手が出るほど欲していたもの。それは「子要素の状態に応じて親のスタイルを書き換える」という制御でした。これまで、私たちは「JavaScriptでクラスを付与する」という、DOMの肥大化を招く泥臭い回避策に頼らざるを得ませんでした。
しかし、`:has()` の登場によってその状況は一変しました。これは単なる「親セレクタ」の追加ではありません。ブラウザのレンダリングパイプラインに、双方向の依存関係を持ち込むことを意味します。この「強力な武器」を、我々上級エンジニアがいかにして安全かつ高パフォーマンスに運用すべきか。現場の深淵から解説します。
1. `:has()` が引き起こす「再計算」のコストを理解する
まず、技術者として認識しておくべきは、`:has()` が持つ「インバリデーション(無効化)のコスト」です。
通常、CSSはDOMツリーの上から下へ、あるいは隣接する兄弟要素へと適用されます。しかし、`:has()` は「子要素の状態が変化したとき、その親の再計算をトリガーする」という逆流を生み出します。
- レンダリング負荷の罠: 大規模なコンポーネントツリーのルート近くで `:has()` を多用すると、子要素のわずかな属性変化が、親コンポーネント全体、さらにはドキュメント全体の再レイアウト(Reflow)を誘発する可能性があります。
- メモリ効率: ブラウザエンジン(BlinkやWebKit)は、`:has()` の使用箇所を監視し、依存する子要素に変更があったかを追跡し続けます。複雑なネスト構造で多用すれば、この追跡のためのメモリ消費は無視できないレベルに達します。
2. 実践的アーキテクチャ:状態管理の局所化
では、どうすればこのコストを最小限に抑えられるのか。鉄則は「スコープの局所化」です。
/ ❌ 避けるべき実装:ルートで広範囲な監視を行う /
body:has(.is-loading) { / ページ全体を監視対象にするのは重い / }
/ ✅ 推奨される実装:コンポーネント単位での局所化 /
.card:has(.card-image–error) {
/ .cardというコンポーネントのスコープ内に限定する /
border: 2px solid var(–color-error);
padding: 1rem;
}
このように、`:has()` を特定のコンポーネント境界内に閉じ込めることで、ブラウザが監視すべきDOMの範囲(サブツリー)を限定でき、パフォーマンスの劣化を最小限に抑えることが可能です。
3. 非同期読み込みと「FOUT」への対抗策
非同期で動的にDOMが挿入されるモダンなWebアプリケーションにおいて、`:has()` は非同期の競合を解決する強力なツールにもなります。例えば、アイコンの有無でレイアウトを調整する場合などです。
/ アイコンがある時だけ隙間を空ける、というUIロジック /
.button {
display: flex;
gap: 0;
transition: gap 0.2s ease;
}
/ アイコンが存在する子要素を持つ親のみにスタイルを適用 /
.button:has(svg) {
gap: 0.5rem;
}
ここで重要なのは、「JavaScriptで制御していたCSS的なロジックを、CSSのレイヤーに回帰させる」ことによる、レンダリングの同期性です。JSによるクラス付与はイベントループの次のティックを待つ必要がありますが、CSSで記述すればブラウザのペイントサイクル内で完結するため、視覚的なチラつき(Layout Shift)を劇的に減らすことができます。
4. 陥りやすい「無限ループ」とバグの回避策
`:has()` を使用する際、最も注意すべきは「セレクタ間の循環参照」です。
/ 危険:無限ループを誘発する可能性がある /
/ 親がスタイルを変えると子が変わり、子がまた親のhas条件に影響を与える /
.container:has(.child–active) {
padding: 20px;
}
.child–active {
margin-top: 20px; / これが親のサイズを変えてしまい、再度hasが評価される… /
}
ブラウザ側でガードレールはあるものの、複雑な依存関係はデバッグを困難にします。「`:has()` はあくまで『見た目の反映』に徹する」という原則を守り、DOM構造自体を大きく変えるようなプロパティ(`display: none` や `position: absolute` 等)を `:has()` の条件に関連付ける場合は、慎重な検証が必要です。
結論:プロフェッショナルの矜持として
`:has()` は、私たちが長い間求めていた「宣言的なUI制御」の頂点です。しかし、強力な力には相応の責任が伴います。
- コンポーネントの責務を汚さないこと
- 監視範囲を極小化すること
- JavaScriptの代替としてではなく、CSSの表現力の拡張として扱うこと
これらを守れば、あなたのアプリケーションはより堅牢に、より直感的になるはずです。フレームワークに依存しすぎていた「クラスの付け替え」という苦行から解放され、CSS本来の力でエレガントなUIを構築する。それこそが、現代のフロントエンド・エンジニアが目指すべき地平線です。
さあ、コードを開き、不要なJSのクラス制御を削除する準備を始めましょう。その場所には、よりシンプルで美しいCSSが待っています。

コメント