Tailwind'de bir varyant, sınıfın önüne yazılan ve onu bir koşula bağlayan ön ektir: hover:, md:, dark: birer varyanttır. Hazır listede olmayan bir koşul için iki yolunuz var. Tek seferlik durumlarda köşeli parantezle keyfi varyant yazarsınız ([&.is-dragging]:cursor-grabbing); tekrar eden durumlarda ise @custom-variant ile varyantı CSS dosyanızda kalıcı olarak tanımlarsınız (@custom-variant gece (&:where([data-tema="gece"] *));). Kendi CSS kurallarınızın içinde bir varyantı uygulamak için de @variant direktifi vardır.

Bu yazıda bu üç aracı, data-*, aria-* ve supports-* varyantlarını, *: ve **: çocuk varyantlarını ve varyant yığma sırasını v4.3.5 ile derlenmiş çıktılarla anlatıyoruz. Hazır durum varyantları (hover, focus-visible, disabled) hover ve focus durumları yazısında, group ve peer group ve peer kullanımı yazısında, serinin geri kalanı Tailwind öğrenme rehberinde.

@custom-variant: kısa yazım

@custom-variant: kısa yazım

Kısa yazımda varyant adından sonra parantez içinde bir seçici verirsiniz; & işareti sınıfın uygulandığı öğeyi temsil eder. Aşağıdaki gece varyantı, data-tema="gece" özniteliği taşıyan bir atanın içindeki öğelere uygulanır. Ayrıca medya sorgusu içeren bir varyantı da blok yazımla tanımladık:

css
@import "tailwindcss" source(none);
@source inline("gece:bg-black gece:text-white any-hover:underline md:gece:p-8");
@custom-variant gece (&:where([data-tema="gece"] *));
@custom-variant any-hover {
  @media (any-hover: hover) {
    &:hover {
      @slot;
    }
  }
}

v4.3.5 ile derlenen CSS:

css
@layer utilities {
  .gece\:bg-black:where([data-tema="gece"] *) {
    background-color: var(--color-black);
  }
  .gece\:text-white:where([data-tema="gece"] *) {
    color: var(--color-white);
  }
  @media (width >= 48rem) {
    .md\:gece\:p-8:where([data-tema="gece"] *) {
      padding: calc(var(--spacing) * 8);
    }
  }
  @media (any-hover: hover) {
    .any-hover\:underline:hover {
      text-decoration-line: underline;
    }
  }
}

Kısa yazımda & yerine sınıf seçicisi konmuş ve parantezdeki koşul ona eklenmiş: .gece\:bg-black:where([data-tema="gece"] *). Yeni varyant hazır varyantlarla da yığılabiliyor; md:gece:p-8 hem medya sorgusunu hem öznitelik koşulunu aldı. HTML tarafında tek yapmanız gereken, kök öğeye data-tema="gece" eklemek. Koyu tema için aynı mekanizmayla dark varyantını yeniden tanımlamak dark mode kullanımı yazısının konusu.

Blok yazım ve @slot

Koşul birden çok katmandan oluşuyorsa (bir medya sorgusunun içinde bir sözde sınıf gibi) blok yazımı kullanırsınız. @slot satırı, utility'nin bildirimlerinin yerleşeceği yeri gösterir. any-hover örneğinde çıktı tam olarak bu iç içe yapıyı izledi: önce @media (any-hover: hover), içinde :hover. Belgedeki supports-grid örneği de aynı biçimde bir @supports (display: grid) bloğunu @slot ile sarar. Medya ya da destek sorgusu gibi at-rule'larda & yazmanız gerekmez.

@variant: kendi CSS'inizde varyant uygulamak

@custom-variant yeni bir varyant tanımlar; @variant ise var olan bir varyantı sınıf yazmadan, kendi kuralınızın içinde kullanmanızı sağlar. Belge iki ek biçim gösterir: iki nokta ile yığma (@variant hover:focus) ve virgülle birden çok varyanta aynı stili verme (@variant hover, focus). Üçünü birlikte derledik:

css
@import "tailwindcss" source(none);
.kutu {
  background: white;
  @variant dark {
    background: black;
  }
  @variant hover:focus {
    outline: 2px solid red;
  }
  @variant hover, focus {
    color: navy;
  }
}
css
.kutu {
  background: white;
  @media (prefers-color-scheme: dark) {
    background: black;
  }
  &:hover {
    @media (hover: hover) {
      &:focus {
        outline: 2px solid red;
      }
    }
  }
  &:hover {
    @media (hover: hover) {
      color: navy;
    }
  }
  &:focus {
    color: navy;
  }
}

v4.3.5 çıktısı her biçimin farkını açıkça gösteriyor. dark varsayılan olarak prefers-color-scheme medya sorgusuna çevrildi. hover:focus iki koşulu iç içe birleştirdi: kural yalnızca öğe hem üzerine gelinmiş hem odaklanmışken geçerli. hover, focus ise iki ayrı blok üretti: koşullardan herhangi biri yeterli. Bu ayrım, bir odak stilinin klavyeyle gezinirken neden görünmediğini araştırırken önemlidir; erişilebilir odak halkaları için erişilebilirlik ve focus-visible yazısına bakın.

Tanımlanmamış bir varyantı @variant ile kullanmak derlemeyi durdurur. @custom-variant satırını yazmadan @variant gece { ... } kullandığımızda mesaj şuydu:

bash
Error: Cannot use `@variant` with unknown variant: gece

Keyfi varyantlar: köşeli parantezli seçiciler

Bir koşula yalnızca bir iki yerde ihtiyacınız varsa varyantı tanımlamadan köşeli parantez içinde yazabilirsiniz. & yine öğenin kendisidir ve alt çizgi seçicide boşluğa dönüşür. Medya ya da destek sorgusu gibi at-rule'lar da köşeli parantez içinde çalışır:

css
@import "tailwindcss" source(none);
@source inline("[&.is-dragging]:cursor-grabbing [&_p]:mt-4 [@supports(display:grid)]:grid lg:[&:nth-child(-n+3)]:hover:underline [&>*]:p-2");
css
@layer utilities {
  .\[\&_p\]\:mt-4 p {
    margin-top: calc(var(--spacing) * 4);
  }
  .\[\&\.is-dragging\]\:cursor-grabbing.is-dragging {
    cursor: grabbing;
  }
  @media (width >= 64rem) {
    @media (hover: hover) {
      .lg\:\[\&\:nth-child\(-n\+3\)\]\:hover\:underline:nth-child(-n+3):hover {
        text-decoration-line: underline;
      }
    }
  }
  .\[\&\>\*\]\:p-2 > * {
    padding: calc(var(--spacing) * 2);
  }
  @supports (display:grid) {
    .\[\@supports\(display\:grid\)\]\:grid {
      display: grid;
    }
  }
}

Seçicilerdeki ters bölü işaretleri, sınıf adındaki özel karakterlerin CSS'te kaçışlanmış hâlidir; sınıfı HTML'de yazdığınız gibi kullanırsınız. [&_p]:mt-4 öğenin içindeki bütün p öğelerini, [&>*]:p-2 yalnızca doğrudan çocukları hedefliyor. Uzun keyfi varyantları birden çok yerde tekrarladığınızı fark ederseniz, belgenin önerisi onları @custom-variant ile adlandırmaktır. Keyfi değerlerdeki boşluk ve alt çizgi kuralları arbitrary values yazısında.

data-* ve aria-* varyantları

data-* ve aria-* varyantları

data-* varyantı bir veri özniteliğinin varlığına, köşeli parantezli biçimi ise belirli bir değerine bakar. aria-* varyantlarında belge aria-busy, aria-checked, aria-disabled, aria-expanded, aria-hidden, aria-pressed, aria-readonly, aria-required ve aria-selected için hazır karşılık verir; bunlar özniteliğin "true" olmasını kontrol eder. Diğer ARIA değerleri için köşeli parantez kullanılır:

html
<div data-active class="border border-gray-300 data-active:border-purple-500">Seçili</div>
<div data-boyut="buyuk" class="data-[boyut=buyuk]:p-8">Büyük kutu</div>
<button aria-expanded="true" class="aria-expanded:bg-sky-700">Menü</button>
<th aria-sort="ascending" class="group aria-[sort=ascending]:font-bold">
  Fatura no
  <svg class="group-aria-[sort=descending]:rotate-180"></svg>
</th>
css
@layer utilities {
  /* (kısaltıldı: border ve border-gray-300) */
  :is(:where(.group)[aria-sort="descending"] .group-aria-\[sort\=descending\]\:rotate-180) {
    rotate: 180deg;
  }
  .aria-expanded\:bg-sky-700[aria-expanded="true"] {
    background-color: var(--color-sky-700);
  }
  .aria-\[sort\=ascending\]\:font-bold[aria-sort="ascending"] {
    --tw-font-weight: var(--font-weight-bold);
    font-weight: var(--font-weight-bold);
  }
  .data-active\:border-purple-500[data-active] {
    border-color: var(--color-purple-500);
  }
  .data-\[boyut\=buyuk\]\:p-8[data-boyut="buyuk"] {
    padding: calc(var(--spacing) * 8);
  }
}

Çıktı, her varyantın bir öznitelik seçicisine dönüştüğünü gösteriyor: data-active: yalnızca [data-active] varlığına, aria-expanded: ise [aria-expanded="true"] değerine bakıyor. group-aria-[sort=descending]: ebeveyndeki ARIA durumuna göre simgeyi döndürüyor. Bu varyantlar, durumu JavaScript ile sınıf ekleyip çıkararak değil, zaten erişilebilirlik için güncellediğiniz özniteliklerle yönetmenizi sağlar.

Kendi data ve aria varyantlarınız

Sık kullandığınız bir öznitelik koşulunu kısa bir ada bağlayabilirsiniz. Belgedeki iki örneği, biri Türkçe adla, birlikte derledik:

css
@import "tailwindcss" source(none);
@source inline("data-checked:underline sirali-artan:font-bold");
@custom-variant data-checked (&[data-ui~="checked"]);
@custom-variant sirali-artan (&[aria-sort="ascending"]);
css
.data-checked\:underline[data-ui~="checked"] {
  text-decoration-line: underline;
}
.sirali-artan\:font-bold[aria-sort="ascending"] {
  --tw-font-weight: var(--font-weight-bold);
  font-weight: var(--font-weight-bold);
}

~= işleci, boşlukla ayrılmış değer listesinde bir kelimeyi arar; böylece data-ui="checked active" taşıyan öğe de eşleşir. Özel tanım, aynı adlı yerleşik data-* davranışının yerine geçti: çıktıda [data-checked] değil [data-ui~="checked"] var. Form durumlarına özgü varyantlar form stilleri yazısında.

supports-* ve not-supports-*

supports-[...] köşeli parantez içine yazdığınız koşulla bir @supports kuralı üretir; yalnızca özellik adını yazarsanız (supports-backdrop-filter) özelliğin desteklenip desteklenmediğine bakar. not-supports-[...] koşulun tersini alır:

html
<div class="flex supports-[display:grid]:grid not-supports-[display:grid]:block">...</div>
<div class="bg-black/75 supports-backdrop-filter:bg-black/25">...</div>
css
@supports not (display:grid) {
  .not-supports-\[display\:grid\]\:block {
    display: block;
  }
}
@supports (backdrop-filter: var(--tw)) {
  .supports-backdrop-filter\:bg-black\/25 {
    background-color: color-mix(in srgb, #000 25%, transparent);
  }
  @supports (color: color-mix(in lab, red, red)) {
    .supports-backdrop-filter\:bg-black\/25 {
      background-color: color-mix(in oklab, var(--color-black) 25%, transparent);
    }
  }
}
@supports (display:grid) {
  .supports-\[display\:grid\]\:grid {
    display: grid;
  }
}
/* (kısaltıldı: flex ve bg-black/75) */

Yalnızca özellik adı verilen biçim, v4.3.5'te @supports (backdrop-filter: var(--tw)) koşuluna çevrildi; yani değer olarak bir değişken kabul edilip edilmediğine bakılıyor. Saydam renk sınıfının ürettiği color-mix() satırları ve iç içe @supports yedeği renk opaklığı yazısında açıklanıyor. Cam efekti gibi bulanıklık kullanımları blur ve backdrop-blur yazısında.

*: ve **: ile çocukları ve torunları biçimlendirmek

*: varyantı öğenin doğrudan çocuklarına, **: bütün alt öğelerine uygulanır. Belge *:'ı kontrol etmediğiniz çocuklar için önerir ve mümkünse sınıfları doğrudan çocuklara yazmanın daha iyi olduğunu ekler. **:'ı ise hedefi daraltan başka bir varyantla birleştirmenizi önerir:

html
<ul class="*:rounded-full *:bg-sky-50 *:px-2">
  <li>Satış</li>
  <li class="bg-red-50">Pazarlama</li>
</ul>
<ul class="**:data-avatar:size-12">
  <li><img data-avatar src="a.png"></li>
</ul>
css
@layer utilities {
  .bg-red-50 {
    background-color: var(--color-red-50);
  }
  :is(.\*\:rounded-full > *) {
    border-radius: calc(infinity * 1px);
  }
  :is(.\*\:bg-sky-50 > *) {
    background-color: var(--color-sky-50);
  }
  :is(.\*\:px-2 > *) {
    padding-inline: calc(var(--spacing) * 2);
  }
  :is(.\*\*\:data-avatar\:size-12 *)[data-avatar] {
    width: calc(var(--spacing) * 12);
    height: calc(var(--spacing) * 12);
  }
}

Bu çıktı, belgedeki uyarının nedenini somut olarak gösteriyor. İkinci li öğesine yazdığımız bg-red-50 kuralı, ebeveynden gelen *:bg-sky-50 kuralından önce yazıldı. Belgeye göre çocuk kuralları normal kurallardan sonra üretilir ve aynı özgüllüğe sahiptir; dolayısıyla sonra gelen gök mavisi kazanır ve çocuktaki sınıf ebeveynin verdiği stili ezemez. Bir çocuğun farklı görünmesi gerekiyorsa sınıfları ebeveyne değil çocuklara tek tek yazın. Çakışmaların genel mantığı çakışan sınıflar yazısında.

Varyant yığma sırası: soldan sağa

Yükseltme rehberine göre v3'te yığılmış varyantlar sağdan sola uygulanıyordu, v4'te ise CSS sözdizimine benzemesi için soldan sağa uygulanıyor. Sıranın gerçekten farklı CSS ürettiğini görmek için aynı iki varyantı iki sırayla derledik:

css
@import "tailwindcss" source(none);
@source inline("*:first:pt-0 first:*:pt-0 dark:md:hover:bg-fuchsia-600");
css
@layer utilities {
  :is(.\*\:first\:pt-0 > *):first-child {
    padding-top: 0px;
  }
  :is(.first\:\*\:pt-0:first-child > *) {
    padding-top: 0px;
  }
  @media (prefers-color-scheme: dark) {
    @media (width >= 48rem) {
      @media (hover: hover) {
        .dark\:md\:hover\:bg-fuchsia-600:hover {
          background-color: var(--color-fuchsia-600);
        }
      }
    }
  }
}

*:first:pt-0, ebeveynin ilk çocuğunun üst boşluğunu sıfırlar; rehberin v4 için önerdiği yazım budur. first:*:pt-0 ise ebeveynin kendisi ilk çocuk olduğunda onun bütün çocuklarına uygulanır; tamamen farklı bir seçici. v3'ten taşınan şablonlarda sıraya duyarlı yığmaları ters çevirmeniz gerekir. dark:md:hover: örneğinde ise koşullar soldan sağa iç içe geçmiş medya sorgularına dönüştü. v3'ten geçişin diğer adımları için Tailwind v4 yenilikleri yazısına bakın.

İki varyant ailesi: seçici ve at-rule

Bu noktaya kadar gördüğünüz çıktılara bakınca varyantların iki gruba ayrıldığını fark etmişsinizdir. Birinci grup seçiciyi değiştirir: hover: sınıfa :hover ekler, aria-expanded: bir öznitelik seçicisi ekler, [&_p]: bir alt öğe seçicisi kurar, gece: bir :where() koşulu bağlar. İkinci grup kuralı bir at-rule ile sarar: md: bir @media, supports-* bir @supports, varsayılan dark: bir prefers-color-scheme sorgusu üretir. Bazıları ikisini birden yapar; v4.3.5'te hover: hem :hover seçicisini hem @media (hover: hover) sorgusunu kullanıyor. Yığdığınız her varyant bu iki işlemden birini soldan sağa uygular. Bu ayrımı bilmek, kendi varyantınızı tanımlarken kısa yazımın mı (tek seçici koşulu) yoksa @slot'lu blok yazımın mı (at-rule ve seçici bir arada) gerektiğine karar vermeyi kolaylaştırır.

Uygulamalı örnek: öznitelikle yönetilen sekmeler

Uygulamalı örnek: öznitelikle yönetilen sekmeler: [&.sinif]: keyfi seçici, [@supports(...)]: keyfi at-rule, @custom-variant ad (seçici), @custom-variant ad { ... @slot; }

Öğrendiklerimizi küçük bir sekmeli arayüzde birleştirelim. Seçili sekme aria-selected="true" ile, açık panel ise data-durum="acik" ile işaretleniyor. Sekmeler için hazır aria-selected varyantını, paneller için kendi tanımladığımız acik varyantını kullanıyoruz:

css
@import "tailwindcss" source(none);
@source "./ornek.html";
@custom-variant acik (&[data-durum="acik"]);
html
<div role="tablist" class="flex gap-4">
  <button role="tab" aria-selected="true" class="pb-2 aria-selected:border-b-2 aria-selected:border-sky-600">Genel</button>
  <button role="tab" aria-selected="false" class="pb-2 aria-selected:border-b-2 aria-selected:border-sky-600">Fatura</button>
</div>
<section data-durum="acik" class="hidden acik:block">Genel ayarlar</section>
<section data-durum="kapali" class="hidden acik:block">Fatura ayarları</section>

v4.3.5 ile derlenen CSS:

css
@layer utilities {
  .flex {
    display: flex;
  }
  .hidden {
    display: none;
  }
  .gap-4 {
    gap: calc(var(--spacing) * 4);
  }
  .pb-2 {
    padding-bottom: calc(var(--spacing) * 2);
  }
  .aria-selected\:border-b-2[aria-selected="true"] {
    border-bottom-style: var(--tw-border-style);
    border-bottom-width: 2px;
  }
  .aria-selected\:border-sky-600[aria-selected="true"] {
    border-color: var(--color-sky-600);
  }
  .acik\:block[data-durum="acik"] {
    display: block;
  }
}
/* (kısaltıldı: @property ve @layer properties) */

Panellerin ikisi de hidden ile başlıyor. .acik\:block[data-durum="acik"] kuralı hem bir sınıf hem bir öznitelik seçicisi içerdiği ve çıktıda .hidden'dan sonra geldiği için, yalnızca data-durum="acik" taşıyan panel görünür. İkinci sekme seçildiğinde JavaScript'in yapması gereken tek şey iki özniteliği güncellemektir; sınıf listesine dokunulmaz ve görsel durum, işaretlemedeki öznitelikle aynı kaynaktan beslenir. ARIA özniteliklerinin erişilebilirlik tarafı erişilebilirlik yazısında, display sınıflarının ayrıntısı display ve hidden yazısında.

AraçNerede yazılırNe zaman
[&.sinif]: keyfi seçiciHTML sınıfındaTek seferlik seçici koşulu
[@supports(...)]: keyfi at-ruleHTML sınıfındaTek seferlik medya ya da destek koşulu
@custom-variant ad (seçici)CSS dosyasındaTekrar eden tek katmanlı koşul
@custom-variant ad { ... @slot; }CSS dosyasındaİç içe at-rule ve seçici içeren koşul
@variant ad { ... }Kendi CSS kuralınızdaVar olan varyantı sınıf yazmadan uygulamak

Sık yapılan hatalar

  • Tanımsız varyant kullanmak: HTML'de xyz:p-4 gibi bilinmeyen bir varyant hata vermez, sınıf sessizce üretilmez; aynı derlemede denedik ve çıktıda hiçbir iz yoktu. @variant içinde ise derleme hata ile durur.
  • Yığma sırasını v3'e göre yazmak: first:*: ile *:first: farklı seçicilerdir.
  • Çocuğu *: ile biçimleyip sonra ezmeye çalışmak: çocuktaki sınıf kazanmaz.
  • Keyfi varyantta boşluk bırakmak: sınıf adları boşluk içeremez; seçicideki boşluk için alt çizgi kullanın ([&_p]).
  • Blok yazımda @slot unutmak: utility bildirimlerinin yerini @slot belirler.
  • Hazır bir adı farkında olmadan ezmek: data-checked örneğinde gördüğünüz gibi özel tanım, aynı addaki yerleşik davranışın yerine geçer. Bilerek yapmıyorsanız gece, acik gibi projeye özgü ve çakışma ihtimali düşük adlar seçin; ekipteki diğer geliştiriciler de varyantın özel olduğunu adından anlar.

Sık Sorulan Sorular

@custom-variant ile @variant arasındaki fark nedir?

@custom-variant yeni bir varyant adı tanımlar ve onu HTML'de ad:sinif biçiminde kullanılabilir kılar. @variant ise var olan (hazır ya da özel) bir varyantı kendi CSS kuralınızın içinde uygular; yeni bir ad oluşturmaz.

Özel varyantım responsive varyantlarla birlikte çalışır mı?

Evet. md:gece:p-8 örneğinde hem medya sorgusu hem özel koşul birlikte uygulandı. Özel utility'lerle de çalışır; @utility direktifi yazısında örnekler var.

Keyfi varyant mı, @custom-variant mı?

Koşulu bir iki yerde kullanıyorsanız keyfi varyant yeterlidir. Aynı köşeli parantezli ifadeyi tekrar tekrar yazıyorsanız belge onu @custom-variant ile adlandırmayı önerir; okunabilirlik artar ve yazım hatası riski azalır. Ek bir avantaj olarak, adlandırılmış varyantın seçicisini ileride değiştirmeniz gerekirse tek bir satırı düzenlersiniz; keyfi varyantta ise her sınıfı tek tek bulup değiştirmeniz gerekir.

Özel varyantım bir Vue bileşeninin stil bloğunda çalışmıyor, neden?

Belgeye göre ana CSS'ten ayrı paketlenen stil blokları ve CSS modülleri, başka dosyalarda tanımlanan özel varyantlara erişemez. Bu bloklarda @variant gece kullanmak için önce ana dosyanızı @reference ile bağlamanız gerekir; aksi hâlde bilinmeyen varyant hatası alırsınız. Ayrıntılar @apply ve @reference yazısında.

Container query varyantları da @custom-variant ile mi tanımlanır?

Hayır, @sm: gibi kapsayıcı sorgusu varyantları hazırdır ve --container-* tema değişkenlerinden gelir; ayrıntılar container queries yazısında.

Marka adları sahiplerine aittir. Bu yazı bağımsız bir teknik rehberdir; adı geçen kuruluşlarla bayilik veya ortaklık ilişkisi bulunduğu anlamına gelmez.

İlgili içerikler: Tailwind öğrenme rehberi · Hover ve focus durumları · group ve peer · Dark mode · @utility direktifi · Erişilebilirlik