Alt Duruma Dayalı Stil

Web özellikleri: :has() :not()

Geçmişte CSS seçiciler yalnızca aşağı doğru ilerleyebiliyordu. Bir alt öğeyi üst öğesine göre biçimlendirebiliyordunuz ancak üst öğeyi alt öğesine göre biçimlendiremiyordunuz. :has() sözde sınıfı bu durumu değiştirerek bir kapsayıcı öğeyi alt öğelerinin varlığına veya durumuna göre koşullu olarak biçimlendirmenize olanak tanır.

:has() öğesini durum sözde sınıflarıyla (ör. :checked, :focus, :valid, :invalid veya :not()) birleştirerek üst öğelerde "değiştirici" sınıfları (ör. .is-active veya .has-error) açıp kapatmak için JavaScript'e ihtiyaç duymadan tamamen CSS'de karmaşık, etkileşimli kullanıcı arayüzü bileşenleri oluşturabilirsiniz.

Bu, özellikle dahili etkileşimlere yanıt vermesi gereken bileşenler için kullanışlıdır. Örneğin, bir onay kutusuna (:checked) tepki veren yerelleştirilmiş bir tema açma/kapatma düğmesi, bir hatayı vurgulayan form grubu (:invalid) veya alt bağlantıya odaklanıldığında yükselen bir kart (:focus-within).

Duruma dayalı kapsayıcı stilini uygulama

ZORUNLU: Etkileşimli alt öğenin belirli durumunu (ör. :checked, :focus, :invalid) algılamak için kapsayıcı öğede :has() seçicisini kullanmanız gerekir.

Stilini bir alt öğenin durumuna göre değiştiren bir bileşen oluşturmak için:

  1. Varsayılan stili tanımlama: Temel durumunu tanımlamak için kapsayıcıda CSS değişkenleri ayarlayın.
  2. Duruma dayalı geçersiz kılmaları uygulama: :has([child-selector]:[state]) ile kapsayıcıyı hedefleyin ve etkin veya alternatif durum için CSS değişkenlerini yeniden tanımlayın.

Örnek: Alt açma/kapatma düğmesine göre temayı değiştiren bir bileşen.

/* 1. Define the default state on the component container */
.theme-card {
  /* Using custom properties makes state-switching cleaner */
  --card-bg: #ffffff;
  --card-text: #333333;
  --card-border: #cccccc;

  background-color: var(--card-bg);
  color: var(--card-text);
  border: 1px solid var(--card-border);

  /* Use a transition for smooth state changes */
  transition: background-color 0.3s, color 0.3s;
}

/* 2. Apply styles when the child enters the specific state */
/* MANDATORY: Target the container and use :has() to check the descendant's state */
.theme-card:has(.theme-toggle:checked) {
  /* Override the properties for the active or alternate state */
  --card-bg: #222222;
  --card-text: #f0f0f0;
  --card-border: #555555;
}
/* You can also combine :has() and :not() to target specific negative states */
/* This selects a card that DOES NOT have a toggle in the checked state */
.theme-card:not(:has(.theme-toggle:checked)) {
  /* Optional: Explicit styles for the unchecked state if needed */
}
<!-- The container element that receives the styling -->
<div class="theme-card">
  <!-- The child element whose state controls the parent -->
  <label>
    <input type="checkbox" class="theme-toggle">
    Enable Dark Mode
  </label>

  <h2>Card Title</h2>
  <p>The style of this entire card is controlled by the checkbox above.</p>
</div>

Performans ipucu: :has() kullanırken seçiciyi mümkün olan en spesifik kapsayıcıya (ör. .theme-card) göre ayarlayın. Stil değişiklikleri yerelleştirilmişse seçiciyi body:has(...) gibi çok üst düzey öğelere sabitlemekten kaçının. Geniş :has() sorguları, daha fazla düzen yeniden hesaplamasını tetikleyebilir.

Yedek stratejiler

Duruma dayalı stil, kullanıcı deneyimi veya sayfa düzeni için kritik öneme sahipse :has() seçiciyi desteklemeyen tarayıcılar için bir yedek sağlamanız gerekir. Yalnızca dekoratif efektler için :has(), yedek olmadan progresif geliştirme olarak kullanılabilir.

ZORUNLU: Kritik özellikler için yedek uygularken geleneksel sınıf tabanlı bir yedek tanımlamak için CSS'nizde @supports not selector(:has(*)) kullanmanız gerekir. Kritik durum değişikliği kullanıcı etkileşimine bağlıysa durum değişikliğini temsil eden eşdeğer JavaScript etkinliğine (ör. change, focus, blur) göre bu sınıfı açıp kapatmak için CSS.supports() ile küçük bir satır içi komut dosyası da kullanmanız gerekir.

/* 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 overrides */
  .theme-card.is-active {
    --card-bg: #222222;
    --card-text: #f0f0f0;
    --card-border: #555555;
  }
}
/* Fallback JavaScript for older browsers */
/* Check for support before running the script to avoid unnecessary work in modern browsers */
if (!CSS.supports('selector(:has(*))')) {
  const toggle = document.querySelector('.theme-toggle');
  const card = document.querySelector('.theme-card');

  if (toggle && card) {
    // Manually toggle the fallback class when the input state changes
    toggle.addEventListener('change', (e) => {
      card.classList.toggle('is-active', e.target.checked);
    });
  }
}