---
title: 'Basamaklama ve Özgüllük'
source: 'https://academia.sh/tr/kurslar/css-temelleri/basamaklama-ve-ozgulluk'
course: 'CSS ile Görsel Sunum'
language: tr
updated: '2026-08-17T18:09:17+00:00'
license: 'CC BY-SA 4.0'
---

# Basamaklama ve Özgüllük

Aynı özelliğe yazan bildirimlerin kaynak, önem, özgüllük ve sıra ölçütleriyle sıralanması; özgüllük üçlüsünün hesabı ve onluk tabanda okunamayacağı.

Önceki derslerin sonunda aynı ögeye birden çok kural uyar hâle geldi. Bir hücre `td`
seçicisiyle, `.olcum-cizelgesi td` seçicisiyle, `td.eksik` seçicisiyle ve `#icerik td`
seçicisiyle birden eşleşiyor. Dördü de `text-align` özelliğine yazarsa ögenin hizalaması ne
olur?

Bu sorunun yanıtı bir sezgi değil, tanımlı bir sıralamadır. Sıralamanın adı
**basamaklama (cascade)**'dır ve dilin adındaki ilk harf ondan gelir. Bu ders
basamaklamanın adımlarını sırayla uygular ve her adımı hesaplanabilir kılar.

## Basamaklama Bildirimleri Sıralar

Önemli bir ayrımla başlamak gerekir: basamaklama kuralları değil, **bildirimleri** sıralar.
Sıralama tek bir öge ve tek bir özellik için yapılır. Aynı kuralın iki bildirimi ayrı ayrı
yarışır; birinin kazanması diğerinin de kazanacağı anlamına gelmez.

Bir öge–özellik çifti için geçerli bildirimler toplandıktan sonra dört ölçüt sırayla
uygulanır. Önceki ölçütte bir kazanan çıkarsa sonraki ölçüte bakılmaz.

1. **Kaynak ve önem.** Bildirimin nereden geldiği ve `!important` taşıyıp taşımadığı.
2. **Satır içi olması.** Ögenin `style` özniteliğinden gelen bildirimler öne geçer.
3. **Özgüllük.** Seçicinin ne kadar dar bir küme hedeflediği.
4. **Sıra.** Aynı özgüllükteki bildirimlerden sonra yazılan kazanır.

Kaynak üç tanedir: tarayıcının kendi varsayılan biçemi, kullanıcının tanımladığı biçem ve
belgenin yazarının yazdığı biçem. Bu üçünün normal sıralaması zayıftan güçlüye şöyledir:
tarayıcı, kullanıcı, yazar.

## Özgüllük Bir Üçlüdür

Özgüllük tek bir sayı değil, üç sayıdan oluşan sıralı bir üçlüdür: $(a, b, c)$.

- $a$: seçicideki kimlik seçicilerinin sayısı.
- $b$: sınıf seçicileri, öznitelik seçicileri ve sözde sınıfların toplam sayısı.
- $c$: tür seçicileri ve sözde elemanların toplam sayısı.

Evrensel seçici `*` ve birleştiriciler hiçbir sütuna katkı vermez.

Aşağıdaki program bir seçici dizesini bu üçlüye çevirir.

```js
// ozgulluk.mjs — bir secici dizesini (a, b, c) uclusune cevirir
function ozgulluk(secici) {
  let s = secici;
  let a = 0, b = 0, c = 0;

  // :where(...) ozgullige katki vermez -> once cikar
  s = s.replace(/:where\([^()]*\)/g, "");

  // :is( ) / :not( ) / :has( ) -> icindeki en yuksek ozgullugu devral
  const kapsayan = /:(?:is|not|has)\(([^()]*)\)/g;
  let m;
  while ((m = kapsayan.exec(s)) !== null) {
    const enYuksek = m[1]
      .split(",")
      .map((p) => ozgulluk(p.trim()))
      .reduce((en, o) => (karsilastir(o, en) > 0 ? o : en), [0, 0, 0]);
    a += enYuksek[0]; b += enYuksek[1]; c += enYuksek[2];
  }
  s = s.replace(kapsayan, "");

  // sozde elemanlar (::x) -> c; sozde siniflar (:x) -> b
  const sozdeEleman = s.match(/::[a-z-]+/g) ?? [];
  c += sozdeEleman.length;
  s = s.replace(/::[a-z-]+/g, "");

  a += (s.match(/#[\w-]+/g) ?? []).length;
  s = s.replace(/#[\w-]+/g, "");

  b += (s.match(/\.[\w-]+/g) ?? []).length;      // sinif
  s = s.replace(/\.[\w-]+/g, "");
  b += (s.match(/\[[^\]]*\]/g) ?? []).length;    // oznitelik
  s = s.replace(/\[[^\]]*\]/g, "");
  b += (s.match(/:[a-z-]+(\([^()]*\))?/g) ?? []).length; // sozde sinif
  s = s.replace(/:[a-z-]+(\([^()]*\))?/g, "");

  c += (s.match(/[a-z][\w-]*/gi) ?? []).length;  // tur secici (* sayilmaz)
  return [a, b, c];
}

const karsilastir = (x, y) => x[0] - y[0] || x[1] - y[1] || x[2] - y[2];

const seciciler = [
  "*",
  "td",
  ".olcum-cizelgesi td",
  "td.eksik",
  "tbody > tr > td",
  ".olcum-cizelgesi tbody td:not(.ad)",
  "input[type=\"number\"]",
  "#icerik td",
  ".olcum-cizelgesi td.deger.eksik",
  "td.eksik::after",
  ":where(.olcum-cizelgesi) td",
  ":is(#icerik, .yan) p",
  "tr:has(td.eksik)",
];

for (const s of seciciler) {
  const [a, b, c] = ozgulluk(s);
  console.log(`${a},${b},${c}   ${s}`);
}
```

```
0,0,0   *
0,0,1   td
0,1,1   .olcum-cizelgesi td
0,1,1   td.eksik
0,0,3   tbody > tr > td
0,2,2   .olcum-cizelgesi tbody td:not(.ad)
0,1,1   input[type="number"]
1,0,1   #icerik td
0,3,1   .olcum-cizelgesi td.deger.eksik
0,1,2   td.eksik::after
0,0,1   :where(.olcum-cizelgesi) td
1,0,1   :is(#icerik, .yan) p
0,1,2   tr:has(td.eksik)
```

Beş satır ayrıca okunmalıdır.

`.olcum-cizelgesi td` ile `td.eksik` **aynı** üçlüyü verdi: her ikisinde bir sınıf ve bir
tür var. Seçicilerin uzunluğu ya da ne kadar "kesin göründüğü" hesaba girmez; yalnızca
sayılar girer.

`:not()`, `:is()` ve `:has()` kendileri hesaba katılmaz; içlerindeki listenin **en yüksek
özgüllüklü** üyesini devralırlar. `:is(#icerik, .yan) p` bu yüzden $1,0,1$ verdi — listede
bir kimlik seçicisi bulunduğu için.

`:where()` ise sıfır katkı verir. `:where(.olcum-cizelgesi) td` üçlüsü, yalnızca `td`
yazılmış gibi $0,0,1$'dir. Bu, bir seçiciyi daraltırken özgüllüğü artırmama yoludur.

Sözde eleman üçüncü sütuna girer: `td.eksik::after` üçlüsünde $c = 2$'dir.

## Üçlü Onluk Tabanda Bir Sayı Değildir

Sık yapılan bir yanlış, üçlüyü yüzler–onlar–birler basamağı gibi okumaktır. Bu okuma on ve
üzeri sayılarda bozulur, çünkü sütunlar arasında elde taşınmaz. Karşılaştırma sözlük
sırasıdır: önce $a$, eşitse $b$, eşitse $c$.

```js
// onluk-degil.mjs — ozgulluk uclusu onluk tabanda bir sayi degildir
const karsilastir = (x, y) => x[0] - y[0] || x[1] - y[1] || x[2] - y[2];
const ciftler = [
  [[0, 11, 0], [1, 0, 0]],
  [[0, 0, 12], [0, 1, 0]],
  [[1, 0, 0], [0, 99, 99]],
];
for (const [x, y] of ciftler) {
  const s = karsilastir(x, y);
  const kazanan = s > 0 ? x : s < 0 ? y : "esit";
  console.log(`${x.join(",")} vs ${y.join(",")} -> kazanan ${Array.isArray(kazanan) ? kazanan.join(",") : kazanan}`);
  console.log(`   onluk okunsaydi: ${x[0]*100+x[1]*10+x[2]} vs ${y[0]*100+y[1]*10+y[2]}`);
}
```

```
0,11,0 vs 1,0,0 -> kazanan 1,0,0
   onluk okunsaydi: 110 vs 100
0,0,12 vs 0,1,0 -> kazanan 0,1,0
   onluk okunsaydi: 12 vs 10
1,0,0 vs 0,99,99 -> kazanan 1,0,0
   onluk okunsaydi: 100 vs 1089
```

İlk satır kritiktir: on bir sınıf taşıyan bir seçici, tek bir kimlik seçicisini yenemez.
Onluk okuma tersini söylerdi. Sonuç şudur: bir kimlik seçicisiyle yazılmış bildirimi
bastırmanın tek yolu, yine bir kimlik seçicisi yazmaktır. Önceki derste "biçem sınıfla
yazılır" kuralının gerekçesi budur.

## Basamaklamanın Uygulanışı

Aşağıdaki program yedi bildirimi tam sıralamaya sokar. Kaynak katmanları zayıftan güçlüye
numaralanmıştır; sıralama ölçütleri sırayla uygulanır.

```js
// basamaklama.mjs — ayni ozellige yazan bildirimleri siralar ve kazanani bulur
// (ozgulluk hesabi onceki programdan; burada elle verilmistir)

// katman sirasi: kucuk sayi = zayif
const KATMAN = {
  "tarayici-normal": 0,
  "kullanici-normal": 1,
  "yazar-normal": 2,
  "yazar-satirici": 3,
  "yazar-onemli": 4,
  "kullanici-onemli": 5,
  "tarayici-onemli": 6,
};

const bildirimler = [
  { kaynak: "tarayici-normal", secici: "td",                       ozgulluk: [0,0,1], sira: 1, deger: "left"   },
  { kaynak: "yazar-normal",    secici: "td",                       ozgulluk: [0,0,1], sira: 2, deger: "left"   },
  { kaynak: "yazar-normal",    secici: ".olcum-cizelgesi td",      ozgulluk: [0,1,1], sira: 3, deger: "right"  },
  { kaynak: "yazar-normal",    secici: "td.ad",                    ozgulluk: [0,1,1], sira: 4, deger: "start"  },
  { kaynak: "yazar-normal",    secici: "#icerik td",               ozgulluk: [1,0,1], sira: 5, deger: "center" },
  { kaynak: "yazar-onemli",    secici: ".olcum-cizelgesi td.eksik",ozgulluk: [0,2,1], sira: 6, deger: "end"    },
  { kaynak: "kullanici-onemli",secici: "td",                       ozgulluk: [0,0,1], sira: 7, deger: "justify"},
];

const karsilastir = (x, y) =>
  KATMAN[x.kaynak] - KATMAN[y.kaynak] ||
  x.ozgulluk[0] - y.ozgulluk[0] ||
  x.ozgulluk[1] - y.ozgulluk[1] ||
  x.ozgulluk[2] - y.ozgulluk[2] ||
  x.sira - y.sira;

function coz(aday) {
  const sirali = [...aday].sort(karsilastir);
  console.log("zayiftan gucluye:");
  for (const b of sirali) {
    const o = b.ozgulluk.join(",");
    console.log(`  ${b.kaynak.padEnd(17)} ${o}  sira=${b.sira}  ${b.secici.padEnd(28)} -> ${b.deger}`);
  }
  console.log(`kazanan: text-align: ${sirali.at(-1).deger}`);
  console.log("");
}

console.log("=== tum bildirimler ===");
coz(bildirimler);

console.log("=== yalnizca yazar-normal ===");
coz(bildirimler.filter((b) => b.kaynak === "yazar-normal"));

console.log("=== ozgulluk esit, sira belirler ===");
coz(bildirimler.filter((b) => b.kaynak === "yazar-normal" && b.ozgulluk.join() === "0,1,1"));
```

```
=== tum bildirimler ===
zayiftan gucluye:
  tarayici-normal   0,0,1  sira=1  td                           -> left
  yazar-normal      0,0,1  sira=2  td                           -> left
  yazar-normal      0,1,1  sira=3  .olcum-cizelgesi td          -> right
  yazar-normal      0,1,1  sira=4  td.ad                        -> start
  yazar-normal      1,0,1  sira=5  #icerik td                   -> center
  yazar-onemli      0,2,1  sira=6  .olcum-cizelgesi td.eksik    -> end
  kullanici-onemli  0,0,1  sira=7  td                           -> justify
kazanan: text-align: justify

=== yalnizca yazar-normal ===
zayiftan gucluye:
  yazar-normal      0,0,1  sira=2  td                           -> left
  yazar-normal      0,1,1  sira=3  .olcum-cizelgesi td          -> right
  yazar-normal      0,1,1  sira=4  td.ad                        -> start
  yazar-normal      1,0,1  sira=5  #icerik td                   -> center
kazanan: text-align: center

=== ozgulluk esit, sira belirler ===
zayiftan gucluye:
  yazar-normal      0,1,1  sira=3  .olcum-cizelgesi td          -> right
  yazar-normal      0,1,1  sira=4  td.ad                        -> start
kazanan: text-align: start
```

İkinci çalıştırma, ölçütlerin **sırayla** uygulandığını gösterir: tek bir kaynakta
kalındığında kazanan özgüllüğe göre belirlendi. Üçüncü çalıştırmada özgüllük de eşitti ve
karar yalnızca yazım sırasına kaldı; sonra yazılan kazandı.

## Önem İşareti Sıralamayı Tersine Çevirir

Bir bildirimin sonuna `!important` yazıldığında bildirim ayrı bir katmana taşınır. Katman
listesindeki sıra dikkatle okunmalıdır: önemli bildirimlerde kaynak sıralaması **ters
döner**. Normal bildirimlerde yazar kullanıcıyı yener; önemli bildirimlerde kullanıcı yazarı
yener.

Bu ters çevirme bir kaza değil, bir tasarım kararıdır. Bir kullanıcının kendi
gereksinimlerini karşılamak üzere yazdığı biçem — yazı boyutunu büyütmek, kontrastı
artırmak, hareketi durdurmak — sayfa yazarının kararlarını geçebilmelidir. Çıktının ilk
bölümünde `kullanici-onemli` katmanının en üstte yer alması bunu gösteriyor.

Yazar tarafında `!important` yazmanın bedeli, kendi biçem dosyasında bir istisna katmanı
açmaktır. Bir kez yazıldığında onu bastırmanın tek yolu yine `!important` yazmaktır ve o
noktada özgüllük hesabı yeniden başlar. Uygulanabilir kural şudur: `!important`, bir çakışma
çözülemediği için değil, çözümü aranmadığı için yazılır; önce özgüllüğü düşürmek denenir.

Kaynak ve önemin arasına yerleşen bir mekanizma daha vardır: `@layer` ile tanımlanan
**basamaklama katmanları**. Yazar biçemi içinde adlandırılmış katmanlar açar ve katman
sırası özgüllükten **önce** değerlendirilir; böylece bir taban katmanının tamamı, özgüllüğü
ne olursa olsun, üstteki katman tarafından bastırılabilir.

## Özgüllüğü Düşük Tutmak

Bu dersin pratik sonucu bir yazım disiplinidir.

- Seçiciler olabildiğince **düz** tutulur. `.olcum-cizelgesi tbody tr td.deger` yerine
  `.olcum-cizelgesi .deger` yazmak, aynı ögeye daha düşük bir üçlüyle ulaşır.
- Biçemde kimlik seçicisi kullanılmaz; ilk sütunu bir kez açan bir kural, sonraki bütün
  düzeltmeleri kimlik yazmaya zorlar.
- Bir seçiciyi bağlamla daraltmak gerekiyorsa `:where()` yazılır; daraltma yapılır,
  özgüllük artmaz.

```css
/* istasyon.css — 6. adım: özgüllüğü düz tutan yazım */
:where(.olcum-cizelgesi) .deger { text-align: right; }   /* 0,1,0 */
:where(.olcum-cizelgesi) .ad    { text-align: start; }   /* 0,1,0 */
.eksik                          { color: #8a1c1c; }      /* 0,1,0 */
```

Üç kural da aynı üçlüyü taşır. Bu, aralarındaki çakışmaların yalnızca yazım sırasıyla
çözüleceği anlamına gelir — okunması ve öngörülmesi en kolay durum budur.

## Özet

- Basamaklama kuralları değil bildirimleri sıralar; sıralama tek bir öge ve tek bir özellik
  için yapılır.
- Ölçütler sırayla uygulanır: kaynak ve önem, satır içi olma, özgüllük, yazım sırası.
  Önceki ölçüt bir kazanan çıkarırsa sonrakine bakılmaz.
- Özgüllük $(a, b, c)$ üçlüsüdür: kimlik sayısı, sınıf–öznitelik–sözde sınıf sayısı,
  tür–sözde eleman sayısı. Karşılaştırma sözlük sırasıdır; sütunlar arasında elde taşınmaz.
- `:is()`, `:not()` ve `:has()` içindeki en yüksek özgüllüğü devralır; `:where()` sıfır
  katkı verir ve daraltmayı özgüllük artırmadan yapmayı sağlar.
- `!important` bildirimi ayrı bir katmana taşır ve kaynak sıralamasını tersine çevirir;
  bu, kullanıcı biçeminin yazar kararlarını geçebilmesi için tanımlanmıştır.

## Sonraki Adım

Basamaklama, bir ögeye **uyan** bildirimler arasından kazananı seçiyor. Peki hiçbir bildirim
uymuyorsa? Bir paragrafa hiçbir kural `color` yazmamışsa o paragrafın rengi ne olur? Sonraki
ders bu boşluğun iki ayrı mekanizmayla doldurulduğunu gösterir — kalıtım ve başlangıç değeri
— ve bu ikisini açıkça çağıran anahtar sözcükleri tanımlar.
