Ders 19 / 23
Özel Özellikler
Özel özelliklerin bildirildiği ögede hesaplanması, kalıtımın hesaplanmış değeri taşıması, yedek değer zincirleri, hesaplanan değerde geçersizlik ve bileşenin biçem arayüzünün kurulması.
İçindekiler
Önceki dersin bileşen dosyasındaki her kural bir ada başvuruyor: var(--cizgi),
var(--uyari), var(--aralik-1). Bu adlar CSS ile Görsel Sunum kursunda kök ögede
tanımlandı ve o kurs için yeterliydi.
Bileşen mimarisi daha fazlasını ister. Bir kart varyantı yalnız zemin rengini değiştirmeli; yan bilgi bölümü kendi renk ölçeğini taşımalı; bir bileşen, dışarıdan hangi değerlerin ayarlanabileceğini bildirmeli. Bu ders özel özelliklere mimari bir araç olarak bakar ve önce hesaplanma anını netleştirir.
Hesaplanma Anı
Özel özellik (custom property) iki tire ile başlayan bir addır ve var() ile okunur.
Belirleyici kuralı, değerinin bildirildiği ögede hesaplanmasıdır.
Bu, kalıtımın ham metni değil hesaplanmış değeri taşıdığı anlamına gelir. Kök ögede
--kart-zemin: var(--zemin) yazıldığında, kökteki --zemin değeri yerine konur ve alt
ağaca giden şey artık o renktir. Bir alt ögede --zemin yeniden tanımlansa bile kökte
hesaplanmış --kart-zemin değişmez.
Aşağıdaki program bunu bir ağaç üzerinde çözüyor.
// ozelozellik.mjs — ozel ozelliklerin hesaplanma ani, kalitim, yedek deger ve gecersizlik davranisi // Kural: bir ozel ozelligin degeri, BILDIRILDIGI ogede hesaplanir; alt agaca hesaplanmis // deger kalitilir. Alt agacta bir kaynak ozelligin yeniden tanimlanmasi, ustte hesaplanmis // bir tureve geriye donuk etki etmez. const AGAC = { ad: ":root", bildirim: { "--zemin": "hsl(203 18% 97%)", "--metin": "hsl(211 29% 16%)", "--kart-zemin": "var(--zemin)", "--aralik-1": "1.5rem" }, cocuk: [{ ad: ".yan-bilgi", bildirim: { "--zemin": "hsl(203 18% 92%)" }, // yalniz --zemin yeniden tanimli cocuk: [{ ad: ".olcum-kart", bildirim: {}, cocuk: [ { ad: ".olcum-kart--uyari", bildirim: { "--kart-zemin": "hsl(0 66% 96%)" }, cocuk: [] }, { ad: ".olcum-kart--sade", bildirim: { "--kart-zemin": "yanlis-deger" }, cocuk: [] }, { ad: ".olcum-kart--yerel", bildirim: { "--kart-zemin": "var(--zemin)" }, cocuk: [] }, ], }], }], }; const VAR_KALIBI = /var\(\s*(--[\w-]+)\s*(?:,\s*([^()]*(?:\([^()]*\)[^()]*)*))?\)/; // metin icindeki var() basvurularini coz; ara(ad) bir ozel ozelligin degerini dondurur function coz(metin, ara) { let s = metin, adim = 0; while (VAR_KALIBI.test(s)) { if (++adim > 50) return { deger: null, neden: "dongu" }; const [tam, ad, yedek] = s.match(VAR_KALIBI); const bulunan = ara(ad); if (bulunan && bulunan.neden) return bulunan; // dongu yukari tasinir if (!bulunan || bulunan.deger === null) { if (yedek === undefined) return { deger: null, neden: `${ad} tanimsiz, yedek yok` }; s = s.replace(tam, yedek); } else { s = s.replace(tam, bulunan.deger); } } return { deger: s.trim(), neden: null }; } // bir ogenin ozel ozellik haritasi: kalitilan harita + kendi bildirimlerinin hesaplanmasi function harita(dugum, kalitilan) { const sonuc = { ...kalitilan }; const cozuluyor = new Set(); const ara = (ad) => { if (ad in dugum.bildirim) { if (cozuluyor.has(ad)) return { deger: null, neden: "dongu" }; cozuluyor.add(ad); const c = coz(dugum.bildirim[ad], ara); cozuluyor.delete(ad); return c; } return ad in kalitilan ? { deger: kalitilan[ad], neden: null } : null; }; for (const ad of Object.keys(dugum.bildirim)) { const c = ara(ad); sonuc[ad] = c.deger; // gecersizse null kalir } return sonuc; } const GECERLI_RENK = /^(hsl\(|rgb\(|#)/; function gez(dugum, kalitilan = {}, yol = []) { const h = harita(dugum, kalitilan); const ad = [...yol, dugum.ad]; const c = coz("var(--kart-zemin, var(--zemin))", (n) => (n in h ? { deger: h[n], neden: null } : null)); let sonuc; if (c.deger === null) sonuc = `GECERSIZ (${c.neden})`; else if (!GECERLI_RENK.test(c.deger)) sonuc = `"${c.deger}" -> hesaplanan degerde gecersiz`; else sonuc = c.deger; console.log(" " + ad.join(" > ").padEnd(62) + sonuc); for (const c2 of dugum.cocuk) gez(c2, h, ad); } console.log("--- agacta background-color: var(--kart-zemin, var(--zemin)) ---"); gez(AGAC); console.log("\n--- kok ogedeki yedek deger zincirleri ---"); const KOK = harita(AGAC, {}); const kokAra = (n) => (n in KOK ? { deger: KOK[n], neden: null } : null); for (const yazim of [ "var(--zemin)", "var(--yok)", "var(--yok, var(--zemin))", "var(--yok, var(--de-yok, hsl(0 0% 100%)))", "calc(var(--aralik-1) * 2)", ]) { const c = coz(yazim, kokAra); console.log(` ${yazim.padEnd(42)} -> ${c.deger === null ? "GECERSIZ: " + c.neden : c.deger}`); } console.log("\n--- dongu ---"); const D = { ad: ":root", bildirim: { "--a": "var(--b)", "--b": "var(--a)" }, cocuk: [] }; console.log(" --a: var(--b); --b: var(--a); -> --a degeri:", harita(D, {})["--a"]);
--- agacta background-color: var(--kart-zemin, var(--zemin)) --- :root hsl(203 18% 97%) :root > .yan-bilgi hsl(203 18% 97%) :root > .yan-bilgi > .olcum-kart hsl(203 18% 97%) :root > .yan-bilgi > .olcum-kart > .olcum-kart--uyari hsl(0 66% 96%) :root > .yan-bilgi > .olcum-kart > .olcum-kart--sade "yanlis-deger" -> hesaplanan degerde gecersiz :root > .yan-bilgi > .olcum-kart > .olcum-kart--yerel hsl(203 18% 92%) --- kok ogedeki yedek deger zincirleri --- var(--zemin) -> hsl(203 18% 97%) var(--yok) -> GECERSIZ: --yok tanimsiz, yedek yok var(--yok, var(--zemin)) -> hsl(203 18% 97%) var(--yok, var(--de-yok, hsl(0 0% 100%))) -> hsl(0 0% 100%) calc(var(--aralik-1) * 2) -> calc(1.5rem * 2) --- dongu --- --a: var(--b); --b: var(--a); -> --a degeri: null
İkinci satır dersin en sık atlanan noktasıdır. .yan-bilgi ögesi --zemin değerini
değiştiriyor, ama zemini hsl(203 18% 97%) kalıyor — çünkü okuduğu --kart-zemin, kök
ögede zaten hesaplanmıştır.
Son satır çözümü gösteriyor: --kart-zemin alt ağaçta yeniden bildirildiğinde, var(--zemin)
o ögede çözülür ve hsl(203 18% 92%) çıkar. Kural şudur: türev bir değerin bağlamdan
etkilenmesi isteniyorsa, türev o bağlamda bildirilmelidir.
calc(var(--aralik-1) * 2) satırı bir başka ayrıntıyı gösteriyor: yerine koyma yalnız
metin düzeyindedir. Değerin anlamlı olup olmadığı, yerine koyma bittikten sonra sınanır.
Yedek Değer ve Geçersizlik
var() ikinci bir argüman alır: adın tanımsız olduğu durumda kullanılacak yedek değer.
Yedek değerler iç içe yazılabilir ve çıktının ikinci bloğu iki katlı bir zinciri çözüyor.
Yedeğin devreye girmediği bir durum vardır ve tuzak oradadır: ad tanımlıysa ama değeri
kullanıldığı yerde anlamsızsa, yedek çalışmaz. --kart-zemin: yanlis-deger bildirimi
tanımlıdır; background-color bu değeri alır ve o noktada geçersiz olur.
Bu duruma hesaplanan değerde geçersizlik (invalid at computed-value time) denir ve sonucu, ayrıştırma sırasındaki geçersizlikten farklıdır:
- Ayrıştırma sırasında geçersiz bir bildirim atılır; aynı özelliğe yazan önceki bildirim yerinde kalır.
- Hesaplanan değerde geçersiz bir bildirim atılmaz; özelliği
unsetdurumuna getirir. Yani kalıtılan özelliklerde kalıtılan değer, kalıtılmayanlarda başlangıç değeri geçerli olur.
Önceki kural yerinde kalmadığı için sonuç genellikle beklenmedik olur. Bir renk bildirimi yanlış bir özel özellik yüzünden düştüğünde, ögenin rengi ana ögesinden gelir.
Döngü ayrı bir durumdur: birbirine başvuran iki özel özellik hiçbir değer üretmez ve ikisi de tanımsız sayılır.
Kayıtlı Özellikler
@property kuralı bir özel özelliği kaydeder: değerinin sözdizimini, kalıtılıp
kalıtılmayacağını ve başlangıç değerini bildirir.
@property --kart-zemin { syntax: "<color>"; inherits: true; initial-value: hsl(203 18% 97%); }
Kaydın üç sonucu vardır. Birincisi tip denetimidir: bildirilen sözdizimine uymayan bir değer
kaydın başlangıç değerine düşer, hesaplanan değerde geçersizlik oluşmaz. İkincisi kalıtımın
denetlenebilmesidir; inherits: false yazılan bir özellik yalnız bildirildiği ögede geçerli
olur. Üçüncüsü hareketle ilgilidir: kayıtsız bir özel özellik yalnız metindir ve ara değeri
hesaplanamaz, kayıtlı bir renk ya da uzunluk ise geçişe ve anahtar karelere girebilir.
Üçüncü sonuç, önceki konudaki hareket bildirimleriyle birleşir: bir renk ölçeğinin kendisi animasyona sokulabilir hâle gelir.
Bileşenin Biçem Arayüzü
Buraya kadarki kurallar bir mimari kalıba varır. Bileşen, dışarıya açtığı ayar noktalarını özel özellik olarak tanımlar; içeride yalnız bu adları okur.
.olcum-kart { --kart-zemin: var(--zemin); --kart-cerceve: var(--cizgi); --kart-vurgu-yolu: -8px; background-color: var(--kart-zemin); border: 1px solid var(--kart-cerceve); } .olcum-kart:hover { transform: translateY(var(--kart-vurgu-yolu)); } .olcum-kart--uyari { --kart-zemin: hsl(0 66% 96%); --kart-cerceve: var(--uyari); } .olcum-kart--durgun { --kart-vurgu-yolu: 0px; }
Değiştiriciler artık bildirim ezmiyor, değer veriyor. .olcum-kart--uyari kuralı
background-color yazmıyor; yalnız bileşenin okuduğu adı değiştiriyor. Bunun üç sonucu var:
özgüllük yarışı çıkmaz, varyant sayısı arttıkça kural sayısı artmaz ve bileşenin neyin
ayarlanabilir olduğu dosyanın ilk satırlarından okunur.
Ad öneki de bir sözleşmedir: --kart- ile başlayanlar bileşenin arayüzüdür, --zemin gibi
öneksiz adlar sayfa ölçeğinden gelir. İkisi karışırsa bileşenin dışarıdan ne beklediği
belirsizleşir.
Tema Yönetimi
Tema, aynı arayüzü farklı değerlerle doldurmaktır. İki katmanlı bir ölçek bunu ayırır: ham değerleri tutan adlar ve işlevi adlandıran adlar. Tema yalnız ikinci katmanı değiştirir.
:root { --gri-95: hsl(203 18% 97%); --gri-15: hsl(211 29% 16%); --zemin: var(--gri-95); --metin: var(--gri-15); } [data-tema="koyu"] { --zemin: var(--gri-15); --metin: var(--gri-95); }
Tema bir öznitelikle bildirildiği için alt ağaç ölçeğinde de uygulanabilir: sayfanın tamamı açık, tek bir bölüm koyu olabilir. Kalıtım bunu kendiliğinden sağlar; ek bir kural gerekmez.
Aynı yapı ortam sorgusuyla da sürülebilir — kullanıcının renk şeması tercihi, öznitelik yerine sorgu içinde aynı iki bildirimi yazar. İki kaynak birlikte kullanılıyorsa hangisinin öncelikli olduğu bir karardır: kullanıcının sayfa içinde yaptığı seçim genellikle sistem tercihini ezer, çünkü daha sonra ve daha özgül yazılır.
Özet
- Özel özellik bildirildiği ögede hesaplanır; alt ağaca hesaplanmış değer kalıtılır, ham metin değil.
- Bir türev değerin bağlamdan etkilenmesi için o bağlamda yeniden bildirilmesi gerekir; kaynak adı alt ağaçta değiştirmek üstte hesaplanmış türevi değiştirmez.
- Yedek değer yalnız ad tanımsızsa çalışır; tanımlı ama anlamsız bir değer hesaplanan
değerde geçersizlik üretir ve özelliği
unsetdurumuna getirir. @propertykaydı sözdizimi, kalıtım ve başlangıç değeri bildirir; kayıtlı özellikler tip denetimi kazanır ve geçişe girebilir.- Bileşen, ayar noktalarını önekli özel özellik olarak açtığında varyantlar bildirim ezmek yerine değer verir; özgüllük düz kalır ve arayüz okunur olur.
Sonraki Adım
Buradaki bütün adlar tarayıcıda, çalışma zamanında çözülüyor. Bir başka araç sınıfı aynı işlerin bir bölümünü daha belge tarayıcıya ulaşmadan yapar: iç içe yazımı düzleştirmek, yinelenen blokları tek yerde tutmak, dosyaları birleştirmek. Sonraki ders derleme zamanında çalışan biçem araçlarını ele alır ve hangi işin çalışma zamanına bırakılması gerektiğini ayırır.
İlerlemeni kaydetmek ve not almak için Giriş yap
Notlarım
Not almak için giriş yapmalısın.