CSS Specificity ve Cascade: Öncelik Sistemi

CSS Specificity ve Cascade: Öncelik Sistemi
CSS'in en çok kafa karıştıran konularından biri, hangi stilin uygulanacağını belirleyen cascade ve specificity sistemidir. Neden bir stil çalışmıyor, neden !important eklenmesi gerekiyor, neden inline style her şeyin önüne geçiyor — bu soruların cevabı cascade ve specificity sistemini anlamaktan geçiyor. Bu rehberde specificity hesaplama yöntemini, cascade layers (@layer) sistemini, !important'ın doğru kullanımını ve :where() ile sıfırlama tekniklerini ele alacağız.
1. Specificity: Öncelik Hesaplama
Her CSS selector'ının bir "özgüllük puanı" vardır. Birden fazla kural aynı elemente uygulandığında, en yüksek özgüllük puanına sahip kural kazanır.
/*
Specificity: (inline, ID, class/attr/pseudo-class, element/pseudo-element)
Gösterim: (a, b, c, d)
- Inline style: (1,0,0,0)
- ID selector: (0,1,0,0)
- Class, attribute, :hover, :nth-child vb: (0,0,1,0)
- Element, ::before vb: (0,0,0,1)
- :not() ve :is(): içerdiği selector'ın özgüllüğü
- :where(): her zaman (0,0,0,0)
- * (evrensel selector): (0,0,0,0)
*/
/* (0,0,0,1) — 1 element */
p { color: blue; }
/* (0,0,1,0) — 1 class */
.text { color: green; }
/* (0,1,0,0) — 1 ID */
#title { color: red; }
/* (0,0,1,1) — 1 class + 1 element */
p.text { color: purple; }
/* (0,1,1,1) — 1 ID + 1 class + 1 element */
#main .text p { color: orange; }
/* Pratik örnekler */
.nav .nav__item .nav__link { color: blue; } /* (0,0,3,0) */
#nav .nav__link { color: red; } /* (0,1,1,0) — kazanır */
/* Neden çalışmıyor sorununa çözüm */
/* YANLIŞ: ID ile ezmeye çalışmak */
#container .button { background: blue; } /* (0,1,1,0) */
/* DOĞRU: Aynı özgüllükte daha aşağıda yazılmış kural */
.container .button { background: red; } /* (0,0,2,0) — kaybeder */
/* DOĞRU: Özgüllüğü eşitleyip sonra yaz */
.container .button.button { background: red; } /* (0,0,3,0) — kazanır */
2. Cascade: Hangi Stil Uygulanır?
Cascade, birden fazla kural çakıştığında hangisinin kazanacağını belirleyen algoritmadır. Aşağıdaki öncelik sırası her zaman geçerlidir.
/*
CASCADE ÖNCELIK SIRASI (düşükten yükseğe):
1. Kullanıcı-agent (tarayıcı) stilleri
2. Author (geliştirici) stilleri — @layer ile yönetilir
3. Kullanıcı stilleri
4. !important author stilleri
5. !important kullanıcı stilleri
Her seviyede:
1. @layer sıralaması (sonra tanımlanan layer kazanır)
2. Specificity (yüksek olan kazanır)
3. Kaynak sırası (sonra gelen kazanır)
*/
/* Örnek: Kaynak sırası (specificity eşit) */
.button { background: blue; }
.button { background: red; } /* Kazanır — sonra yazılmış */
/* Örnek: Specificity farkı */
.nav .link { color: blue; } /* (0,0,2,0) */
.link { color: red; } /* (0,0,1,0) — kaybeder */
/* Örnek: Inheritance (miras) */
body { font-family: sans-serif; }
/* Tüm alt elementler font-family'i miras alır */
/* background, border gibi özellikler miras alınmaz */
/* inherit, initial, unset, revert */
.reset-color { color: inherit; } /* Ebeveynden al */
.reset-all { color: initial; } /* Tarayıcı varsayılanına dön */
.reset-font { all: unset; } /* TÜM özellikleri sıfırla */
3. @layer: Cascade Layers
CSS Cascade Layers (@layer), stilleri mantıksal katmanlara ayırmanızı ve bu katmanların öncelik sırasını açıkça belirlemenizi sağlar. Bu özellik, büyük projelerde !important olmadan specificity savaşlarını sonlandırır.
/* Layer sıralaması tanımla — düşükten yükseğe */
@layer reset, base, components, utilities;
/* reset — en düşük öncelik */
@layer reset {
*, *::before, *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
img { display: block; max-width: 100%; }
}
/* base — temel stiller */
@layer base {
body {
font-family: system-ui, sans-serif;
line-height: 1.5;
color: #374151;
}
h1, h2, h3, h4, h5, h6 { font-weight: 600; }
a { color: #3b82f6; }
}
/* components — bileşen stilleri */
@layer components {
.button {
padding: 0.5rem 1rem;
border-radius: 6px;
font-weight: 500;
cursor: pointer;
background: #3b82f6;
color: white;
border: none;
}
.card {
background: white;
border-radius: 12px;
padding: 1.5rem;
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
}
}
/* utilities — en yüksek öncelik (!)important olmadan) */
@layer utilities {
.hidden { display: none; }
.flex { display: flex; }
.text-center { text-align: center; }
.mt-4 { margin-top: 1rem; }
.text-red { color: #ef4444; }
}
/* Harici CSS (örn: üçüncü taraf kütüphane) — Layer dışı */
/* Layer içindeki stillerin önüne geçer (layer > no-layer) */
/* Layer'ı import et */
@import url('bootstrap.css') layer(vendor);
@layer reset, vendor, base, components, utilities;
@layer ile tasarım sistemleri oluştururken utility class'ları en üst katmana koyun. Bu sayede text-center gibi utility'ler, bileşen stillerini her zaman ezer — !important gerekmez.
4. !important: Doğru ve Yanlış Kullanım
!important, cascade'i devre dışı bırakarak bir stili zorla uygular. Yanlış kullanıldığında sürdürülmesi neredeyse imkânsız CSS'e yol açar.
/* YANLIŞ kullanım örnekleri */
.title {
color: #111827 !important; /* Neden? Specificity sorununu !important ile çözmek kötü */
}
/* YANLIŞ: Zincir başlatmak */
.override { color: red !important; }
/* Başkası ezerse: */
.more-override { color: blue !important; } /* !important savaşı başlar */
/* DOĞRU kullanım: Erişilebilirlik / kullanıcı tercihi */
@media (prefers-reduced-motion: reduce) {
*, *::before, *::after {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
}
}
/* DOĞRU: JavaScript tarafından dinamik eklenen inline stilleri ezmek */
.force-hidden { display: none !important; }
/* DOĞRU: Üçüncü taraf kütüphane stillerini override (ama @layer tercih edilmeli) */
.widget-title {
font-size: 1.5rem !important; /* Kütüphane inline style kullanıyorsa */
}
/* @layer ile !important'sız override */
@layer utilities {
.no-display { display: none; } /* Layer sayesinde !important gerekmez */
}
5. :where() ile Sıfır Özgüllüklü Seçiciler
:where() pseudo-class fonksiyonu, argümanlarının özgüllüğünü sıfıra indirger. Tüketilmesi kolay, ezilmesi kolay temel stiller yazarken idealdir.
/* :is() — argümanların özgüllüğünü ALIR */
:is(header, main, footer) p {
color: gray; /* header p'nin özgüllüğü: (0,0,0,2) */
}
/* :where() — SIFIR özgüllük */
:where(header, main, footer) p {
color: gray; /* Özgüllük: (0,0,0,1) — sadece 'p' sayılır */
}
/* Reset/Base stilleri için :where() */
:where(h1, h2, h3, h4, h5, h6) {
margin: 0;
line-height: 1.2;
}
:where(ul, ol) {
padding: 0;
list-style: none;
}
/* :where() ile üzerine yazmak çok kolay */
.article h2 {
color: #1f2937; /* (0,0,1,1) — :where(h2) sıfır olduğu için kazanır */
}
/* Modern CSS reset örneği — :where() kullanarak */
:where(*, *::before, *::after) {
box-sizing: border-box;
}
:where(body) {
min-height: 100dvh;
font-family: system-ui, sans-serif;
}
:where(img, video, svg) {
display: block;
max-width: 100%;
}
:where(input, button, textarea, select) {
font: inherit;
}
:where(p, h1, h2, h3, h4, h5, h6) {
overflow-wrap: break-word;
}
:is() ve :where() görsel olarak aynı işlevi görür ama farklı özgüllükler üretir. Reset ve temel stil tanımlamalarında :where() tercih edin çünkü üzerine yazmak daha kolaydır.
6. Specificity Savaşlarından Kaçınma Stratejileri
/* Strateji 1: Düşük specificity'de tutun */
/* KÖTÜ */
#page-header .main-nav ul li a.active { color: blue; } /* (0,1,3,3) */
/* İYİ */
.nav__link--active { color: blue; } /* (0,0,1,0) */
/* Strateji 2: @layer kullanın */
@layer base, components, overrides;
@layer base { .button { background: gray; } }
@layer overrides { .button { background: blue; } } /* Layer sayesinde kazanır */
/* Strateji 3: CSS Custom Properties ile tema */
.button {
background: var(--btn-bg, #3b82f6); /* Fallback değer */
color: var(--btn-color, white);
}
/* Override — specificity sorunu yok */
.button-danger {
--btn-bg: #ef4444;
}
/* Strateji 4: :not() ile koşullu stiller */
.list-item:not(.list-item--featured) {
opacity: 0.7;
}
/* Strateji 5: CSS Nesting (modern tarayıcılar) */
.card {
background: white;
& .title { font-size: 1.25rem; } /* (0,0,2,0) */
&:hover { box-shadow: 0 4px 12px #0002; } /* (0,0,2,0) */
}
Sonuç
CSS specificity ve cascade sistemini doğru anlamak, "neden bu stil çalışmıyor" sorularını büyük ölçüde ortadan kaldırır. Specificity'yi düşük tutun, @layer ile cascade'i bilinçli yönetin, :where() ile sıfır özgüllüklü temel stiller yazın ve !important'ı son çare olarak değil, gerçekten zorunlu olduğunda kullanın. Bu prensipler, büyüyen projelerde CSS'in sürdürülebilir kalmasının temelini oluşturur.