@sedatdemirAra

CSS if() ve style queries ile koşullu stil

Bir bileşenin "variant" prop'una göre farklı renkler uygulamak için bugüne kadar ya CSS sınıflarını JavaScript ile değiştirdik ya da CSS custom property'lerini set edip her değer için ayrı bir kural yazdık. Tekrar eden kod fazlaydı, bakımı zordu. CSS'in kendi içinde "eğer bu değişken şuysa, şu değeri uygula" diyebileceğimiz bir mekanizma hep eksikti. Container style queries ve CSS if() fonksiyonu bu boşluğu doldurmak için geliyor.

Container style queries nedir?

Container queries denince akla genellikle boyut sorguları (container-type: inline-size) gelir. Ama spesifikasyonun bir de style queries tarafı var: bir container'ın computed custom property değerine göre alt elemanları stillendirmek.

Sözdizimi şöyle:

.card {
  container-name: card;
  --variant: default;
}

@container card style(--variant: danger) {
  .card-title {
    color: var(--red-600);
  }
  .card-border {
    border-color: var(--red-400);
  }
}

@container card style(--variant: success) {
  .card-title {
    color: var(--green-600);
  }
  .card-border {
    border-color: var(--green-400);
  }
}

Burada container-type belirtmeye gerek yok. Style queries custom property sorgularında eleman zaten otomatik olarak style container sayılır. Boyut sorguları için container-type gerekir ama style queries için gerekmez; bu fark sık karıştırılıyor.

Nasıl çalışıyor?

Tarayıcı, @container style(--variant: danger) gördüğünde en yakın ancestor container'ın --variant değerine bakar. Eğer computed value danger string'ine eşitse, blok içindeki kurallar uygulanır. Burada karşılaştırma custom property'nin ham token değeri üzerinden yapılır; calc() sonucu ya da resolved renk değeri üzerinden değil.

Bu, pratikte şu anlama gelir: sadece custom property değerleri sorgulanabilir. style(color: red) gibi standart property sorguları henüz hiçbir tarayıcıda desteklenmiyor ve spesifikasyon taslağında da Level 2'ye ertelenmiş durumda.

CSS if() fonksiyonu

Style queries @container bloğu açıp içine kurallar yazmayı gerektiriyor. Peki tek bir property değerini koşullu belirlemek istesek? CSS if() fonksiyonu tam bunu yapıyor.

CSS Values and Units Level 5 taslağında tanımlanan if() fonksiyonunun sözdizimi:

.badge {
  background-color: if(
    style(--variant: danger): var(--red-500),
    style(--variant: warning): var(--yellow-500),
    else: var(--gray-200)
  );
}

Her koşul bir style(), media() veya supports() fonksiyonu olabilir. Virgülle ayrılmış dallardan ilk eşleşen kazanır, hiçbiri eşleşmezse else dalı devreye girer.

if() ile media() ve supports() birleşimi

if() sadece style queries ile sınırlı değil. Media query ve feature query de koşul olarak kullanılabiliyor:

.sidebar {
  width: if(
    media(width >= 1024px): 280px,
    media(width >= 768px): 220px,
    else: 100%
  );
}

Bu, bugün ayrı @media blokları açarak yazdığımız responsive değerleri tek bir satırda ifade etmemizi sağlıyor. Ama dikkat: if() henüz hiçbir tarayıcıda stabil sürümde yok (Haziran 2025 itibarıyla). Chrome Canary'de #enable-experimental-web-platform-features flag'i ile denenebilir.

Style queries ve if() farkı

İkisi birbirinin alternatifi değil, birbirini tamamlıyor. Farkları şöyle düşünmek daha doğru:

@container style(...) bir kural bloğu açar. İçine istediğiniz kadar property koyarsınız. Birden fazla elemanı tek seferde etkileyebilirsiniz.

if() ise tek bir property'nin değerini belirler. İnline düzeyde koşul yazmak için idealdir. Bir elemanın sadece color değerini koşullu yapmak istiyorsanız @container bloğu açmak gereksiz ağırlık yaratır.

Gerçek dünya örneği: tema değişkeni ile bileşen varyantları

Bir buton bileşenini düşünelim. Bugün genellikle .btn-primary, .btn-danger, .btn-ghost gibi sınıflar tanımlarız. Style queries ile bunu custom property üzerinden yapabiliriz:

/* Buton container */
.btn {
  --btn-variant: primary;
  padding: 0.5rem 1rem;
  border-radius: 0.375rem;
  border: 1px solid transparent;
  cursor: pointer;
  font-weight: 500;
}

/* Varyant sorguları */
@container style(--btn-variant: primary) {
  .btn-inner {
    background-color: #2563eb;
    color: #fff;
  }
}

@container style(--btn-variant: danger) {
  .btn-inner {
    background-color: #dc2626;
    color: #fff;
  }
}

@container style(--btn-variant: ghost) {
  .btn-inner {
    background-color: transparent;
    color: #374151;
    border-color: #d1d5db;
  }
}

HTML tarafı:

<button class="btn" style="--btn-variant: danger">
  <span class="btn-inner">Sil</span>
</button>

Burada --btn-variant değerini inline style ile, bir CSS sınıfı ile veya JavaScript ile set edebilirsiniz. Hangi yoldan gelirse gelsin, style query eşleşir.

Bu yaklaşımın sınıf tabanlı sisteme göre bir avantajı var: custom property inheritance sayesinde bir parent container'daki değer tüm alt bileşenleri etkiler. Bir section elementine --btn-variant: danger koyduğunuzda içindeki tüm butonlar otomatik olarak danger varyantına geçer.

Tarayıcı desteği tablosu

Haziran 2025 itibarıyla durum şöyle:

Özellik Chrome Firefox Safari
@container style() (custom property) 111+ 131+ 18+
@container style() (standart property) Yok Yok Yok
CSS if() Canary (flag) Nightly (flag) TP (flag)

Custom property üzerinden style queries artık üç büyük tarayıcıda da stabil. Bu, production'da kullanılabilir demek. if() ise en erken 2025 sonunda stabil kanallara gelebilir.

@supports ile progressive enhancement

Style queries'i bugün kullanmak istiyorsanız ama eski tarayıcıları da desteklemeniz gerekiyorsa:

/* Fallback: sınıf tabanlı */
.btn.danger .btn-inner {
  background-color: #dc2626;
  color: #fff;
}

/* Style query destekleniyorsa */
@supports (container-name: test) {
  @container style(--btn-variant: danger) {
    .btn-inner {
      background-color: #dc2626;
      color: #fff;
    }
  }
}

Bu pattern biraz tekrar yaratır ama geçiş döneminde güvenli bir yol. @supports kontrolü ile container-name property'sinin tanınıp tanınmadığını sorguluyoruz. Style queries desteği olan tüm tarayıcılar container-name'i de tanır.

JavaScript framework'leri ile etkileşim

React, Vue veya Svelte gibi framework'lerde CSS sınıfı yerine custom property set etmek kolay:

function Button({ variant = "primary", children }: {
  variant?: "primary" | "danger" | "ghost";
  children: React.ReactNode;
}) {
  return (
    <button
      className="btn"
      style={{ "--btn-variant": variant } as React.CSSProperties}
    >
      <span className="btn-inner">{children}</span>
    </button>
  );
}

CSS tarafındaki style queries hiç değişmez. Framework sadece custom property'yi set eder, stil mantığı tamamen CSS'te kalır. Bu ayrım test edilebilirlik açısından da iyi: CSS'i framework'den bağımsız test edebilirsiniz.

Bu yaklaşım CSS anchor positioning ile tooltip ve popover yazısında ele aldığım CSS-first düşünce biçimiyle de uyumlu. Stil mantığını mümkün olduğunca CSS'te tutmak, JavaScript bundle boyutunu küçültür.

Sınırlamalar ve dikkat edilecekler

Style queries'in birkaç sınırlaması var, bunları bilmeden kullanmak hayal kırıklığı yaratır.

Birincisi: sadece custom property değerleri sorgulanabilir. style(display: flex) veya style(color: red) çalışmaz. Bu, CSS Containment Level 2 spesifikasyonunda açıkça belirtilmiş: standart property sorguları gelecek seviyeye bırakılmış.

İkincisi: karşılaştırma token bazlıdır. --size: 16px ile --size: 1rem farklı değerlerdir, tarayıcı bunları resolve edip karşılaştırmaz. Bu yüzden custom property değerlerinde tutarlı birimler kullanmak gerekir.

Üçüncüsü: if() fonksiyonu nested kullanılamıyor (en azından mevcut taslakta). Yani if(style(...): if(media(...): ...)) geçerli değil. Karmaşık koşullar için @container blokları hâlâ gerekli.

Dördüncüsü: performans. Style queries layout invalidation'a neden olmaz (boyut sorguları neden olabilir) ama çok sayıda iç içe container'da çok sayıda style query yazmak style recalculation maliyetini artırabilir. Yüzlerce bileşenin olduğu bir listede bunu gözlemlemek mümkün, ancak normal kullanımda fark edilir bir sorun oluşturmaz.

if() ne zaman gelecek?

CSS if() fonksiyonu W3C CSS Working Group'un aktif çalışma alanlarından biri. Chrome'da Intent to Ship henüz gönderilmedi (Haziran 2025), ama Canary'deki implementasyon oldukça stabil. Firefox Nightly ve Safari Technology Preview'da da deneysel destek var. Üç tarayıcının da eş zamanlı çalışması, spesifikasyonun hızlı ilerleyeceğine işaret ediyor.

Bu arada if() gelmeden de style queries tek başına güçlü bir araç. Bugün production'da kullanılabilir ve Popover API ile kütüphanesiz overlay ya da scroll-driven animations gibi diğer modern CSS özellikleriyle birleştiğinde JavaScript bağımlılığını ciddi ölçüde azaltır.

Bileşen varyantlarını sınıf listeleri ile yönetmek hâlâ geçerli bir yöntem, ama custom property + style queries kombinasyonu daha az CSS kodu, daha kolay inheritance ve framework'den bağımsız stil mantığı sunuyor. Projenizde Tailwind ya da CSS Modules kullanıyor olsanız bile, varyant mantığını custom property'lere taşımayı denemeye değer. if() stabil olduğunda bu geçiş çok daha doğal hissedecek.