はじめに
:has()疑似クラスは、子要素の存在や状態に基づいて親要素をスタイリングできる画期的な機能です。
従来のCSSセレクタは親から子への一方向の選択しかできませんでしたが、:has()により逆方向の選択が可能になりました。
この機能は2024年以降、Chrome、Firefox、Safari、Edgeなどすべての主要ブラウザでサポートされています。
State of CSS 2025の調査では、:has()が最も使用され、最も愛されているCSS機能として1位に輝きました!
基本構文
/* 基本形 */
親要素:has(子要素セレクタ) {
  /* スタイル */
}
/* 例:画像を含むdivにスタイルを適用 */
div:has(img) {
  border: 2px solid blue;
  background-color: lightblue;
}
実用例
1. 親要素セレクタとして使用
:has()の最も基本的な使い方は、特定の子要素を持つ親要素のスタイリングです。
/* アイコンを含むボタンのスタイリング */
button:has(svg) {
  padding-left: 2.5rem;
}
/* サブメニューを持つナビゲーション項目にドロップダウンアイコンを追加 */
nav li:has(ul) > a::after {
  content: "+";
  margin-inline: 10px;
}
2. 条件付きレイアウトの調整
画像の有無によってカードレイアウトを動的に変更できます。
/* 画像を含むカードは2列分を占める */
.card-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
  gap: 1rem;
}
.card:has(img) {
  grid-column: span 2;
}
/* 画像を含まないカードは通常サイズ */
.card:not(:has(img)) {
  grid-column: span 1;
}
3. フォームバリデーションのスタイリング
無効な入力を含むフォーム要素全体のスタイルを変更できます。
/* 無効な入力を含むフィールドセット */
fieldset:has(:invalid) {
  border-color: red;
  background-color: #fff5f5;
}
fieldset:has(:invalid) legend {
  color: red;
  font-weight: bold;
}
/* 有効な入力を含むラベル */
label:has(:valid) {
  color: green;
}
4. 状態に応じたテーマ切り替え
ドキュメント全体の状態に基づいてテーマを変更できます。
/* ダークモードが選択されている場合 */
body:has(option[value="dark"]:checked) {
  --primary-color: #e43;
  --surface-color: #1b1b1b;
  --text-color: #eee;
}
/* モーダルが開いている時のスクロールロック */
body:has(dialog[open]) {
  overflow: hidden;
}
5. 兄弟要素の相互作用(疑似前方兄弟セレクタ)
CSSには次の兄弟要素を選択する方法はありますが、前の兄弟を選択する方法はありません。:has()を使えばこれが可能になります。
/* ホバーされていない兄弟要素を選択 */
.container:has(.card:hover) .card:not(:hover) {
  opacity: 0.5;
  filter: grayscale(100%);
}
/* 前の兄弟要素を選択 */
h2:has(+ p) {
  margin-bottom: 0.5rem;
}
6. 数量クエリ
要素の数に応じてスタイルを変更できます。
/* テーブルが5行以上ある場合、最大高さ制限を解除 */
.table-wrapper:has(tr:nth-child(5)) {
  max-height: none;
  overflow: visible;
}
/* リスト項目が10個以上の場合、2カラムレイアウトに */
ul:has(li:nth-child(10)) {
  columns: 2;
  column-gap: 2rem;
}
7. データ属性に基づくスタイリング
ネストされた要素のデータ属性に基づいて親要素をスタイリングできます。
/* カテゴリーに応じたカードの背景色 */
.card:has([data-category*="Dev" i]) {
  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
}
.card:has([data-category*="Design" i]) {
  background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);
}
.card:has([data-category*="Marketing" i]) {
  background: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%);
}
8. コンテンツの存在確認
空の状態を検出してスタイリングできます。
/* 空のカードにメッセージを表示 */
.card:not(:has(*))::before {
  content: "コンテンツがありません";
  color: #999;
  font-style: italic;
}
/* 画像を含まないfigure要素 */
figure:not(:has(img)) {
  border: 2px dashed #ccc;
  min-height: 200px;
  display: flex;
  align-items: center;
  justify-content: center;
}
9. インタラクティブコンポーネント
メガメニューの開閉でヘッダー全体の色を変更するなど、コンポーネント間の連携が可能です。
/* メガメニューが開いている時、ヘッダーの色を変更 */
header:has(nav[aria-expanded="true"]) {
  background-color: #1a1a1a;
  color: white;
}
/* アコーディオンが展開されている時、アイコンを回転 */
.accordion:has(.panel[aria-hidden="false"]) .icon {
  transform: rotate(180deg);
}
10. 複数条件の組み合わせ
:has()は複数の条件を組み合わせてAND/OR条件を実現できます。
/* OR条件:カンマ区切りで複数指定 */
article:has(img, video) {
  padding: 2rem;
}
/* AND条件:複数の:has()を連結 */
article:has(h2):has(img) {
  grid-column: span 2;
}
/* 複雑な条件 */
.post:has(img):has(.tag--featured):not(:has(.tag--draft)) {
  border: 3px solid gold;
}
パフォーマンスの考慮事項
:has()はとても便利ですが、パフォーマンスへの影響を考慮する必要があります。ブラウザはDOM変更時に:has()セレクタを再評価する必要があり、複雑なセレクタは計算コストが高くなります。
パフォーマンス最適化のベストプラクティス
/* ❌ 避けるべき:広範囲なアンカー要素 */
body:has(.sidebar) { /* ... */ }
*:has(.item) { /* ... */ }
/* ✅ 推奨:特定の要素にスコープを限定 */
.container:has(.sidebar) { /* ... */ }
.gallery:has(.item) { /* ... */ }
/* ❌ 避けるべき:制約のない内部セレクタ */
.ancestor:has(.foo) { /* すべての子孫を検索 */ }
/* ✅ 推奨:直接の子や隣接兄弟に制約 */
.ancestor:has(> .foo) { /* 直接の子のみ */ }
.ancestor:has(+ .sibling .foo) { /* 隣接兄弟の子孫 */ }
内部セレクタを特定のクラスや直接子セレクタで制約することで、不要な先祖探索を減らし、パフォーマンスを向上させます。
ブラウザサポート
:has()は以下のブラウザでサポートされています:
- Chrome: 105以降
 - Edge: 105以降
 - Firefox: 122以降
 - Safari: 15.4以降
 
2025年10月時点で、ブラウザ互換性スコアは82%に達しており、実用的に使用できる状況です。
まとめ
:has()疑似クラスはの主な利点としては、以下の3つが挙げられます。
- JavaScriptの削減: これまでJavaScriptで実装していた多くの機能をCSSだけで実現できます
 - コードの簡潔化: 親要素の状態管理が不要になり、よりシンプルなコードが書けます
 - 保守性の向上: スタイルとロジックの分離が進み、メンテナンスが容易になります
 
ただし、以下の点に注意が必要です。
- パフォーマンスへの影響を考慮し、セレクタを適切にスコープする
 - 複雑すぎる条件は避け、可読性を保つ
 - ブラウザサポートを確認し、必要に応じてフォールバックを用意する
 
:has()を使いすぎないよう注意し、特に大規模で動的なレイアウトでは慎重に使用してください。適切に使用すれば、よりクリーンで柔軟なCSSを書くことができます!