İçeriğe geç
academia.sh

Ders 07 / 26

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ğı.

İçindekiler

Ö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, b, c).

  • aa: seçicideki kimlik seçicilerinin sayısı.
  • bb: sınıf seçicileri, öznitelik seçicileri ve sözde sınıfların toplam sayısı.
  • cc: 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.

// 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,11,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,10,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=2c = 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 aa, eşitse bb, eşitse cc.

// 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.

// 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.
/* 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)(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.

İlerlemeni kaydetmek ve not almak için Giriş yap

Notlarım

Not almak için giriş yapmalısın.

Aramak için yazmaya başlayın.

↑↓ Esc gezin · aç · kapat