【CSSの革命】:has() 疑似クラスで実装する、特定の子要素を持つ親のスタイル切り替え

Webサイトのレイアウトを作っているとき、「この子要素(バッジや画像など)が含まれている時だけ、親要素(カード全体など)の枠線や背景色を変えたい!」と思ったことはありませんか?

CSSは基本的に「親から子」へとスタイルを適用していく言語であるため、少し前までは「子要素の状態を検知して親要素のスタイルを変える」という、いわゆる「親セレクタ」のような実装はCSS単体では不可能でした。

従来の「親要素切り替え」の苦労
昔は、特定の子要素がある時だけ親の見た目を変えたい場合、JavaScriptを使って「子要素の有無を判定し、親要素に特定のクラスを付与する」という処理を書くか、WordPressなどのバックエンド側(PHP)で複雑な条件分岐を使ってクラスを出し分けるしかありませんでした。

そんな長年の開発者の悩みを一発で解決するために登場したのが、モダンCSSの救世主 :has() 疑似クラス です!

今回は、カード型レイアウトでもよく使われる「特定のバッジがあるカードだけ、親要素のスタイルを自動でゴージャスにする」という実装例を通して、:has() の強力さと便利さをご紹介します!

デモ動作

See the Pen :has() by mikanbako (@mikanbako) on CodePen.

全画面でのデモ動作は以下のリンクからご確認下さい!(別タブで開かれます)

実装コードの紹介

【HTML】

<div class="l-container">
  <h2>特定の子要素(バッジ)がある時だけ親カードを変える</h2>
  
  <div class="grid">
    <div class="p-card">
      <span class="p-badge">NEW</span>
      <h3 class="p-card__title">注目の最新記事タイトル</h3>
      <p class="p-card__text">この記事には「NEW」バッジが含まれているため、親であるカード全体の枠線と影がCSSだけで自動的に変化します。</p>
    </div>

    <div class="p-card">
      <h3 class="p-card__title">通常の記事タイトル</h3>
      <p class="p-card__text">ここにはバッジが含まれていません。そのため、通常の控えめなカードスタイルのまま表示されます。</p>
    </div>
  </div>
</div>

【CSS】

/* 通常のカードスタイル */
.p-card {
  position: relative;
  background-color: #fff;
  border: 1px solid #e0e0e0; /* 通常は薄いグレーの枠線 */
  border-radius: 12px;
  padding: 24px;
  box-shadow: 0 2px 4px rgba(0,0,0,0.04);
}

/* バッジのスタイル */
.p-badge {
  display: inline-block;
  background-color: #ff5a5a;
  color: #fff;
  font-size: 12px;
  font-weight: bold;
  padding: 4px 8px;
  border-radius: 4px;
  margin-bottom: 12px;
}

.p-card__title {
  font-size: 18px;
  margin: 0 0 8px 0;
}

.p-card__text {
  font-size: 14px;
  color: #666;
  margin: 0;
  line-height: 1.6;
}

/* =========================================
   ここからが今回の主役「:has()」
========================================= */
/* 「.p-badge」という子要素を持っている「.p-card」だけにスタイルを適用 */
.p-card:has(.p-badge) {
  border: 2px solid #1bb4d4; /* 枠線をメインカラーにして太く! */
  box-shadow: 0 8px 20px rgba(27, 180, 212, 0.15); /* 華やかな影をつける */
  transform: translateY(-4px); /* ちょっと上に浮かせる */
}

コードの解説・こだわったポイント

.p-card:has(.p-badge) の仕組み

今回のコードの肝は、CSSの最後に記述した以下のセレクタです。

.p-card:has(.p-badge) { ... }

これは、「内部に .p-badge というクラスを持つ .p-card 要素」 という指定になります。 条件に当てはまる親要素だけをピンポイントでスタイルを適用できるため、HTML側には切り替え用の特別なクラス(is-featured など)をわざわざ手動で付与する必要がありません。

JavaScriptやサーバーサイドの処理を劇的に削減

もしこの実装を :has() を使わずにやろうとした場合、以下のようなアプローチが必要でした。

  • JavaScript: ページ読み込み時にすべてのカードをループで回し、内部にバッジがあるか querySelector で確認して、あったら親に classList.add('is-active') する。
  • WordPress (PHP): 記事のループ処理の中で「もし特定の条件(カスタムフィールドやタグなど)を満たしていたら、クラス名を出力する」というPHPのif文を書く。

:has() を使えば、こうした「スタイル(見た目)のためだけに用意していたJSやPHPのロジック」をすべて排除し、デザインの制御を完全にCSSだけに一任できるようになります。コード全体の設計が圧倒的にシンプルになります。

補足:参考リンク(ブラウザの対応状況など)

:has() 疑似クラスは、現在すべての主要なモダンブラウザ(Chrome、Safari、Firefox、Edge)で完全にサポートされており、使用できる環境が整っています。

・MDN: :has() 疑似クラス
・Can I Use: :has

まとめ

今回は、特定の子要素を検知して親要素のスタイルを柔軟に切り替えるモダンCSSの強力な機能、:has() 疑似クラスをご紹介しました。

かつては「CSSだけでは不可能」と言われ、JavaScriptを駆使して力技で実装していた「親セレクタ」の概念が、今はこれだけ短いCSSコードで完結するようになったのは、まさにフロントエンドの進化の結晶だと感じます。

「条件に応じた見た目の変化は、できる限りCSSのレイヤーで解決する」

このアプローチを意識することで、無駄なスクリプトの実行を減らし、サイトのパフォーマンス向上やバグの削減にも繋がります。 これからも、最新のCSS仕様を把握し、シンプルでメンテナンス性の高い、洗練されたコーディングができるエンジニアを目指していきます!

最後まで読んでいただき、ありがとうございました!

Prev
Next