Bir elemana üç farklı yerden renk veriyorsun ama tarayıcı senin istemediğin rengi seçiyor. İşte burada devreye CSS specificity giriyor: aynı elemana uygulanan birden çok kural çakıştığında hangisinin kazanacağını belirleyen öncelik sistemidir. Specificity'yi anladığında, "neden bu stil tutmuyor?" sorusuyla kaybettiğin saatler biter; rastgele !important serpiştirmek yerine kuralın neden uygulandığını ya da uygulanmadığını tam olarak görürsün.
Cascade nedir, specificity nerede devreye girer?
CSS'in adındaki "Cascading" (kademeli), tarayıcının çakışan kuralları sıraya sokma sürecidir. Tarayıcı kazananı belirlerken sırasıyla şu adımlara bakar:
- Origin ve önem: Stilin kaynağı (tarayıcı varsayılanı, kullanıcı stilleri, yazar stilleri) ve
!importanttaşıyıp taşımadığı. - Specificity: Selector'ün ne kadar "spesifik" olduğu — asıl odağımız bu.
- Kaynak sırası: Diğer her şey eşitse, dosyada en son yazılan kural kazanır.
Yani specificity tek başına karar vermez; cascade'in bir basamağıdır. Ama günlük çalışmada en çok kafa karıştıran ve en sık çakışmaya yol açan basamak da budur.
Specificity nasıl hesaplanır: üç haneli sistem
Her selector'e üç bileşenli bir değer verildiğini düşün. Bunu çoğu zaman (a, b, c) üçlüsü olarak yazarız:
- a — ID seçiciler:
#headergibi her ID bir puan. - b — Class, attribute ve pseudo-class:
.btn,[type="text"],:hovergibi her biri bir puan. - c — Eleman ve pseudo-element:
div,a,::beforegibi her biri bir puan.
Karşılaştırma soldan sağa yapılır: önce a'lara bakılır, eşitse b'lere, sonra c'lere. Birkaç örnek:
/* (0,0,1) — tek eleman */
p { color: black; }
/* (0,1,1) — bir class + bir eleman */
p.intro { color: blue; }
/* (1,0,0) — tek ID */
#main { color: green; }
/* (1,1,1) — ID + class + eleman */
#main p.intro { color: red; }
Burada hepsi aynı <p class="intro"> elemanını hedeflerse, kazanan en yüksek değere sahip #main p.intro olur ve renk kırmızı olur. Önemli bir ayrıntı: değerler 10'luk tabanda toplanmaz. Yani 11 tane class (0,11,0), tek bir ID'yi (1,0,0) asla geçemez. a sütununda tek bir 1, b sütunundaki onlarca puandan üstündür.
Puan saymayan seçiciler ve özel durumlar
Bazı şeyler specificity'ye hiç katkı yapmaz:
- Evrensel seçici
*ve kombinatörler (>,+,~, boşluk) puana sıfır katkı yapar. :where()her zaman sıfır specificity üretir — içine ne koyarsan koy. Bu, kolayca ezilebilen "yumuşak" varsayılanlar yazmak için çok kullanışlıdır.:is()ve:not()kendileri puan eklemez ama içlerindeki en spesifik seçicinin puanını alır. Örneğin:is(#a, .b)bir ID kadar sayılır.
/* :where() ile yazılan kural sıfır specificity'dir */
:where(.card) a { color: teal; }
/* tek bir eleman seçici bile bunu ezer (0,0,2 > 0,0,1) */
.card a { color: orange; }
!important ve inline stil: sıralamayı bozanlar
İki şey normal specificity hesabının dışında kalır. Birincisi inline stil, yani style özniteliği; bu, selector tabanlı her kuraldan daha güçlüdür (kavramsal olarak dördüncü ve en soldaki haneyi düşün). İkincisi !important: bir bildirime eklendiğinde o bildirim ayrı, daha üstün bir katmana taşınır.
<p id="x" style="color: green">Merhaba</p>
#x { color: red; } /* inline kazanır → yeşil */
#x { color: blue !important; } /* !important inline'ı da ezer → mavi */
İki !important çakışırsa aralarında yine normal specificity ve kaynak sırası kuralları işler. Pratikte tavsiye nettir: !important'ı son çare olarak gör. Bir kez kullanmaya başlayınca onu ezmek için yine !important gerekir ve stil dosyan bir tırmanış yarışına döner.
Pratikte specificity savaşından kaçınmak
Amaç düşük ve tutarlı specificity ile çalışmaktır:
- ID yerine class kullan: Stillemede ID'ler gereksiz yere yüksek puan getirir. Seçicilerini class tabanlı tut.
- Selector'leri sığ tut:
.nav ul li ayerine doğrudan.nav-linkgibi tek bir class hedefle. - Cascade layer'ları kullan:
@layerile katman önceliğini açıkça belirleyebilirsin; sonra gelen katman, specificity'den bağımsız olarak öncekini ezer. - Yumuşak varsayılanlar için
:where()kullan ki bileşen kullanıcıları stilleri kolayca geçersin kılabilsin.
Tarayıcının DevTools'unda "Styles" panelini aç: üstü çizili bildirimler hangi kuralın ezildiğini, hangisinin kazandığını anında gösterir. Tahmin etmek yerine bu paneli okuman, specificity'yi içselleştirmenin en hızlı yoludur.
Sık Sorulan Sorular
Eşit specificity'de ne olur?
Origin, önem ve specificity tamamen eşitse kaynak sırası karar verir: CSS'te en son tanımlanan kural kazanır. Bu yüzden bir override yazarken onu mevcut kuraldan sonra koymak çoğu zaman yeterlidir.
!important her zaman kazanır mı?
Yazar stilleri arasında evet, neredeyse her zaman normal bildirimleri ezer. Ancak başka bir !important bildirim daha yüksek specificity'ye sahipse ya da daha sonra geliyorsa o kazanır. Mümkünse !important'a hiç gerek bırakmayacak bir yapı kur.
:is() ve :where() arasındaki fark nedir?
İkisi de seçici listesini gruplar ve aynı eşleşmeyi yapar. Fark specificity'dedir: :is() içindeki en spesifik seçicinin puanını alır, :where() ise her zaman sıfır puan üretir.
Stil çakışmaları büyüdüyse ve CSS'in sürdürülemez bir !important yığınına dönüştüyse, mimariyi class tabanlı ve katmanlı bir yapıya taşımana yardım edebilirim. Bir projeyi konuşmak için benimle iletişime geç.