:has()擬似クラスを使いこなす!CSSだけで実現する新しいインタラクティブ表現とレイアウト術
Web開発の現場で、より複雑で動的なUIやレイアウトを構築しようとすると、すぐにJavaScriptに頼りがちになりますよね。しかし、CSSの世界に新たな革命が訪れました。それが、待望の:has()
擬似クラスです。
この強力な新機能は、特定の要素が子孫に特定の要素を持っているか、あるいは特定の要素が兄弟要素に特定の要素を持っているかといった条件に基づいて、スタイルを適用することを可能にします。これにより、これまでJavaScriptでしか実現できなかったようなインタラクティブな表現や、より柔軟なレイアウト調整が、純粋なCSSだけで実現できるようになりました。
本記事では、:has()
擬似クラスの基本的な使い方から、具体的なユースケース、そしてWebサイトの表現力とメンテナンス性を劇的に向上させるための応用テクニックまで、詳しく解説していきます。
モダンなWeb開発の最前線を学び、あなたのWebサイトを次のレベルへと引き上げましょう。
注意:
:has()
擬似クラスは比較的新しい機能です。主要ブラウザは対応済みですが、本番環境で利用する際は対象ユーザーのブラウザサポート状況を確認してください。
[参考] Can I useによるブラウザサポート状況
:has() CSS relational pseudo-class | Can I use… Support tables for HTML5, CSS3, etc

CSSの :has()擬似クラスとは?基本的な使い方と「親セレクタ」の概念
:has()
擬似クラスは、CSS Selectors Level 4で導入された擬似クラスで、「指定された要素内に引数で与えられたセレクタに一致する要素が存在する場合に、その指定された要素を選択する」という機能を持っています。
これまでのCSSでは、子要素や兄弟要素を選択することはできても、親要素や先行する兄弟要素を条件にスタイルを適用することは非常に困難でした。しかし、:has()
擬似クラスは、この常識を覆し、事実上の「親セレクタ」として機能する画期的な存在です。
つまり、:has()
擬似クラスは、引数で指定された「セレクタリスト」にマッチする要素を子孫に持つ要素を選択することができます。
もう少し具体的に説明すると、
「もし私(選択対象の要素)が、特定の何か(:has()の引数)を子孫に持っているなら、私自身(選択対象の要素)をスタイルする」
という条件を設定できるようになった、ということです。
基本文法
:has()
擬似クラスを使用するときの基本的な文法は以下の通りです。
CSS
/* p要素を子孫に持っているdiv要素のスタイルを定義 */ div:has(p) { /* スタイル */ } /* badgeクラスを持つ要素を子孫に持っているcardクラス要素のスタイルを定義 */ .card:has(.badge) { /* スタイル */ }
基本的な使用例
例えば、以下のようなHTMLがあるとします。
HTML
<div class="card-container"> <!-- ケース1:ステータスバッジがあるカード --> <div class="item-card"> <h3>新商品!素敵な靴</h3> <p>足元を彩る、履き心地抜群の新作スニーカーが登場しました。</p> <span class="status-badge">New</span> </div> <!-- ケース2:ステータスバッジがないカード --> <div class="item-card"> <h3>人気のバッグ</h3> <p>収納力とデザイン性を兼ね備えた、普段使いにぴったりのバッグです。</p> </div> </div>
子要素に.status-badge
を持つ親要素.item-card
だけに特別なスタイルを適用したい場合、:has()
擬似クラスを使って下のように書くことで実現できます。
CSS
.item-card:has(.status-badge) { padding-right: 60px; /* 右側のパディングを大きくして、バッジがコンテンツに重なるのを防ぐ */ background-color: #E1F0FC; /* 背景の色を変更 */ }
See the Pen
Untitled by saku (@web-saku)
on CodePen.
新しいインタラクティブ表現を実現する応用例
:has()
擬似クラスは、ユーザーとのインタラクションや動的な状態変化に応じたスタイリングに革命をもたらします。
ここからは具体的な使用例のアイデアをいくつか紹介していきます。
1. フォームの入力状態に応じたUI変化(バリデーション)
フォーム要素の有効/無効、入力済み/未入力などの状態に応じて、親要素や関連要素のスタイルを変更できます。
See the Pen
Untitled by saku (@web-saku)
on CodePen.
こちらの使用例では、:has()
擬似クラスと:user-invalid
擬似クラスを利用してフォームのバリデーションを行なっています。
記述例は以下の通りです。
HTML
<form> <div class="form-group"> <label for="username">ユーザー名:</label> <input type="text" id="username" name="username" required placeholder="ユーザー名を入力してください"> <span class="error-message">ユーザー名は必須です。</span> </div> <div class="form-group"> <label for="email">メールアドレス:</label> <input type="email" id="email" name="email" required placeholder="有効なメールアドレスを入力してください"> <span class="error-message">有効なメールアドレス形式で入力してください。</span> </div> <button type="submit">送信</button> </form>
CSS
/* --- 基本的なフォーム要素のスタイル --- */ body { font-family: sans-serif; background-color: #f7f7f7; padding: 20px; } .form-group { margin-bottom: 20px; padding: 15px; border: 1px solid #ddd; /* 通常時の枠線 */ border-radius: 8px; background-color: #ffffff; /* 通常時の背景色 */ transition: all 0.3s ease; /* スムーズな変化のためにトランジションを追加 */ } .form-group label { display: block; margin-bottom: 8px; font-weight: bold; color: #333; } .form-group input { width: 100%; padding: 10px 12px; border: 1px solid #ccc; /* 通常時の入力欄の枠線 */ border-radius: 5px; box-sizing: border-box; font-size: 16px; transition: border-color 0.3s ease; } /* エラーメッセージの初期設定(デフォルトでは非表示) */ .error-message { color: #e74c3c; /* エラーメッセージの色 */ font-size: 0.85em; margin-top: 8px; display: none; /* ここが重要:初期状態では非表示 */ } .form-group:has(input:user-invalid) { border-color: #e74c3c; /* 無効な入力がある場合の親の枠線を赤くする */ background-color: #fff0f0; /* 無効な入力がある場合の親の背景を薄い赤にする */ } /* 無効な入力欄がある場合の入力欄自体のスタイル */ .form-group:has(input:user-invalid) input { border-color: #e74c3c; /* 入力欄自体の枠線も赤くする */ } /* 無効な入力がある場合に、その中のエラーメッセージを表示 */ .form-group:has(input:user-invalid) .error-message { display: block; /* エラーメッセージを表示 */ }
2. 特定の要素の有無に応じたレイアウト調整
前述のカードの例のように、特定のコンテンツ(画像、ボタン、サブタイトルなど)があるかどうかで、コンポーネント全体のレイアウトやスタイルを変更できます。
See the Pen
Untitled by saku (@web-saku)
on CodePen.
こちらの使用例では、:has()
擬似クラスを利用して画像を含むカードだけに2カラムのレイアウトが適用されるようにしています。
記述例は以下の通りです。
HTML
<div class="container"> <!-- 画像があるカード1 (2カラムになる) --> <div class="card"> <img src="https://web-saku.net/wp-content/uploads/2025/06/cat-150a.jpg" alt="サンプル画像1"> <div class="card__body"> <h2>画像ありカードのタイトル1</h2> <p>このカードには画像が含まれているため、画像が左、テキストが右の2カラムレイアウトになります。テキストコンテンツがここに続きます。</p> </div> </div> <!-- 画像がないカード1 (1カラムになる) --> <div class="card"> <div class="card__body"> <h2>画像なしカードのタイトル1</h2> <p>このカードには画像が含まれていないため、シンプルな1カラムレイアウトになります。画像がない分、テキストが全幅を使います。</p> </div> </div> <!-- 画像があるカード2 (2カラムになる) --> <div class="card"> <img src="https://web-saku.net/wp-content/uploads/2025/06/cat-150b.jpg" alt="サンプル画像2"> <div class="card__body"> <h2>画像ありカードのタイトル2</h2> <p>こちらも画像を含むカードなので、画像とテキストで構成される2カラムレイアウトになります。別の画像を使用しています。</p> </div> </div> <!-- 画像がないカード2 (1カラムになる) --> <div class="card"> <div class="card__body"> <h2>画像なしカードのタイトル2</h2> <p>このカードも画像がないため、コンテンツが縦に並ぶ1カラムレイアウトになります。シンプルな情報の表示に適しています。</p> </div> </div> </div>
CSS
body { font-family: sans-serif; background-color: #f4f4f4; padding: 20px; } .container { max-width: 900px; margin: 0 auto; display: grid; /* カードを縦に並べるためのグリッドコンテナ */ gap: 20px; } /* カードの共通スタイル */ .card { border: 1px solid #ddd; background-color: #fff; border-radius: 8px; overflow: hidden; /* 画像がはみ出さないように */ box-shadow: 0 2px 5px rgba(0,0,0,0.1); padding: 15px; /* デフォルトのパディング */ } .card h2 { margin-top: 0; color: #333; } .card p { margin-bottom: 0; color: #666; } .card img { max-width: 100%; height: auto; display: block; /* 画像の下の余白をなくす */ border-radius: 4px; /* 角を丸める */ } /* .card が
要素 を子孫に持っている場合 */ .card:has(img) { display: flex; /* Flexboxコンテナにする */ align-items: flex-start; /* 上揃え */ gap: 20px; /* 画像とテキストの間の余白 */ padding: 0; /* Flexbox化するため、親のパディングは一度リセット */ } /* 画像があるカード内の画像スタイル */ .card:has(img) img { flex-shrink: 0; /* 画像は縮まない */ flex-basis: 150px; /* 画像の固定幅 */ width: 150px; /* IE/Edgeの古いFlexboxバグ対策 */ height: 150px; /* 高さを固定 */ object-fit: cover; /* 画像がはみ出す場合の表示方法 */ border-radius: 8px 0 0 8px; /* カード全体の角丸と合わせる */ } /* 画像があるカード内のテキストコンテンツのラッパー */ .card:has(img) .card__body { flex-grow: 1; /* 残りのスペースを全て埋める */ padding: 15px; /* テキストコンテンツのパディング */ }
:has()擬似クラスのメリットと考慮すべき点
:has()擬似クラスのメリット
:has()
擬似クラスを利用するメリットには以下のようなものがあります。
- JavaScriptの削減: 多くのインタラクティブな表現がCSSだけで実現できるようになり、JavaScriptのコード量を減らし、複雑性を軽減できます。
- パフォーマンス向上: JavaScriptの実行が減ることで、ページのロード時間やインタラクションのレスポンスが改善される可能性があります。
- 保守性の向上: HTML構造とCSSがより密接に連携し、DOMの変更がスタイルに直接反映されるため、コードの可読性と保守性が向上します。
- セマンティックなマークアップの維持: スタイル目的のためだけに無駄なクラスや要素を追加する必要がなくなり、よりセマンティックなHTML構造を保つことができます。
- 表現力の飛躍的な向上: これまで不可能だったCSS表現が可能になり、より洗練されたUI/UXをデザインできるようになります。
:has()擬似クラスのを使用する際に考慮すべき点
:has()
擬似クラスを使用する際には以下の点に注意してください。
- ブラウザサポート: 最新の機能であるため、古いブラウザでは動作しない可能性があります。Can I use…などで常に最新の対応状況を確認し、必要に応じてフォールバックを検討しましょう。
- パフォーマンス: 複雑な
:has()
擬似クラスを多用すると、ブラウザの再計算負荷が増え、パフォーマンスに影響を与える可能性もゼロではありません。一般的な使用においては問題ありませんが、注意は必要です。 - 学習コスト: 新しい概念であるため、慣れるまでに時間がかかるかもしれません。しかし、その強力さを考えれば投資する価値は十分にあります。
まとめ
CSSの:has()
擬似クラスは、Web開発の常識を塗り替える画期的な機能です。これまでJavaScriptの領分だった領域にCSSが踏み込むことで、よりシンプルで高性能、そしてメンテナンス性の高いWebサイト構築への道が開かれました。
この擬似クラスを習得し、適切に活用することで、あなたはWebサイトの表現力を飛躍的に向上させ、よりユーザーフレンドリーで魅力的な体験を提供できるようになるでしょう。ぜひ今日の開発から:has()
擬似クラスを試してみてください。
今後のCSSの進化にも引き続き注目し、モダンなWeb開発を楽しんでいきましょう!
[参考ページ]
:has() – CSS: カスケーディングスタイルシート | MDN
:user-invalid (:-moz-ui-invalid) – CSS: カスケーディングスタイルシート | MDN
関連記事
CSSの擬似要素を使って背景画像にフィルターをかける
CSSの擬似要素を使って背景画像にフィルターをかける方法を紹介します。
メディアクエリを使ってWebサイト内の文章の改行をコントロールする方法
CSSのメディアクエリを使って、デバイスのディスプレイ幅に応じて文章の改行をコントロールする方法について説明します。 HTMLのbrタグを使う方法と、CSSの擬似要素を使う方法の二つを紹介します。
コメントを残す