Penataan Gaya Berbasis Konten

Fitur web: :has() :not()

Secara historis, menerapkan tata letak yang berbeda ke komponen berdasarkan kontennya memerlukan JavaScript atau logika kondisional dalam bahasa template HTML untuk memasukkan class pengubah (seperti .card--has-image atau .card--text-only).

Class pseudo :has() menghilangkan kebutuhan ini dengan bertindak sebagai pemilih induk. Dengan pemilih ini, Anda dapat menata gaya elemen penampung secara bersyarat berdasarkan ada atau tidaknya elemen turunan tertentu.

Dengan menggunakan :has(), Anda dapat dengan mudah menentukan variasi tata letak yang berbeda sepenuhnya dalam CSS berdasarkan konten DOM aktual komponen. Anda juga dapat menggabungkannya dengan :not() untuk secara eksplisit menargetkan ketiadaan konten guna menentukan tata letak default.

Menerapkan gaya visual penampung berbasis konten

WAJIB: Anda harus menggunakan pemilih :has() pada elemen penampung untuk mendeteksi keberadaan konten turunan tertentu.

Untuk membuat komponen yang mengubah tata letaknya berdasarkan kontennya:

  1. Tentukan gaya default: Terapkan gaya tata letak dasar ke elemen penampung (misalnya, tumpukan satu kolom sederhana).
  2. Terapkan penggantian berbasis konten: Targetkan penampung dengan :has([child-selector]) dan terapkan gaya tata letak baru saat konten tersebut ada (misalnya, petak multi-kolom).

Contoh: Komponen kartu yang beralih ke tata letak berdampingan jika ada gambar.

/* 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>

Tips performa: Saat menggunakan :has(), cakupkan pemilih ke penampung komponen yang paling spesifik (seperti .article-card). Hindari menautkannya ke elemen tingkat sangat tinggi seperti body:has(img) jika perubahan gaya sangat terlokalisasi, karena kueri :has() yang luas dapat memicu lebih banyak penghitungan ulang tata letak.

Strategi penggantian

Jika gaya tata letak berbasis konten sangat penting untuk pengalaman pengguna atau desain halaman, Anda harus memberikan penggantian untuk browser yang tidak mendukung pemilih :has(). Untuk efek dekoratif murni, :has() dapat digunakan sebagai progressive enhancement tanpa penggantian.

WAJIB: Saat menerapkan penggantian untuk tata letak penting, Anda harus menggunakan @supports not selector(:has(*)) di CSS untuk menentukan penggantian berbasis class tradisional (misalnya, .has-image).

Tidak seperti gaya visual berbasis status interaktif, keberadaan konten biasanya diketahui pada waktu render. Penggantian yang paling andal adalah dengan meminta mesin template sisi server (atau generator situs statis) memasukkan class seperti .has-image ke penampung jika elemen turunan (seperti gambar) ada dalam data.

Jika rendering sisi server tidak memungkinkan, Anda harus menggunakan skrip kecil dengan CSS.supports() untuk mendeteksi konten dan menambahkan class saat pemuatan atau setelah injeksi konten dinamis.

/* 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');
    }
  });
}