Yazılarımız

OfisData

CSS SPECIFICITY HATALARINI ÖNLEMEK

CSS specificity dört haneli puan kartı inline ID class element segmentleri ve denge terazi sembolü

"Tarayıcıda inspect ediyorum, stil orada yazıyor ama uygulanmıyor." Bu cümleyi her ay onlarca kez görüyorum. Çoğu zaman cevap basit: başka bir kural daha "ağır" yazılmış ve seninkini eziyor. CSS bu ağırlığa specificity diyor.

Sorun !important ile kapatıldığında bir başka kural daha ağır olur, sonra başka bir !important gelir, kod tabanı kısa sürede içinden çıkılmaz bir savaş alanına döner. Doğru cevap savaşı kazanmak değil, savaşa girmemek. Bu da specificity'yi anlamakla başlar.

Specificity'nin hesabı öğrenildiğinde !important bir kurtarıcı olmaktan çıkıp itiraf ettiği şeye dönüşür: mimari sorunu. Cascade kurallarından BEM ve CSS Modules'a uzanan yol, aslında tek hedefe gider — hangi kuralın neden kazandığını her zaman bilen, sürdürülebilir bir CSS mimarisi.

Specificity Nasıl Hesaplanır

Specificity dört seviyeli bir puan sistemiyle hesaplanır. Yüksek olan kazanır.

SeviyeÖrnekPuan
Inline stylestyle="color: red"1000
ID seçici#header100
Sınıf, attribute, pseudo-class.btn, [type="text"], :hover10
Element, pseudo-elementdiv, ::before1

Birkaç örnek üzerinden:

  • p → 0,0,0,1 (1 puan)
  • .menu li → 0,0,1,1 (11 puan)
  • #nav .menu a → 0,1,1,1 (111 puan)
  • nav#main ul.menu li a:hover → 0,1,2,3 (123 puan)
  • style="..." → 1,0,0,0 (1000 puan)

İki kuralın specificity'si eşitse cascade devreye girer: dosyada daha sonra yazılan kazanır. Bu yüzden CSS dosyalarının sıralaması önemlidir; cascade ve specificity algoritmasının tam tanımı için web teknolojileri dokümantasyonu başvurulacak ilk kaynaktır.

Yaygın Hata: Gereksiz Yüksek Specificity

Yeni başlayan geliştiriciler güvende olmak için seçicileri "daha spesifik" yazma eğilimindedir:

/* YANLIŞ — gereksiz spesifik */
nav#main-nav ul.menu li.menu-item a.menu-link {
  color: blue;
}

/* DOĞRU — sade ve yeterli */
.menu-link {
  color: blue;
}

İlk yaklaşım çalışır ama bir tuzak: bu link'in rengini değiştirmek isteyen başka bir kural daha spesifik olmak zorunda kalır. Specificity savaşı işte böyle başlar.

Pratik kural: en spesifik değil, en sade selector'ı kullan. İhtiyaç yoksa parent yazma. Bir component class'ı genelde yeterlidir.

!important Tuzağı

!important specificity'yi atlatır, kuralın koşulsuz kazanmasını sağlar. Bu güçtür ama zehirdir.

.btn {
  background: red !important;
}

Sorunlar:

  • Bu butonu başka yerde override etmek için yine !important gerekiyor.
  • Tüm kod tabanı zamanla !important'la dolup taşıyor.
  • Refactor imkansız hâle geliyor; hangi kuralın neyi nerede ezdiği belirsizleşiyor.
  • Debugging cehennemleşiyor.

!important'in kabul edilebilir tek yeri utility class'lar (Tailwind tarzı tek görevli class'lar) ve üçüncü taraf bir kütüphanenin override'ını zorunlu kaldığında. Üretim kodunda kendi component CSS'inde !important görürsen, oradaki bir tasarım hatası var demektir.

VS Code dark theme editör CSS specificity kıyas görünümü iki seçici puan hesabı yorum satırlarında ve override edilen kural üzerinde çizgi

Cascade ve Kaynak Sırası

İki kural aynı specificity'ye sahipse cascade kazanan kuralı belirler. Sıralama şu önceliklerle çalışır:

  1. Origin (kullanıcı, tarayıcı, geliştirici)
  2. !important (origin başına)
  3. Layer (@layer)
  4. Specificity
  5. Kaynak sırası

Pratikte geliştiricinin yazdığı CSS aynı origin'dir; o yüzden specificity ve kaynak sırası belirleyici olur. Aynı specificity'de daha sonra gelen kural kazanır.

Bu kural kütüphane CSS'lerinin neden component CSS'lerinden ÖNCE import edilmesi gerektiğinin sebebidir. Bootstrap'tan sonra senin CSS'in gelmeli ki kendi class'larınla onun stillerini override edebilesin.

@layer ile Cascade Kontrol

Modern CSS'in en güzel özelliklerinden biri @layer. Cascade içinde manuel "katman" tanımlamayı sağlar:

@layer reset, base, components, utilities;

@layer reset {
  * { box-sizing: border-box; }
}

@layer components {
  .btn { padding: 10px 20px; }
}

@layer utilities {
  .text-red { color: red; }
}

Layer'lar listede yukarıda olanı yenebilir; en altta olan en güçlü. Specificity'den BAĞIMSIZ çalışır. Yani utilities layer'ındaki sade bir .text-red kuralı, components layer'ındaki çok spesifik bir #header .btn kuralını yenebilir.

@layer ile artık specificity savaşı çıkmaz; utility'lerin component'leri override etmesi, base style'ların reset'i ezmemesi otomatik olur. Modern CSS mimarisinin temeli haline geliyor.

BEM ve Sınıf Tabanlı Mimari

BEM (Block, Element, Modifier) eski ama hâlâ etkili bir convention. Tek seviyeli class isimlendirmesi sayesinde specificity hep aynı kalır:

/* Block */
.card { }

/* Element */
.card__title { }
.card__body { }
.card__footer { }

/* Modifier */
.card--featured { }
.card__title--large { }

Tüm seçiciler 0,0,1,0 puanında. İç içe yazılmamış, parent context'ten bağımsız. Bir card başka bir yere taşındığında stilleri değişmez; isim kendinde tüm bilgiyi taşıyor.

BEM'in pratik yararı: ne nereye ait belli, override için ekstra parent yazmaya gerek yok, refactor güvenli.

CSS Modules / Scoped CSS

Modern frontend framework'lerinde CSS Modules veya Scoped CSS yaygın. Her component'in class'ları otomatik olarak hash'lenir, global çakışma riski ortadan kalkar.

// React + CSS Modules
import styles from './Card.module.css';

<div className={styles.card}>
  <h2 className={styles.title}>Başlık</h2>
</div>

Üretimde class isimleri Card_card__a1b2c gibi hash'lenmiş hâle gelir. Başka bir component'te .card kullansan da çakışma olmaz.

Tailwind utility-first başka bir yaklaşım: tek görevli class'larla tüm stilleri JSX içinde yazıyorsun, kendi CSS'ini yazmak ortadan kalkıyor. Specificity sorunu da onunla. Modern CSS mimari kararları ve component stilleme stratejileri, React eğitimi sürecinde detaylı işlenir.

Specificity Hesaplama Pratiği

Bir karmaşık seçicinin specificity'sini hesaplamak için adımlar:

  1. Inline style var mı? → 1000 başlangıç.
  2. ID seçici(ler) say. Her biri 100.
  3. Class, attribute, pseudo-class say. Her biri 10.
  4. Element ve pseudo-element say. Her biri 1.
  5. Toplam o seçicinin puanıdır.

:not() ve :is() pseudo-class'ları özel davranır: kendi specificity'leri argümanları kadar. :not(.foo) → 0,0,1,0. :is(.foo, #bar) → 0,1,0,0 (içinden en yüksek olanı). :where() ise specificity'yi sıfır yapar — modern CSS'in yeni keşfettiği bir araç:

:where(button, .btn) {
  cursor: pointer;
}

Bu kural specificity 0; istediğin her şey onu override edebilir. Reset veya base style yazarken çok değerli.

Sürdürülebilir CSS Mimarisi

İyi bir CSS mimarisi kurmak için kararlar:

  • ID seçici stillemekte kullanma: ID'ler JS hook'ları için; CSS'te class kullan.
  • Maksimum iki seviye nesting: SCSS yazıyorsan & ile derin iç içe geçmiş seçicilerden kaçın.
  • Component-bazlı düşün: Her bileşen kendi class'ına sahip, kendi stil'ini taşır.
  • Utility class'lar utilities layer'ında: @layer ile spesifiklikten bağımsız kazanmalarını sağla.
  • Global stil minimum: Reset, base typography, brand colors. Üzerine her şey component bazlı.
  • Naming convention: BEM, CSS Modules, Tailwind — birini seç ve takım olarak tutarlı kullan.

Bu kararlar bir kez verildikten sonra specificity sorunu kayboluyor. Çakışma çıkması için iki kuralın aynı şeyi farklı şekilde yazması gerekiyor; iyi mimari bunu zaten önlüyor.

Specificity Bug'larını Debug Etmek

Chrome DevTools'ta Inspect > Styles paneline bakınca:

  • Üstte uygulanan stiller; sıralı liste, en altta olan en yenisi.
  • Geçili kurallar (ezilmiş olanlar) gri renkte gösterilir.
  • Hangi dosyadan geldiği yazıda belirtilir.
  • Computed sekmesinde nihai değerin hangi kuraldan geldiği görünür.

"Stilim uygulanmıyor" dediğinde önce DevTools'ta o elementi inspect et. Stilin üzerinde çizgi varsa başka bir kural daha spesifik. Çözüm o kuralı daha spesifik yazmak değil; başlangıçta gereksiz spesifik yazılanı sadeleştirmek.

Chrome DevTools Styles paneli ezilmiş üstü çizgili CSS kuralları ve daha spesifik kuralın uygulanmış halde gösterildiği inspector görünümü

Gerçek Bir Senaryoda Doğru Yaklaşım

Bir tipik durumla bitirelim. Şu senaryoda nasıl ilerlersin:

Bir Bootstrap projesi var. Default button rengi mavi. Senin tasarımın butonun yeşil olmasını istiyor. Nasıl yazarsın?

Yanlış yaklaşım: .btn-primary { background: green !important; } — bu işe yarar ama her override yine !important gerektirir.

Doğru yaklaşım:

  1. Bootstrap CSS'ini önce import et.
  2. Sonra kendi CSS'ini import et.
  3. Aynı specificity'de .btn-primary { background: green; } yaz — cascade kuralıyla sonraki kazanır.
  4. Eğer Bootstrap kendi içinde daha spesifik bir kural koymuşsa (örn. .btn.btn-primary), sen de aynı seviyede yaz.
  5. Sass kullanıyorsan Bootstrap'ın değişkenlerini override et — en temizi.

Bu yaklaşım !important kullanmadan, kod tabanını kirletmeden problemi çözer. Ve sonradan başka bir buton da değişeceği zaman aynı disiplinle düzenlersin.

 Vimaj