これまで、コンテンツに基づいてコンポーネントに異なるレイアウトを適用するには、JavaScript または HTML テンプレート言語の条件付きロジックを使用して、修飾子クラス(.card--has-image や .card--text-only など)を挿入する必要がありました。
:has() 疑似クラスは、親セレクタとして機能することで、この必要性を排除します。特定の子孫要素の有無に基づいて、コンテナ要素のスタイルを条件付きで設定できます。
:has() を使用すると、コンポーネントの実際の DOM コンテンツに基づいて、CSS でさまざまなレイアウト バリエーションを簡単に定義できます。必要に応じて :not() と組み合わせて、コンテンツの不在を明示的にターゲットに設定して、デフォルトのレイアウトを定義することもできます。
コンテンツベースのコンテナ スタイルを実装する
必須: コンテナ要素で :has() セレクタを使用して、特定の子コンテンツの存在を検出する必要があります。
コンテンツに基づいてレイアウトを変更するコンポーネントを構築するには:
- デフォルトのスタイルを定義する: 基本的なレイアウト スタイルをコンテナ要素に適用します(例: シンプルな 1 列のスタック)。
- コンテンツ ベースのオーバーライドを適用する:
:has([child-selector])を使用してコンテナをターゲットにし、そのコンテンツが存在する場合(複数列のグリッドなど)に新しいレイアウト スタイルを適用します。
例: 画像がある場合に横並びのレイアウトに切り替わるカード コンポーネント。
/* 1. Define the default state on the component container */
/* This applies when there is NO image */
.article-card {
display: flex;
flex-direction: column;
gap: 1rem;
padding: 1.5rem;
border: 1px solid #ccc;
border-radius: 8px;
}
/* 2. Apply styles when a specific child element is present */
/* MANDATORY: Target the container and use :has() to check for the descendant */
.article-card:has(img) {
/* Change the layout to a row if an image exists */
flex-direction: row;
align-items: center;
}
/* Optional: Style the image itself (no :has() needed here) */
.article-card img {
width: 150px;
height: auto;
border-radius: 4px;
}
/* You can also combine :has() and :not() to explicitly target the ABSENCE of content */
/* This selects a card that DOES NOT have an image */
.article-card:not(:has(img)) {
/* E.g., apply a different background color for text-only cards */
background-color: #f9f9f9;
}
<!-- Assume an <h1> precedes these components in the document layout -->
<!-- A card WITH an image (will use row layout) -->
<article class="article-card">
<img src="thumbnail.jpg" alt="Article thumbnail" />
<div class="content">
<h2>Card With Image</h2>
<p>This card lays out its content horizontally.</p>
</div>
</article>
<!-- A card WITHOUT an image (will use default column layout) -->
<article class="article-card">
<div class="content">
<h2>Text-Only Card</h2>
<p>This card lays out its content vertically, and gets its background color from the :not(:has()) rule.</p>
</div>
</article>
パフォーマンスのヒント: :has() を使用する場合は、セレクタのスコープを可能な限り具体的なコンポーネント コンテナ(.article-card など)に設定します。スタイルの変更が高度にローカライズされている場合は、body:has(img) などの非常に高レベルの要素にアンカーを設定しないでください。:has() クエリが広範囲に及ぶと、レイアウトの再計算が頻繁に発生する可能性があります。
代替戦略
コンテンツ ベースのレイアウト スタイルがユーザー エクスペリエンスやページデザインにとって重要な場合は、:has() セレクタをサポートしていないブラウザ用の代替手段を用意する必要があります。純粋に装飾的な効果を得るには、フォールバックなしのプログレッシブ エンハンスメントとして :has() を使用できます。
必須: 重要なレイアウトのフォールバックを実装する場合は、CSS で @supports not selector(:has(*)) を使用して、従来のクラスベースのフォールバック(.has-image など)を定義する必要があります。
インタラクティブな状態ベースのスタイル設定とは異なり、コンテンツの有無は通常、レンダリング時に判明します。最も堅牢なフォールバックは、子要素(画像など)がデータに存在する場合に、サーバーサイドのテンプレート エンジン(または静的サイト ジェネレータ)が .has-image などのクラスをコンテナに挿入することです。
サーバーサイド レンダリングが不可能な場合は、CSS.supports() を含む小さなスクリプトを使用してコンテンツを検出し、読み込み時または動的コンテンツの挿入後にクラスを追加する必要があります。
/* Fallback CSS for older browsers */
/* We check if the browser DOES NOT support the :has() selector */
@supports not selector(:has(*)) {
/* Define a traditional modifier class that applies the exact same layout overrides */
.article-card.has-image {
flex-direction: row;
align-items: center;
}
.article-card:not(.has-image) {
background-color: #f9f9f9;
}
}
/* Fallback JavaScript for older browsers (if not using SSR to add the class) */
/* Check for support before running the script to avoid unnecessary work in modern browsers */
if (!CSS.supports('selector(:has(*))')) {
// Find all components that need checking
const cards = document.querySelectorAll('.article-card');
cards.forEach(card => {
// If the critical content exists, manually add the fallback class
if (card.querySelector('img')) {
card.classList.add('has-image');
}
});
}