CSS @layer kuralı stillerinizi adlandırılmış cascade katmanlarına ayırmanızı ve bu katmanların öncelik sırasını açıkça belirlemenizi sağlar. Katmanlar arasında çakışma olduğunda kazananı özgüllük değil katman sırası belirler: Sonra tanımlanan katmandaki kural, önceki katmandaki daha özgül bir kuralı da geçersiz kılar. Herhangi bir katmana konmamış stiller ise tüm normal katmanlı stillerden üstündür.

Bu yazıda katman oluşturmanın üç yolunu, sıranın nasıl belirlendiğini, katmansız stillerin yerini, !important bildirimlerinde sıranın neden tersine döndüğünü, iç içe katmanları, @import ile bir stil dosyasını katmana almayı ve revert-layer anahtar kelimesini anlatıyoruz. Cascade'in genel işleyişi ve kaynak (origin) kavramı cascade ve kalıtım yazısında, seçici ağırlıkları ise özgüllük yazısında ele alınıyor.

Cascade içinde katmanların yeri

Cascade içinde katmanların yeri

MDN'nin cascade rehberine göre tarayıcı aynı özelliğe değer veren birden çok bildirimle karşılaştığında önce bildirimleri kaynaklarına ve önem durumlarına (!important olup olmadıklarına) göre sıralar. Katmanlar bu kaynak sıralamasının içinde çalışır: Her kaynak türünde (tarayıcı, kullanıcı, yazar) stiller adlandırılmış ya da adsız katmanlarda veya katman dışında bulunabilir. Katman dışında yazılmış stiller, en son tanımlanmış adsız bir katmanın parçası gibi değerlendirilir. Özgüllük ancak katman karşılaştırmasında eşitlik varsa devreye girer.

Bunun pratik sonucu şudur: Katmanları kullanmaya başladığınızda bir kuralın kazanması için seçicisini daha özgül yapmanız gerekmez. MDN'nin ifadesiyle, katman sırası kurulduktan sonra katmanlar arası karşılaştırmada özgüllük ve yazılış sırası göz ardı edilir. Bu da daha basit seçiciler yazmanıza imkân tanır; yeter ki kural doğru katmanda olsun.

Katman oluşturmanın üç yolu

Birinci yol bir blok kuralıdır: @layer ad { ... } yazarak adlandırılmış bir katman oluşturur ve kuralları doğrudan içine koyarsınız. İkinci yol bir ifade kuralıdır: @layer ad; ya da virgülle ayrılmış @layer a, b, c; biçimiyle bir veya birden çok katmanı stil eklemeden tanımlarsınız. Üçüncü yol ad vermeden yazılan @layer { ... } bloğudur ve adsız (anonim) bir katman oluşturur.

css
/* 1. blok: katmanı oluştur ve kural ekle */
@layer utilities {
  .padding-sm {
    padding: 0.5rem;
  }
}

/* 2. ifade: yalnızca sırayı belirle */
@layer theme, layout, utilities;

/* 3. adsız katman */
@layer {
  p {
    margin-block: 1rem;
  }
}

Adsız katman adlandırılmış katmanlar gibi çalışır, ancak ona daha sonra kural eklenemez; ona başvurmanın bir yolu yoktur. Önceliği, adlı ya da adsız tüm katmanlar arasındaki tanımlanma sırasına göre belirlenir ve katman dışındaki stillerden düşüktür.

Sıra nasıl belirlenir

Katmanların önceliğini ilk kez tanımlandıkları sıra belirler. İlk tanımlanan katman en düşük, son tanımlanan en yüksek önceliği alır. Bir katmanın adını daha sonra yeniden yazdığınızda kurallar o katmana eklenir ama sıra değişmez. Bu yüzden önerilen düzen, stil dosyasının en başına tek bir @layer ifadesiyle tüm katman sırasını yazmak ve kuralları sonra istediğiniz yerde eklemektir.

css
@layer base, special;

@layer special {
  .item {
    color: rebeccapurple;
  }
}

@layer base {
  .item {
    color: green;
    border: 5px solid green;
    font-size: 1.3em;
    padding: 0.5em;
  }
}
/* sonuç: renk rebeccapurple; kenarlık, yazı boyutu ve padding base'den gelir */

Bu MDN örneğinde special katmanı kaynakta base katmanından önce yazılmış olsa da ilk satırdaki ifade sırayı base, special olarak kurmuştur. Bu yüzden iki katman da .item için renk tanımladığında special kazanır. base katmanındaki diğer bildirimler ise çakışma olmadığından olduğu gibi uygulanır. Katmanlar bir kuralı bütünüyle değil, her özelliği ayrı ayrı yarıştırır.

Katmansız stiller her zaman önde

Katmanlarla ilgili en çok şaşırtan kural, katman dışında yazılmış normal stillerin tüm katmanlı normal stillerden üstün olmasıdır. Seçici ne kadar zayıf olursa olsun ve kaynakta ne kadar önce yazılırsa yazılsın bu böyledir. MDN'nin temel örneğinde p seçicisiyle katman dışında yazılan renk, type katmanındaki daha özgül .box p seçicisini yener:

html
<div class="box">
  <p>Hello, world!</p>
</div>
css
p {
  color: rebeccapurple;
}

@layer type {
  .box p {
    font-weight: bold;
    font-size: 1.3em;
    color: green;
  }
}
/* özgüllük: p = 0-0-1, .box p = 0-1-1
   ama katmansız stil katmanlıdan önceliklidir
   sonuç: metin rebeccapurple, kalın ve 1.3em */

Katmanlar olmasaydı .box p daha yüksek özgüllüğü nedeniyle kazanır ve metin yeşil olurdu. Katmanla birlikte karşılaştırma özgüllüğe hiç gelmeden kaynak ve katman aşamasında biter. Kalınlık ve yazı boyutu ise çakışma olmadığından katmandan uygulanır. Bu kural, mevcut bir projeye katmanları kademeli olarak eklerken dikkat edilmesi gereken bir noktadır: Henüz katmana taşınmamış eski stiller yeni katmanlı stillerinizi ezer.

!important ile sıranın tersine dönmesi

!important ile sıranın tersine dönmesi: A, ilk katman, B, ikinci katman, C, son katman, Tüm katmansız stiller

!important bildirimlerinde katman sırası ters işler. MDN'ye göre önemli bildirimler arasında ilk tanımlanan katman en yüksek önceliği alır; katman içindeki önemli bildirimler katman dışındaki önemli bildirimlerden üstündür. Bu tasarım bilinçlidir: Düşük öncelikli bir temel katman, gerçekten korunması gereken bir değeri !important ile kilitleyebilir ve sonraki katmanlar onu ezemez.

Öncelik (düşükten yükseğe)Yazar stiliÖnem
1A, ilk katmannormal
2B, ikinci katmannormal
3C, son katmannormal
4Tüm katmansız stillernormal
5Satır içi stylenormal
6Animasyonlar
7Tüm katmansız stiller!important
8C, son katman!important
9B, ikinci katman!important
10A, ilk katman!important
11Satır içi style!important
12Geçişler (transitions)

Tablo MDN'nin cascade rehberindeki yazar stilleri sıralamasıdır. Aşağıdaki örnek iki durumu karşılaştırır. Normal bildirimlerde katmansız kırmızı kazanır; ikisine de !important eklendiğinde ise katmandaki mavi kazanır.

css
/* durum 1: normal bildirimler */
p {
  color: red;
}
@layer B {
  :root p {
    color: blue;
  }
}
/* sonuç: kırmızı (katmansız normal > katmanlı normal) */

/* durum 2: ikisi de !important */
p {
  color: red !important;
}
@layer B {
  :root p {
    color: blue !important;
  }
}
/* sonuç: mavi (katmanlı önemli > katmansız önemli) */

MDN bu tersine dönme yüzünden harici stilleri ezmek için !important kullanılmamasını önerir. Doğru yol, çatı, bileşen kütüphanesi gibi harici stil dosyalarını @import ile bir katmana alıp sıranın en başına koymaktır. Böylece onların önceliği düşer ve sonra tanımladığınız kendi katmanlarınız onları kolayca geçersiz kılar. Rehbere göre !important en fazla, ilk tanımlanan katmanda vazgeçilmez stilleri sonraki değişikliklere karşı korumak için ve çok seyrek kullanılmalıdır.

Katmanlar yalnızca yazar stillerinde değil

Katmanları genellikle kendi stil dosyalarımız için düşünürüz, ama MDN'nin cascade rehberine göre katman kavramı üç kaynak türünün hepsinde vardır: tarayıcı (user-agent), kullanıcı ve yazar. Tam sıralamada her kaynak kendi içinde aynı mantıkla işler. Normal bildirimlerde önce tarayıcı katmanları ve katmansız tarayıcı stilleri, sonra kullanıcı katmanları ve katmansız kullanıcı stilleri, sonra yazar katmanları, katmansız yazar stilleri ve satır içi stiller gelir. Ardından animasyonlar yer alır. Önemli bildirimlerde sıra her kaynakta tersine döner ve kaynakların sırası da ters çevrilir: önemli yazar stilleri, onların üstünde önemli kullanıcı stilleri, en üstte önemli tarayıcı stilleri bulunur. Geçişler (transitions) ise hepsinin üzerindedir.

Bu sıralamanın pratik anlamı şudur: Siz kendi katmanlarınızı ne kadar dikkatli düzenlerseniz düzenleyin, kullanıcının !important ile işaretlediği bir tercih, örneğin erişilebilirlik için büyütülmüş bir yazı boyutu, sizin önemli bildirimlerinizin önüne geçer. Katmanlar yazar kaynağı içindeki düzeni yönetmek için tasarlanmıştır, kullanıcının tercihlerini aşmak için değil. MDN ayrıca @import ile katman belirtilmeden içe aktarılan CSS'in, son tanımlanmış katman gibi, yani katmansız stillerle aynı düzeyde ele alındığını belirtir.

Cascade'e yalnızca özellik ve değer bildirimleri katılır. @layer bloğunun içindeki @font-face ya da @keyframes gibi at-rule'lar bütün olarak değerlendirilir; içlerindeki tanımlayıcılar (descriptor) tek tek yarışmaz. Bu yüzden katmanlar asıl olarak seçici kurallarındaki bildirimlerin önceliğini belirlemek için kullanılır.

İç içe katmanlar

Katmanlar iç içe yazılabilir. Bir katmanın içinde tanımlanan katman, dış katmanın alt katmanı olur. Alt katmana dışarıdan kural eklemek için iki adı nokta ile birleştirirsiniz. Bu düzen, örneğin bir çatının kendi iç katmanlarını sizin katman sıranızı bozmadan tek bir dış katman altında toplamasına izin verir.

css
@layer framework {
  @layer layout {
  }
}

/* framework içindeki layout katmanına kural ekle */
@layer framework.layout {
  p {
    margin-block: 1rem;
  }
}

@import ile stil dosyasını katmana almak

Katman oluşturmanın bir yolu da @import kuralıdır. URL'den sonra layer anahtar kelimesini ya da layer(ad) fonksiyonunu yazarak içe aktarılan dosyanın tüm kurallarını bir katmana yerleştirirsiniz. MDN'ye göre içe aktarılan stil dosyalarındaki bildirimler, içe aktarma noktasına doğrudan yazılmış gibi cascade'e katılır. Aynı ada sahip birden çok içe aktarma aynı katmanda birleşir.

css
@layer reset, framework, components;

@import "reset.css" layer(reset);
@import "framework.css" layer(framework);

@layer components {
  .btn {
    padding: 0.5rem 1rem;
  }
}

Burada sıranın bir inceliği var. MDN'ye göre @import kuralı @charset ve katman oluşturan @layer ifadeleri dışında tüm kurallardan önce gelmelidir; aksi hâlde yok sayılır. @layer blokları bu istisnaya dahil değildir. Yani yukarıdaki örnekte ilk satırdaki sıra ifadesi @import satırlarından önce durabilir, ama @layer components { ... } bloğu ancak içe aktarmalardan sonra gelebilir. layer anahtar kelimesini ad vermeden yazarsanız (@import "style.css" layer;) dosya adsız bir katmana alınır ve bu katmana sonradan kural eklenemez. @import satırına supports() ve media query koşulları da eklenebilir; bunları @supports yazısında anlatıyoruz.

revert-layer: önceki katmana geri dönmek

revert-layer, CSS genelinde geçerli bir anahtar kelimedir. Bir katman içindeki özelliğin değerini, o öğeyle eşleşen ve önceki bir katmanda bulunan kuralın değerine geri sarar. Değer, mevcut katmanda o öğe için hiç kural yokmuş gibi yeniden hesaplanır. Önceki katmanlarda eşleşen bir kural yoksa değer bir önceki kaynağa, yani kullanıcı ya da tarayıcı stillerine döner.

css
@layer base, special;

@layer special {
  .item {
    color: red;
  }
  .feature {
    color: revert-layer;
  }
}

@layer base {
  .item {
    color: blue;
  }
  .feature {
    color: green;
  }
}
/* sonuç: .item öğeleri kırmızı;
   class="item feature" olan öğe base katmanına geri döner
   ve orada .feature (sonra geldiği için) .item'ı yenerek yeşil olur */

revert ile farkı kapsamdadır. revert yazar stillerini tamamen geri sararak kullanıcı ya da tarayıcı kaynağına döner; revert-layer ise yalnızca bir önceki katmana döner ve yazar kaynağı içinde kalır. initial, inherit, unset ve revert anahtar kelimeleri cascade yazısında karşılaştırılıyor.

Uygulamalı mini örnek: dört katmanlı proje

Aşağıdaki proje dört katman kullanıyor. Hangi kuralın kazandığını adım adım izleyelim.

html
<button class="btn primary bg-red">Gönder</button>
css
@layer reset, base, components, utilities;

@layer reset {
  button {
    font: inherit;
  }
}

@layer components {
  .btn.primary {
    background-color: blue;   /* özgüllük 0-2-0 */
  }
}

@layer utilities {
  .bg-red {
    background-color: red;    /* özgüllük 0-1-0 */
  }
}

İki kural da background-color için yarışıyor. .btn.primary iki sınıf içerdiği için 0-2-0, .bg-red tek sınıf içerdiği için 0-1-0 özgüllüğe sahiptir. Katmanlar olmasaydı mavi kazanırdı. Ancak utilities sırada components katmanından sonra geldiği için kırmızı kazanır; katmanlar arası karşılaştırmada özgüllüğe bakılmaz. Şimdi dosyanın sonuna katman dışında button { background-color: green; } eklediğinizi düşünün. Bu seçici yalnızca 0-0-1 özgüllüğe sahiptir ama katmansız olduğu için tüm normal katmanlı stilleri yener ve düğme yeşil olur. Son olarak reset katmanına background-color: gray !important eklerseniz, ilk katmandaki önemli bildirim ters sıralama nedeniyle hepsini geçer ve düğme gri olur.

Sık yapılan hatalar

Sık yapılan hatalar: Sırayı en başta belirtmemek, Katmansız eski stilleri unutmak, Harici stilleri !important ile ezmeye çalışmak, @import satırını bir @layer bloğundan sonra...
  • Sırayı en başta belirtmemek: Katmanların önceliği ilk tanımlanma sırasına göre kurulur. Bir bloğu dosyanın başına yazmak, istemeden onu en düşük katman yapabilir.
  • Katmansız eski stilleri unutmak: Katman dışında kalan her normal kural, katmanlı tüm normal kurallardan güçlüdür.
  • Harici stilleri !important ile ezmeye çalışmak: Katmanlarda önemli bildirimlerin sırası tersine döner; harici dosyayı erken bir katmana alın.
  • @import satırını bir @layer bloğundan sonra yazmak: @import yalnızca @charset ve @layer ifadelerinden sonra gelebilir; bir bloktan ya da stil kuralından sonra gelirse yok sayılır.
  • Adsız katmana sonradan kural eklemeye çalışmak: Adsız katmanlara başvurulamaz; tekrar kullanacağınız her katmana ad verin.
  • Katmanın kuralı bütünüyle ezdiğini sanmak: Karşılaştırma özellik bazındadır; çakışmayan bildirimler önceki katmandan uygulanmaya devam eder.

Sık Sorulan Sorular

@layer özgüllüğü tamamen kaldırır mı?

Hayır. Özgüllük aynı katman içindeki kurallar arasında ve katmansız kurallar arasında eskisi gibi çalışır. Yalnızca farklı katmanlardaki kurallar karşılaştırılırken katman sırası özgüllükten önce gelir.

Katmansız stillerimi en düşük önceliğe almak istersem ne yapmalıyım?

Onları da bir katmana taşıyın ve o katmanı sıranın başına yazın. Katman dışında kalan stiller tanım gereği tüm normal katmanlı stillerin üzerindedir.

Satır içi style özniteliği katmanlardan etkilenir mi?

MDN'nin sıralamasına göre normal satır içi stiller tüm normal yazar stillerinden, önemli satır içi stiller de tüm önemli yazar stillerinden üstündür. Katmanlar bu sırayı değiştirmez.

@layer kuralını bir seçicinin içine yazabilir miyim?

Evet. Yerel CSS nesting ile @layer bir stil kuralının içinde kullanılabilir ve iç içe katman adları nokta ile birleşir. Örnekleri nesting yazısında bulabilirsiniz.

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: CSS öğrenme rehberi · Cascade ve kalıtım · Özgüllük hesaplama · @scope ile kapsamlı stil · CSS nesting · CSS hata ayıklama