Mert Özen Her Satırda Daha İleri
Frontend CSS3: Flexbox, Grid ve Modern CSS — #01

CSS Nasıl Karar Verir? Cascade, Specificity, Inheritance ve Cascade Layers (@layer)

Mert Özen 14 Kas 2026 15 dk 1 görüntülenme
CSS Nasıl Karar Verir? Cascade, Specificity, Inheritance ve Cascade Layers (@layer)

Bir element'e yazılan değerlerden hangisinin kazanacağına cascade karar verir ve sıra şöyledir: origin ve importance, context, style attribute, layer, specificity, kaynak sırası; layer specificity'den önce gelir. Bookstore'un "neden bu buton kırmızı değil?" hatasını DevTools'un kullandığı CDP ile adım adım çözüyor, @layer ile üçüncü parti CSS'i kendi stillerimizin altına alıyor ve inherit/initial/unset/revert/revert-layer farkını iki tarayıcı motorunda ölçüyoruz.

Sürüm notu: Bu yazı CSS Cascading and Inheritance Level 5 spesifikasyonuna ve Baseline'ın Kasım 2026 durumuna göre yazıldı; örnekler Chrome 158 ve WebKit 27.2 (Playwright 1.65) ile denenmiştir, Firefox desteği web-features verisinden alınmıştır.

Önce Tahmin Et

Sitedeki serilerin ortak örnek uygulaması Bookstore (bir online kitapçı) için kitap detay sayfasındayız. Ürün ekibi "Add to cart" butonunun kampanya süresince kırmızı olmasını istiyor. Projede butonların stili üçüncü parti bir UI kit'ten geliyor ve biri bu kit'i sayfaya doğrudan <link> ile eklemiş:

/* Üçüncü parti UI kit: düz (layer'sız) CSS olarak geliyor */
.btn {
    display: inline-flex;
    align-items: center;
    gap: 0.5rem;
    padding: 0.5rem 1rem;
    border: 1px solid transparent;
    border-radius: 0.375rem;
    font-weight: 600;
    cursor: pointer;
}

.btn-primary {
    background-color: #2563eb;
    color: #ffffff;
}

.btn-primary:hover {
    background-color: #1d4ed8;
}

Bizim kendi stillerimiz ise components adında bir cascade layer'ın içinde. İlk denemede .add-to-cart yazıldı, buton mavi kaldı. Specificity yetmiyor diye düşünülüp selector'e bir id eklendi:

<!doctype html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <title>Lab 01: unlayered vendor CSS</title>
    <link rel="stylesheet" href="../css/vendor/ui-kit.css">
    <style>
        @layer components {
            #book-page .add-to-cart {
                background-color: #b91c1c;
            }
        }
    </style>
</head>
<body>
    <main id="book-page">
        <button class="btn btn-primary add-to-cart" type="button">Add to cart</button>
    </main>
</body>
</html>

Bizim selector'ümüzde bir id ve bir class var, kit'inkinde yalnız bir class. Kural da sayfada daha sonra geliyor. Buton şimdi ne renk? Kırmızı mı, mavi mi? Bir sonraki adım olarak !important eklemek mi gerekiyor?

Cevabı aklında tut. Bu yazı CSS3: Flexbox, Grid ve Modern CSS serisinin ilk yazısı ve serinin geri kalanı için zemin: tarayıcı aynı element'e aynı property için birden fazla değer yazıldığında hangisini seçiyor? Bu kararı veren mekanizmaya cascade deniyor. Flexbox ve Grid gibi layout konularına gelmeden önce bunu oturtmak gerekiyor, çünkü "CSS'im çalışmıyor" şikâyetlerinin büyük kısmının sebebi layout değil, cascade'in yanlış bilinmesi.

Cascade'in Sıralama Adımları

Bir element için aynı property'ye birden fazla declaration eşleştiğinde tarayıcı bunları bir dizi kritere göre sıralar ve en üstteki kazanır. Cascade Level 5 spesifikasyonunda kriterler şu sırayla uygulanır: origin ve importance, context, element-attached style'lar, layer'lar, specificity ve son olarak kaynaktaki sıra (order of appearance). Önemli olan şu: bir kriter iki declaration'ı ayırt edebiliyorsa karar orada verilir ve sonraki kriterlere hiç bakılmaz. Specificity yalnız kendinden önceki dört adım berabere kaldığında devreye girer.

01-cascade-steps-tr.png

Tahmin sorusunun cevabı bu sıralamada saklı. İki aday da author origin'inde ve normal (!important'sız), ikisi de aynı document'ta, ikisi de style attribute değil. Dördüncü adım olan layer'da fark çıkıyor: kit'in kuralı hiçbir layer'da değil, bizimki components layer'ında. Unlayered style'lar her zaman bütün layer'ların üstünde sayılır. Karar bu adımda verildi; bizim selector'ün (1,1,0) specificity'si hiç karşılaştırılmadı. Buton mavi kaldı ve id eklemek hiçbir şeyi değiştirmedi.

Origin, Importance ve Style Attribute

İlk adım declaration'ın nereden geldiğine bakar. Üç origin var: tarayıcının kendi varsayılan stilleri (user agent), kullanıcının tarayıcı ayarlarıyla verdiği stiller (user) ve sayfanın yazarı olarak bizim yazdıklarımız (author). Normal declaration'larda sıralama author, user, user agent şeklindedir: bizim yazdığımız tarayıcınınkini yener. !important bu sırayı ters çevirir: important user agent declaration'ı important user'ı, o da important author'u yener. Bu ters çevirme bilinçli: kullanıcının erişilebilirlik için verdiği bir !important ayar, sitenin stilini her zaman yenebilsin diye. Spec'te iki ara origin daha var: çalışan bir CSS animation'ının değerleri normal author declaration'larının üstünde, important olanların altında; transition'daki değerler ise her şeyin üstünde.

İkinci adım context. Shadow DOM (bir component'in markup'ını ve stilini sayfanın geri kalanından ayıran ayrı bir element ağacı) kullanan bir web component'te iki ayrı stil kaynağı var: component'in kendi shadow root'undaki stiller ve dışarıdaki sayfanın stilleri. Normal declaration'larda dışarıdaki kazanır, important olanlarda içerideki. Bunu küçük bir stock-badge custom element'iyle denedim:

<style>
    stock-badge { color: #b91c1c; }
    stock-badge.strict { color: #b91c1c !important; }
</style>
<stock-badge>In stock</stock-badge>
<stock-badge class="strict">In stock</stock-badge>
<script>
    customElements.define('stock-badge', class extends HTMLElement {
        constructor() {
            super();
            this.attachShadow({ mode: 'open' }).innerHTML = `<style>
                :host { color: #15803d; }
                :host(.strict) { color: #15803d !important; }
            </style><slot></slot>`;
        }
    });
</script>
chromium normal  rgb(185, 28, 28) | strict  rgb(21, 128, 61)
webkit normal  rgb(185, 28, 28) | strict  rgb(21, 128, 61)

İlk badge'de dışarıdaki kırmızı, component'in kendi yeşilini yendi; component'i kullanan sayfa onu stillendirebilsin diye. İkincisinde iki taraf da !important ve component'in içi kazandı; component yazarı vazgeçilmez bir kuralı böyle koruyabiliyor. İki tarayıcı motoru da aynı sonucu verdi.

Üçüncü adım element-attached style'lar, pratikte style attribute'u. Eski kaynaklarda inline style'ın specificity'si (1,0,0,0) diye anlatılır. Level 5 spec'i bunu ayrı bir adım olarak tanımlıyor ve bu adım layer'lardan da önce geliyor. Yani hiçbir layer, hiçbir selector normal bir inline style'ı yenemez; yenebilecek tek şey bir !important.

Specificity: (id, class, type) Nasıl Sayılır?

İlk dört adım berabere kalırsa specificity'ye bakılır. Selector'deki parçalar üç sütunda sayılır: id selector'leri birinci sütuna, class'lar, attribute selector'leri ve pseudo-class'lar ikinci sütuna, type selector'leri (button, p) ve pseudo-element'ler üçüncü sütuna. Karşılaştırma soldan sağa yapılır: tek bir id, kaç class olursa olsun class'lardan oluşan her selector'ü yener. * ve combinator'ler (boşluk, >, +, ~) sayılmaz. :is(), :not(), :has() ve specificity'si her zaman sıfır olan :where() ayrı bir hesap istiyor; onları serinin modern selector'ler yazısında ölçeceğiz.

Son adım kaynak sırası: her şey eşitse sonra gelen kazanır. "Sonra" derken document'taki <link> ve <style> sırası, @import'ların yeri ve dosyanın içindeki satır sırası birlikte düşünülür.

Cascade Layers: @layer ile Sırayı Sen Belirle

Specificity'nin sorunu, büyüyen bir projede bir silahlanma yarışına dönmesi: kit'in kuralını yenmek için bir id eklersin, sonra o kuralı yenmek için bir sonraki geliştirici iki class daha ekler, en sonunda !important gelir. Cascade layer'lar bu yarışı bitirmek için var. Mart 2022'den beri Baseline, Eylül 2024'ten beri widely available. Fikir basit: stillerini isimli katmanlara koyarsın ve katmanların öncelik sırasını bir kez, açıkça yazarsın. Katmanlar arasında karar layer sırasıyla verilir; specificity yalnız aynı katmanın içinde konuşur.

Bookstore'un stil dosyalarını serinin geri kalanında kullanacağımız şekilde kurdum. Giriş dosyası yalnız layer sırasını tanımlıyor ve diğer dosyaları içeri alıyor:

/* Layer sırası en başta, bir kez yazılır: sonra gelen layer kazanır */
@layer reset, vendor, base, components, utilities;

@import url("reset.css") layer(reset);
@import url("vendor/ui-kit.css") layer(vendor);
@import url("base.css") layer(base);
@import url("components/button.css") layer(components);
@import url("utilities.css") layer(utilities);

İlk satırdaki @layer ifadesi sıralamayı sabitliyor: reset en düşük, utilities en yüksek öncelikte. Bir layer'ın sırası ilk geçtiği yere göre belirlendiği için bu satırı en başa koymak, dosyaların hangi sırayla yüklendiğinden bağımsız olarak sırayı garanti ediyor. @import'taki layer(vendor) ise üçüncü parti kit'in bütün kurallarını bizim vendor layer'ımıza koyuyor. Kit'in dosyasına dokunmadan onu kendi stillerimizin altına almış olduk. Bizim buton kuralımız artık sade:

.add-to-cart {
    background-color: var(--color-accent);
}

Layer'lar iç içe de olabilir. components'in içinde bir alt katman açmak için nokta kullanılır:

/* nested layer: components.cards, components'in içinde yer alır */
@layer components.cards {
    .book-card {
        border-radius: 0.5rem;
    }
}

İsimsiz layer'lar da var (@layer { … } ya da @import url(…) layer;), ama sıraları yazıldıkları yere göre belirlenir ve sonradan onlara kural eklenemez; bu yüzden bir projede isimli layer'ları tercih ediyorum.

Şimdi tahmin sorusundaki tuzağın kuralını açıkça yazalım: bir layer'a girmemiş her stil, en sonda tanımlanmış görünmez bir layer'daymış gibi davranır ve normal declaration'larda bütün layer'ları yener. Layer kullanmaya başladığın projeye birisi bir kütüphaneyi <link> ile eklerse, o kütüphanenin en zayıf selector'ü bile senin en güçlü layer'lı kuralını yener. HTML'de bugün <link> ile yüklenen bir dosyayı doğrudan bir layer'a koymanın yolu yok; bu yüzden üçüncü parti CSS'i @import … layer() ile içeri almak gerekiyor.

"Neden Bu Buton Kırmızı Değil?" DevTools ile Adım Adım

Gerçek hayatta bu soruyu Chrome DevTools'un Elements panelinde cevaplıyoruz. Adımlar şöyle: butona sağ tıklayıp Inspect de; Styles sekmesinde background-color'ı ara. Styles, kazanan kuralı en üstte gösterir ve kaybeden declaration'ların üstünü çizer. Layer'lı kuralların üstünde @layer components gibi bir başlık görünür; layer'sız kuralların başlığı yoktur. Kurallar önce layer'a göre gruplandığı için mavi rengin layer'sız ui-kit.css'ten geldiğini ve bizim id'li kuralımızın üstünün çizildiğini hemen görürsün. Styles araç çubuğundaki "Toggle CSS layers view" butonu da sayfadaki layer sırasını bir liste olarak açar. Computed sekmesi ise property'nin son değerini ve bu değeri hangi dosyanın hangi satırının verdiğini gösterir. Panelin kendisi Browser DevTools serisinin "Chrome DevTools'a Giriş ve Elements Paneli" yazısında ayrıntılı anlatıldı.

Bu yazıdaki çıktıların gerçek olması için aynı bilgiyi script ile aldım. DevTools, tarayıcıyla CDP (Chrome DevTools Protocol; DevTools'un tarayıcıyla konuştuğu protokol) üzerinden konuşuyor ve Styles sekmesindeki liste CSS.getMatchedStylesForNode komutundan geliyor. Playwright ile aynı komutu çağıran küçük bir yardımcı yazdım:

// Chromium'a (DevTools'un kullandığı protokol CDP üzerinden) bir element'te property'yi hangi kuralların yazdığını sorar
export async function explain(page, selector, property) {
    const cdp = await page.context().newCDPSession(page);
    const sheets = new Map();
    cdp.on('CSS.styleSheetAdded', ({ header }) =>
        sheets.set(header.styleSheetId, header.sourceURL.split('/').pop() || '<style>'));
    await cdp.send('DOM.enable');
    await cdp.send('CSS.enable');
    const { root } = await cdp.send('DOM.getDocument');
    const { nodeId } = await cdp.send('DOM.querySelector', { nodeId: root.nodeId, selector });
    const { matchedCSSRules, inlineStyle } = await cdp.send('CSS.getMatchedStylesForNode', { nodeId });

    const rows = [];
    for (const { rule, matchingSelectors } of matchedCSSRules) {
        const declared = rule.style.cssProperties.filter(p => p.name === property && p.value);
        // Author kurallarında her declaration iki kez gelir (kaynak metin + parse edilmiş); kaynaktakini tut
        const own = declared.some(p => p.text) ? declared.filter(p => p.text) : declared.slice(0, 1);
        for (const p of own) {
            const sel = rule.selectorList.selectors[matchingSelectors[0]];
            const s = sel.specificity;
            rows.push({
                origin: rule.origin,
                layer: rule.layers?.map(l => l.text).join('.') || (rule.origin === 'user-agent' ? '' : '(unlayered)'),
                selector: sel.text,
                specificity: s ? `(${s.a},${s.b},${s.c})` : '',
                value: p.value,
                file: rule.origin === 'user-agent' ? 'user agent' : sheets.get(rule.styleSheetId) ?? '',
                important: p.important
            });
        }
    }
    const inline = (inlineStyle?.cssProperties ?? []).filter(p => p.name === property && p.text);
    for (const p of inline) {
        rows.push({ layer: '', selector: 'style=""', specificity: '', value: p.value, file: 'style attribute', important: p.important });
    }
    const computed = await page.$eval(selector, (el, prop) => getComputedStyle(el).getPropertyValue(prop), property);
    await cdp.detach();
    // satırlar artan cascade sırasında; !important'ta sıra ters döner (önceki layer kazanır)
    const important = rows.filter(r => r.important);
    const normal = rows.filter(r => !r.important).reverse();
    return { computed, rows: [...important, ...normal] };
}

export function print({ computed, rows }, property) {
    console.log(`computed ${property}: ${computed}`);
    rows.forEach((r, i) => {
        console.log(`${i === 0 ? '✓' : '✗'} ${r.layer.padEnd(18)} ${r.selector.padEnd(36)} ${r.specificity.padEnd(8)} ${r.value.padEnd(22)} ${r.file}`);
    });
}
import { open } from './browser.mjs';
import { explain, print } from './cascade-report.mjs';

async function report(file, checks) {
    const { browser, page } = await open(file);
    console.log(`--- ${file}`);
    for (const [selector, property] of checks) {
        print(await explain(page, selector, property), property);
    }
    await browser.close();
}

// 1) Hata: vendor CSS layer'sız link'lenmiş, bizim kural bir layer'ın içinde
await report('lab/01-unlayered-vendor.html', [['.add-to-cart', 'background-color']]);
// 2) Çözüm: vendor kendi, daha alttaki layer'ına import ediliyor
await report('book.html', [['.add-to-cart', 'background-color']]);
// 3) !important layer sırasını ters çevirir; style attribute her layer'ı yener
await report('lab/01-importance.html', [['.stock-note', 'display'], ['.add-to-cart', 'background-color']]);

Örnek projeyi kendi makinende çalıştırmak için proje klasöründe:

npm install
npx playwright install chromium webkit
node scripts/01-cascade.mjs
--- lab/01-unlayered-vendor.html
computed background-color: rgb(37, 99, 235)
✓ (unlayered)        .btn-primary                         (0,1,0)  #2563eb                ui-kit.css
✗ components         #book-page .add-to-cart              (1,1,0)  #b91c1c                01-unlayered-vendor.html
✗                    button                               (0,0,1)  buttonface             user agent
--- book.html
computed background-color: rgb(185, 28, 28)
✓ components         .add-to-cart                         (0,1,0)  var(--color-accent)    button.css
✗ vendor             .btn-primary                         (0,1,0)  #2563eb                ui-kit.css
✗                    button                               (0,0,1)  buttonface             user agent
--- lab/01-importance.html
computed display: none
✓ reset              [hidden]                             (0,1,0)  none !important        reset.css
✗ components         .stock-note                          (0,1,0)  block !important       01-importance.html
✗                    p                                    (0,0,1)  block                  user agent
computed background-color: rgb(21, 128, 61)
✓                    style=""                                      #15803d                style attribute
✗ components         .add-to-cart                         (0,1,0)  var(--color-accent)    button.css
✗ vendor             .btn-primary                         (0,1,0)  #2563eb                ui-kit.css
✗                    button                               (0,0,1)  buttonface             user agent

Satırları DevTools gibi oku: en üstteki kazanan (✓), altındakiler kaybeden (✗); sütunlar layer, eşleşen selector, specificity, değer ve dosya. İlk blok hatayı gösteriyor: (1,1,0) specificity'li kuralımız (0,1,0)'lık .btn-primary'ye kaybetmiş, çünkü .btn-primary'nin layer sütunu (unlayered). İkinci blok çözümden sonrası: kit vendor layer'ında, bizim kural components'te ve ikisinin specificity'si eşit olmasına rağmen kazanan bizimki, buton kırmızı. Id'ye de !important'a da gerek kalmadı.

Üçüncü blok iki kuralı birlikte gösteriyor. reset layer'ındaki [hidden] { display: none !important; }, components'teki display: block !important'ı yendi. Important declaration'larda layer sırası ters döner: en alttaki layer'ın !important'ı en güçlüsüdür. Aynı kural unlayered style'lar için de geçerli; spec'e göre important bir unlayered declaration, important layer'lı bir declaration'a kaybeder. Bu, reset'e yazdığımız "hidden attribute'u olan şey her zaman gizli kalsın" kuralını hiçbir component'in yanlışlıkla bozamayacağı anlamına geliyor. Bloğun son kısmında ise style attribute'undaki yeşil, iki layer'ı da yendi, element-attached adımı layer'dan önce geldiği için.

Inheritance ve CSS-wide Keyword'ler

Cascade bir element'te bir property için hiç declaration bulamazsa iki ihtimal var. Property inherited ise (color, font-family, line-height gibi metinle ilgili olanların çoğu) değeri parent element'ten alır. Inherited değilse (background-color, border, margin gibi kutu ile ilgili olanlar) initial değerini kullanır. Bu davranışı açıkça istemek için her property'de geçerli beş keyword var: inherit, initial, unset, revert ve revert-layer. Farklarını aynı buton üzerinde ölçtüm:

@layer components {
    .buy-box {
        color: #1f2937;
        background-color: #fef3c7;
    }
    .kw-inherit { color: inherit; background-color: inherit; }
    .kw-initial { color: initial; background-color: initial; }
    .kw-unset { color: unset; background-color: unset; }
    .kw-revert { color: revert; background-color: revert; }
    .kw-revert-layer { color: revert-layer; background-color: revert-layer; }
}

Butonların hepsi btn btn-primary class'ını taşıyor, yani vendor layer'ından beyaz yazı ve mavi zemin alıyor. Parent'ları .buy-box koyu gri yazı ve açık sarı zemine sahip:

--- chromium 158.0.8127.18
inherit       color: rgb(31, 41, 55)      background-color: rgb(254, 243, 199)
initial       color: rgb(0, 0, 0)         background-color: rgba(0, 0, 0, 0)
unset         color: rgb(31, 41, 55)      background-color: rgba(0, 0, 0, 0)
revert        color: rgb(0, 0, 0)         background-color: rgb(239, 239, 239)
revert-layer  color: rgb(255, 255, 255)   background-color: rgb(37, 99, 235)
--- webkit 27.2
inherit       color: rgb(31, 41, 55)      background-color: rgb(254, 243, 199)
initial       color: rgb(0, 0, 0)         background-color: rgba(0, 0, 0, 0)
unset         color: rgb(31, 41, 55)      background-color: rgba(0, 0, 0, 0)
revert        color: rgba(0, 0, 0, 0.847) background-color: rgb(192, 192, 192)
revert-layer  color: rgb(255, 255, 255)   background-color: rgb(37, 99, 235)

inherit iki property'de de parent'ın değerini aldı. initial property'nin spec'teki başlangıç değerine döndü: color için siyah, background-color için şeffaf. unset akıllı olanı: inherited property'de inherit, diğerlerinde initial gibi davranıyor; color koyu gri, zemin şeffaf oldu. revert author origin'indeki her şeyi yok sayıp tarayıcının kendi stiline dönüyor ve burada iki motor ayrıştı: Chromium'un varsayılan buton zemini rgb(239, 239, 239), WebKit'inki rgb(192, 192, 192). revert'ün sonucu tarayıcıdan tarayıcıya değişebilir, çünkü her tarayıcının user agent stylesheet'i farklı. revert-layer ise yalnız içinde bulunduğu layer'ı yok sayıyor ve bir alttaki layer'a, yani kit'in beyaz-mavi stiline dönüyor. "Bu component için kendi stilimi geri al, kit'inki kalsın" demenin en temiz yolu bu.

Pratikte en sık kullandığın inherit olacak. Reset dosyamızdaki input, button, select, textarea { font: inherit; } satırı bunun klasik örneği: form element'leri font'u varsayılan olarak inherit etmez ve sayfanın font'unu almaları için bunu açıkça söylemek gerekir.

Üretimde Dikkat

Layer sırası bir mimari karar ve proje başında verilmeli. Bookstore'da seçtiğim reset, vendor, base, components, utilities sırası yaygın bir başlangıç: en genel kurallar altta, en spesifik ve en nadir kullanılması gerekenler üstte. Utility class'larının en üstte olması, .visually-hidden gibi bir yardımcının hiçbir component tarafından ezilmemesini sağlıyor. Tailwind CSS v4 de kendi CSS'ini benzer şekilde native layer'lara koyuyor; ayrıntısı ilerideki Tailwind CSS serisinde.

@import zincirine dikkat. Tarayıcı import edilen dosyayı ancak onu import eden dosyayı indirip okuduktan sonra keşfeder; büyük bir projede bu seri halde indirme demek. Üretimde bu dosyalar build aşamasında tek dosyada birleştirilmeli. Lightning CSS'in bundler'ı gibi araçlar layer() bilgisini koruyarak birleştirebiliyor, ama kullandığın aracın çıktısını mutlaka kontrol et: birleştirilmiş dosyada @layer blokları duruyor mu? Layer bilgisi kaybolursa bütün kit unlayered olur ve bu yazının başındaki hatayı üretimde yaşarsın. CSS yüklemenin performansa etkisi ilerideki Web Performansı serisinde.

Ekip içinde "yeni CSS nereye yazılır" kuralını netleştirin. En sık gördüğüm hata, layer'lı bir projede birinin hızlıca bir <style> bloğu ya da yeni bir CSS dosyası eklemesi ve onu bir layer'a koymayı unutması. O dosyadaki her kural sessizce her şeyi yener. Code review'da layer'sız CSS'i sorgulamak, bu hatanın en ucuz önlemi.

Firefox desteği bu yazının konusu olan her şey için geniş: cascade layer'lar Firefox 97'den, revert 67'den beri var. Bu yazıdaki örnekleri Chromium ve WebKit'te çalıştırdım; Firefox için web-features verisine dayanıyorum.

Sık Düşülen Tuzaklar

Birincisi, !important savaşı. Bir kuralı yenemediğinde !important eklemek o anı kurtarır, ama o kuralı ezmek isteyen bir sonraki kişiyi de !important yazmaya zorlar. Üstelik layer'lı bir projede !important ters çalışır: üst layer'daki !important, alttakine kaybeder. !important'ı yalnız reset'teki [hidden] gibi "asla ezilmesin" denen birkaç kural için sakla.

İkincisi, id selector'leriyle stil vermek. Id'nin specificity'si o kadar yüksek ki, onu ezmek için ya başka bir id ya da !important gerekir. Id'leri link hedefleri, form label'ları ve JavaScript için kullan; stil için class yeterli.

Üçüncüsü, inline style ile yarışmak. Bir component kütüphanesi ya da bir script element'e style attribute'u yazıyorsa, ona karşı hiçbir layer ve hiçbir selector kazanamaz. Ya kaynağı düzelt ya da değeri bir custom property üzerinden geçir; bu tekniği serinin custom property ve theming yazısında göreceğiz.

Dördüncüsü, bir layer'ın sırasını yanlışlıkla belirlemek. Dosyanın ortasında bir @layer utilities { … } bloğu, en üstteki sıralama satırından önce yüklenirse utilities'in sırası orada belirlenir. Sıralama satırını giriş dosyasının ilk satırı yap ve başka yerde yeni layer adı uydurma.

Küçük Bir Deneme

★☆☆ book.html'i Chrome'da aç, "Add to cart" butonunu Inspect et ve Styles sekmesinde @layer başlıklarını bul. Ardından main.css'teki ilk satırda components ile vendor'ın yerini değiştir; sayfayı yenileyip butonun rengini ve Styles'taki üstü çizili kuralı karşılaştır.

★★☆ scripts/01-keywords.mjs'teki ölçümü font-weight ve border-radius için tekrarla. Hangisi inherited, hangisi değil? unset'in iki property'de neden farklı davrandığını sonuçla açıkla.

★★★ Kendi projendeki üçüncü parti CSS'leri listele ve hepsini @import … layer(vendor) ile bir layer'a al. Sonra projedeki !important'ları say; layer'lar kurulduktan sonra kaç tanesini silebildiğini not et.

Bir sonraki yazıda selector'lere geçiyoruz: :is(), :where(), :has() ve :not()'un specificity'yi nasıl etkilediğini aynı yardımcıyla ölçecek, :has() ile kapaklı ve kapaksız kitap kartlarını ayrı stillendireceğiz.