İçeriğe geç
academia.sh

Ders 11 / 19

Tema Yönetimi

Temaların kaynak tablodan tek kuralla türetilmesi, aynalamanın koruduğu ve kaçırdığı sözleşmeler, düzeltmenin istisna listesi olarak tutulması, marka temasının tek aileyle sınanması ve rol–tema kapsamının ölçülmesi.

İçindekiler

Temel katman önceki derste tamamlandı: renk, tipografi, aralık, boyut ve düzen kararları adlandırılmış ve türetilebilir durumda. Geriye Renk Belirteçleri dersinden kalan boşluk kalıyor. Orada yirmi rolün yalnız sekizinin koyu tema karşılığı vardı ve eksik hücreler elle doldurulmayı bekliyordu.

Elle doldurma iki nedenle işe yaramaz. Birincisi, her yeni rol her temada bir kez daha yazılmak zorundadır ve bu iş rol sayısıyla tema sayısının çarpımı kadar büyür. İkincisi, elle yazılan değer bir gerekçe taşımaz: bir rolün koyu temada niçin o basamağa düştüğü kayıtlı değildir. Bu ders temayı bir değer listesi olarak değil, kaynak temadan türetilen bir tablo olarak kurar ve türetmenin nerede yetmediğini ölçer.

Tema Bir Türetme Kuralıdır

Bir tema, anlamsal katmanın her rolüne bir ilkel değer bağlayan tablodur. İki tema arasındaki fark, bu bağlamanın kuralıdır. Koyu tema için en yalın kural basamak aynalamasıdır: açıklık rampasının iki ucu yer değiştirir, 000 ile 900, 050 ile 800 eşleşir. Yüzey en açıktan en koyuya, metin en koyudan en açığa geçer; rol adları ve sözleşme değişmez.

Kuralın değeri, tek bir satırın bütün rolleri taşımasıdır. Yeni bir rol eklendiğinde koyu karşılığı ayrıca yazılmaz; kural onu da kapsar. Kuralın sınırı ise ölçülerek bulunur: aynalama kontrast oranını korumaz, çünkü açıklık rampası algılanan parlaklık ekseninde bakışımlı değildir.

Aşağıdaki betik iki sözleşmeyi de her temada çalıştırır: eşik sözleşmesi (ön plan–zemin çiftlerinin en az kaç kontrast oranı taşıyacağı) ve durum ayrımı sözleşmesi (bir durum rolünün taban rolünden ayırt edilebilmesi için gereken en küçük oran, Etkileşim Durumları dersinde 1.2 olarak benimsenmişti).

// tema.mjs — temalarin tek kuraldan turetilmesi ve sozlesmenin her temada denetlenmesi

function hslRgb(h, s, l) {
  s /= 100; l /= 100;
  const k = (n) => (n + h / 30) % 12;
  const a = s * Math.min(l, 1 - l);
  const f = (n) => l - a * Math.max(-1, Math.min(k(n) - 3, Math.min(9 - k(n), 1)));
  return [f(0), f(8), f(4)].map((v) => Math.round(v * 255));
}
const onaltilik = (rgb) => "#" + rgb.map((v) => v.toString(16).padStart(2, "0")).join("");
function kanal(v) {
  const s = v / 255;
  return s <= 0.03928 ? s / 12.92 : Math.pow((s + 0.055) / 1.055, 2.4);
}
const parlaklik = ([r, g, b]) => 0.2126 * kanal(r) + 0.7152 * kanal(g) + 0.0722 * kanal(b);
function kontrast(a, b) {
  const [x, y] = [parlaklik(a), parlaklik(b)].sort((p, q) => q - p);
  return (x + 0.05) / (y + 0.05);
}

// Ilkel katman (Renk Belirtecleri dersindeki rampa).
const ACIKLIK = { "000": 100, "050": 97, 100: 92, 200: 84, 300: 74, 400: 62, 500: 50, 600: 40, 700: 31, 800: 22, 900: 14 };
const AILE = { notr: [214, 8], ana: [214, 62], basari: [145, 55], uyari: [38, 85], hata: [8, 68] };

// Anlamsal katman: rol -> [aile, basamak]. Acik tema kaynagidir.
const ACIK = {
  yuzey: ["notr", "000"], "yuzey-ikincil": ["notr", "050"], "yuzey-secili": ["ana", "100"],
  kenarlik: ["notr", "500"], "metin-birincil": ["notr", "900"], "metin-ikincil": ["notr", "600"],
  "eylem-ana": ["ana", "600"], "eylem-ana-uzeri": ["notr", "000"], "eylem-ana-uzerine": ["ana", "700"],
  "eylem-ana-basili": ["ana", "800"], "eylem-devre-disi": ["notr", "200"],
  "eylem-devre-disi-uzeri": ["notr", "500"], "odak-halkasi": ["ana", "600"],
  "basari-zemin": ["basari", "100"], "basari-metin": ["basari", "800"],
  "uyari-zemin": ["uyari", "100"], "uyari-metin": ["uyari", "800"],
  "hata-zemin": ["hata", "100"], "hata-metin": ["hata", "700"],
};

// Sozlesme: on plan, zemin, esik.
const SOZLESME = [
  ["metin-birincil", "yuzey", 4.5], ["metin-birincil", "yuzey-ikincil", 4.5],
  ["metin-ikincil", "yuzey", 4.5], ["metin-birincil", "yuzey-secili", 4.5],
  ["kenarlik", "yuzey", 3.0], ["eylem-ana", "yuzey", 3.0],
  ["eylem-ana-uzeri", "eylem-ana", 4.5], ["eylem-ana-uzeri", "eylem-ana-uzerine", 4.5],
  ["eylem-ana-uzeri", "eylem-ana-basili", 4.5], ["odak-halkasi", "yuzey", 3.0],
  ["basari-metin", "basari-zemin", 4.5], ["uyari-metin", "uyari-zemin", 4.5],
  ["hata-metin", "hata-zemin", 4.5],
];

// Turetme kurali 1: basamak aynalamasi. Aciklik rampasi ters cevrilir.
const AYNA = { "000": "900", "050": "800", 100: "700", 200: "600", 300: "500", 400: "400", 500: "300", 600: "200", 700: "100", 800: "050", 900: "000" };
const aynala = (kaynak) =>
  Object.fromEntries(Object.entries(kaynak).map(([rol, [aile, b]]) => [rol, [aile, AYNA[b]]]));

// Turetme kurali 2: marka temasi. Yalniz "ana" ailesinin ton ve doygunlugu degisir.
const markaAilesi = (ton, doygunluk) => ({ ...AILE, ana: [ton, doygunluk] });

function denetle(rolTablosu, aileTablosu) {
  const renk = (rol) => {
    const [aile, b] = rolTablosu[rol];
    return hslRgb(aileTablosu[aile][0], aileTablosu[aile][1], ACIKLIK[b]);
  };
  return SOZLESME.map(([on, zemin, esik]) => {
    const k = kontrast(renk(on), renk(zemin));
    return { on, zemin, esik, k, gecti: k >= esik };
  });
}
const kalanlar = (sonuc) => sonuc.filter((s) => !s.gecti);
const yaz = (baslik, sonuc) => {
  console.log(`\n${baslik}`);
  console.log("on plan                  zemin                  oran   esik  sonuc");
  for (const s of sonuc) {
    console.log(
      `${s.on.padEnd(24)} ${s.zemin.padEnd(22)} ${s.k.toFixed(2).padStart(5)} ${s.esik.toFixed(1).padStart(6)}  ${s.gecti ? "gecti" : "KALDI"}`
    );
  }
  console.log(`esigi gecemeyen: ${kalanlar(sonuc).length} / ${sonuc.length}`);
};

// Ikinci sozlesme: durum turevleri tabandan ayirt edilebilmeli (Etkilesim Durumlari: 1.2).
const AYRIM = [
  ["eylem-ana-uzerine", "eylem-ana"], ["eylem-ana-basili", "eylem-ana-uzerine"],
  ["yuzey-ikincil", "yuzey"], ["eylem-devre-disi", "yuzey"],
];
const AYRIM_ESIGI = 1.2;
function ayrimDenetle(rolTablosu, aileTablosu) {
  const renk = (rol) => {
    const [aile, b] = rolTablosu[rol];
    return hslRgb(aileTablosu[aile][0], aileTablosu[aile][1], ACIKLIK[b]);
  };
  return AYRIM.map(([durum, taban]) => {
    const k = kontrast(renk(durum), renk(taban));
    return { durum, taban, k, gecti: k >= AYRIM_ESIGI };
  });
}
const ayrimYaz = (baslik, sonuc) => {
  console.log(`\n${baslik}`);
  console.log("durum rolu               taban rol              oran   esik  sonuc");
  for (const s of sonuc) {
    console.log(
      `${s.durum.padEnd(24)} ${s.taban.padEnd(22)} ${s.k.toFixed(3).padStart(5)} ${AYRIM_ESIGI.toFixed(1).padStart(6)}  ${s.gecti ? "gecti" : "KALDI"}`
    );
  }
  console.log(`ayirt edilemeyen: ${sonuc.filter((s) => !s.gecti).length} / ${sonuc.length}`);
};

console.log("--- 1. acik tema (kaynak) ---");
yaz("acik tema sozlesmesi", denetle(ACIK, AILE));
ayrimYaz("acik tema durum ayrimi", ayrimDenetle(ACIK, AILE));

console.log("\n--- 2. aynalamayla turetilen koyu tema ---");
const KOYU = aynala(ACIK);
yaz("koyu tema sozlesmesi (ham turetme)", denetle(KOYU, AILE));
ayrimYaz("koyu tema durum ayrimi (ham turetme)", ayrimDenetle(KOYU, AILE));

console.log("\n--- 3. koyu temada duzeltme ---");
// Aynalama, yalniz kalan eslesmelerin basamagini kaydirir; kural degil istisna listesidir.
const KOYU_DUZELTME = {
  kenarlik: ["notr", "400"],
  "eylem-ana": ["ana", "300"],
  "eylem-ana-uzeri": ["notr", "900"],
  "eylem-ana-uzerine": ["ana", "200"],
  "eylem-ana-basili": ["ana", "100"],
  "odak-halkasi": ["ana", "300"],
};
const KOYU2 = { ...KOYU, ...KOYU_DUZELTME };
yaz("koyu tema sozlesmesi (duzeltmeden sonra)", denetle(KOYU2, AILE));
ayrimYaz("koyu tema durum ayrimi (duzeltmeden sonra)", ayrimDenetle(KOYU2, AILE));
console.log(`duzeltme sayisi: ${Object.keys(KOYU_DUZELTME).length} rol / ${Object.keys(ACIK).length}`);

console.log("\n--- 4. marka temasi: yalniz ana ailenin tonu degisir ---");
for (const [ad, ton, doygunluk] of [["mor", 276, 62], ["yesil", 145, 62], ["turuncu", 28, 62]]) {
  const aileler = markaAilesi(ton, doygunluk);
  const acik = kalanlar(denetle(ACIK, aileler));
  const koyu = kalanlar(denetle(KOYU2, aileler));
  console.log(
    `${ad.padEnd(8)} ton ${String(ton).padStart(3)}  acik temada kalan: ${acik.length}  koyu temada kalan: ${koyu.length}` +
      (acik.length ? "  (" + acik.map((s) => s.on + "/" + s.zemin).join(", ") + ")" : "")
  );
}

console.log("\n--- 5. kapsam: rol x tema hucreleri ---");
const TEMALAR = { acik: ACIK, koyu: KOYU2 };
let eksik = 0;
for (const rol of Object.keys(ACIK)) {
  for (const [ad, tablo] of Object.entries(TEMALAR)) if (!(rol in tablo)) { eksik++; console.log(`  eksik: ${rol} / ${ad}`); }
}
console.log(`tanimli hucre: ${Object.keys(ACIK).length * Object.keys(TEMALAR).length - eksik} / ${Object.keys(ACIK).length * Object.keys(TEMALAR).length}`);

console.log("\n--- 6. bicem cikti (ilk dort rol) ---");
const bicem = (secici, tablo, aileler) =>
  [`${secici} {`]
    .concat(Object.entries(tablo).slice(0, 4).map(([rol, [aile, b]]) =>
      `  --renk-${rol}: ${onaltilik(hslRgb(aileler[aile][0], aileler[aile][1], ACIKLIK[b]))};`))
    .concat(["}"]).join("\n");
console.log(bicem(":root", ACIK, AILE));
console.log(bicem('[data-tema="koyu"]', KOYU2, AILE));
--- 1. acik tema (kaynak) ---

acik tema sozlesmesi
on plan                  zemin                  oran   esik  sonuc
metin-birincil           yuzey                  15.74    4.5  gecti
metin-birincil           yuzey-ikincil          14.70    4.5  gecti
metin-ikincil            yuzey                   5.89    4.5  gecti
metin-birincil           yuzey-secili           12.82    4.5  gecti
kenarlik                 yuzey                   4.11    3.0  gecti
eylem-ana                yuzey                   6.50    3.0  gecti
eylem-ana-uzeri          eylem-ana               6.50    4.5  gecti
eylem-ana-uzeri          eylem-ana-uzerine       9.05    4.5  gecti
eylem-ana-uzeri          eylem-ana-basili       12.68    4.5  gecti
odak-halkasi             yuzey                   6.50    3.0  gecti
basari-metin             basari-zemin            7.54    4.5  gecti
uyari-metin              uyari-zemin             7.57    4.5  gecti
hata-metin               hata-zemin              7.21    4.5  gecti
esigi gecemeyen: 0 / 13

acik tema durum ayrimi
durum rolu               taban rol              oran   esik  sonuc
eylem-ana-uzerine        eylem-ana              1.393    1.2  gecti
eylem-ana-basili         eylem-ana-uzerine      1.401    1.2  gecti
yuzey-ikincil            yuzey                  1.071    1.2  KALDI
eylem-devre-disi         yuzey                  1.459    1.2  gecti
ayirt edilemeyen: 1 / 4

--- 2. aynalamayla turetilen koyu tema ---

koyu tema sozlesmesi (ham turetme)
on plan                  zemin                  oran   esik  sonuc
metin-birincil           yuzey                  15.74    4.5  gecti
metin-birincil           yuzey-ikincil          11.80    4.5  gecti
metin-ikincil            yuzey                  10.78    4.5  gecti
metin-birincil           yuzey-secili            9.05    4.5  gecti
kenarlik                 yuzey                   8.23    3.0  gecti
eylem-ana                yuzey                  10.29    3.0  gecti
eylem-ana-uzeri          eylem-ana              10.29    4.5  gecti
eylem-ana-uzeri          eylem-ana-uzerine      12.82    4.5  gecti
eylem-ana-uzeri          eylem-ana-basili       14.63    4.5  gecti
odak-halkasi             yuzey                  10.29    3.0  gecti
basari-metin             basari-zemin            5.02    4.5  gecti
uyari-metin              uyari-zemin             5.09    4.5  gecti
hata-metin               hata-zemin              7.21    4.5  gecti
esigi gecemeyen: 0 / 13

koyu tema durum ayrimi (ham turetme)
durum rolu               taban rol              oran   esik  sonuc
eylem-ana-uzerine        eylem-ana              1.246    1.2  gecti
eylem-ana-basili         eylem-ana-uzerine      1.141    1.2  KALDI
yuzey-ikincil            yuzey                  1.333    1.2  gecti
eylem-devre-disi         yuzey                  2.670    1.2  gecti
ayirt edilemeyen: 1 / 4

--- 3. koyu temada duzeltme ---

koyu tema sozlesmesi (duzeltmeden sonra)
on plan                  zemin                  oran   esik  sonuc
metin-birincil           yuzey                  15.74    4.5  gecti
metin-birincil           yuzey-ikincil          11.80    4.5  gecti
metin-ikincil            yuzey                  10.78    4.5  gecti
metin-birincil           yuzey-secili            9.05    4.5  gecti
kenarlik                 yuzey                   5.75    3.0  gecti
eylem-ana                yuzey                   7.63    3.0  gecti
eylem-ana-uzeri          eylem-ana               7.63    4.5  gecti
eylem-ana-uzeri          eylem-ana-uzerine      10.29    4.5  gecti
eylem-ana-uzeri          eylem-ana-basili       12.82    4.5  gecti
odak-halkasi             yuzey                   7.63    3.0  gecti
basari-metin             basari-zemin            5.02    4.5  gecti
uyari-metin              uyari-zemin             5.09    4.5  gecti
hata-metin               hata-zemin              7.21    4.5  gecti
esigi gecemeyen: 0 / 13

koyu tema durum ayrimi (duzeltmeden sonra)
durum rolu               taban rol              oran   esik  sonuc
eylem-ana-uzerine        eylem-ana              1.349    1.2  gecti
eylem-ana-basili         eylem-ana-uzerine      1.246    1.2  gecti
yuzey-ikincil            yuzey                  1.333    1.2  gecti
eylem-devre-disi         yuzey                  2.670    1.2  gecti
ayirt edilemeyen: 0 / 4
duzeltme sayisi: 6 rol / 19

--- 4. marka temasi: yalniz ana ailenin tonu degisir ---
mor      ton 276  acik temada kalan: 0  koyu temada kalan: 0
yesil    ton 145  acik temada kalan: 1  koyu temada kalan: 0  (eylem-ana-uzeri/eylem-ana)
turuncu  ton  28  acik temada kalan: 0  koyu temada kalan: 0

--- 5. kapsam: rol x tema hucreleri ---
tanimli hucre: 38 / 38

--- 6. bicem cikti (ilk dort rol) ---
:root {
  --renk-yuzey: #ffffff;
  --renk-yuzey-ikincil: #f7f7f8;
  --renk-yuzey-secili: #dee9f7;
  --renk-kenarlik: #757e8a;
}
[data-tema="koyu"] {
  --renk-yuzey: #212327;
  --renk-yuzey-ikincil: #34383d;
  --renk-yuzey-secili: #1e4980;
  --renk-kenarlik: #969da6;
}

Aynalama Neyi Geçer, Neyi Kaçırır

Birinci bölüm açık temanın durumunu tekrarlar: eşik sözleşmesinin on üç eşleşmesi de geçiyor, durum ayrımında ise ikincil yüzey 1.071 oranla kalıyor. Bu, Renk Belirteçleri dersinde saptanan kusurdur ve burada bilinçli olarak korunmuştur — türetmenin kaynağı kusursuz bir tablo değil, gerçek tablodur.

İkinci bölüm ham aynalamanın sonucudur ve iki şey gösterir. Eşik sözleşmesinin tamamı koyu temada da geçiyor; dahası oranlar genellikle yükseliyor. İkincil yüzeyin açık temada geçemediği ayrım eşiği koyu temada 1.333 ile geçiyor: aynı kural, aynı roller, farklı sonuç. Bir temada doğrulanmış bir kararın diğerinde de doğru olduğunu varsaymanın neden yanlış olduğu buradan görünür.

Aynı bölüm kuralın kaçırdığı yeri de gösteriyor. Basılı durum ile üzerine gelme durumu arasındaki oran koyu temada 1.141’e düşüyor ve eşiğin altında kalıyor. Nedeni rampanın yapısıdır: aynalama 700 ve 800 basamaklarını 100 ve 050’ye taşır, bu iki basamak arasındaki açıklık farkı ise diğer uçtakinden küçüktür. Kural bozuk değildir; kuralın varsayımı — rampanın bakışımlı olması — yalnızca yaklaşık doğrudur.

Düzeltme Bir İstisna Listesidir

Üçüncü bölüm düzeltmeyi uygular. Düzeltme, kuralı değiştirmez; kuralın ürettiği tablonun üzerine yazılan altı satırlık bir istisna listesidir. Altı rol on dokuz rolün yaklaşık üçte biridir ve bu oran kuralın işe yaradığının kanıtıdır: on üç rol hiçbir müdahale olmadan doğru değeri aldı.

İstisnanın liste olarak tutulması iki şey sağlar. Denetim, istisnaların da sözleşmeden geçtiğini gösterir — düzeltmeden sonra iki sözleşme de sıfır ihlalle kapanıyor. Gözden geçirme ise listeye bakarak kuralın nerede yetmediğini görür; bu bilgi bir sonraki rampa düzenlemesinin girdisidir. Değerler tabloya tek tek yazılsaydı bu ayrım kaybolurdu: hangi değerin kuraldan, hangisinin karardan geldiği görünmez olurdu.

Marka Teması: Değişen Tek Ailenin Sınanması

Dördüncü bölüm ikinci türetme kuralını uygular. Marka teması, rol tablosunu hiç değiştirmeden yalnız ana ailesinin tonunu ve doygunluğunu değiştirir. Rol adları, basamak bağlantıları ve sözleşme aynı kalır; değişen tek şey ilkel katmandaki iki sayıdır.

Sonuç, marka renginin serbest bir seçim olmadığını gösteriyor. Mor ve turuncu tonlar iki temada da sözleşmeyi geçiyor. Yeşil ton açık temada bir eşleşmeyi düşürüyor: birincil eylemin üzerindeki metin, yeşil 600 basamağının üzerinde 4.5 eşiğini tutturamıyor. Bu bir tasarım tercihi değil, ölçülebilir bir kısıttır ve iki yolu vardır — ya eylem rolü daha koyu bir basamağa bağlanır ya da eylem üzeri metni ailenin kendi açık ucundan seçilir. Hangisi seçilirse seçilsin, karar marka temasının belirtecine yazılır ve denetim bir sonraki koşumda bunu doğrular.

Kapsam ve Çıktı

Beşinci bölüm kapsamı sayar: on dokuz rol ile iki temanın çarpımı otuz sekiz hücredir ve hepsi tanımlıdır. Renk Belirteçleri dersindeki on iki boş hücre, elle doldurularak değil kural yazılarak kapandı. Kapsam ölçümü bir tema eklendiğinde de aynı biçimde çalışır: yeni temanın kuralı yazıldığı anda bütün roller karşılığını alır.

Altıncı bölüm belirteç kaynağından biçem çıktısını üretir. Temalar arasındaki fark tek bir yerde toplanır: kök seçicide açık temanın değerleri, tema özniteliğini taşıyan seçicide koyu temanın değerleri. Bileşenler hiçbir tema bilgisi taşımaz; yalnız rol adını okurlar. Bu, Biçem Mimarisi dersinde kurulan özel özellik kalıbının sistem düzeyindeki karşılığıdır.

İşletim sistemi tercihine uyum bu yapının üzerine tek bir ortam sorgusuyla eklenir; tercihin kendisi Kullanıcı Tercihleri dersinde ele alınmıştı. Sistemin sorumluluğu tercihi okumak değil, tercihin karşılığı olan tablonun eksiksiz ve denetlenmiş olmasıdır.

Özet

  • Tema, rol başına yazılmış bir değer listesi değil, kaynak tablodan türetilen bir eşlemedir; kural bir kez yazılır ve yeni roller onu kendiliğinden izler.
  • Aynalama kuralı eşik sözleşmesini korur ama kontrast oranlarını korumaz: açık temada ayrım eşiğini geçemeyen ikincil yüzey koyu temada geçiyor, basılı durum ise tersine 1.141’e düşüp kalıyor. Her sözleşme her temada yeniden çalıştırılır.
  • Düzeltme kuralı değiştirmez; kuralın üzerine yazılan bir istisna listesidir. On dokuz rolün altısı düzeltildi, on üçü kuraldan geldi ve hangisinin nereden geldiği kayıtlıdır.
  • Marka teması yalnız bir ailenin ton ve doygunluğunu değiştirir; bu iki sayı sözleşmeyi düşürebilir — yeşil tonda birincil eylem üzerindeki metin açık temada eşiği tutturamıyor.
  • Kapsam, rol ve tema kümelerinin çarpımı üzerinden ölçülür: otuz sekiz hücrenin tamamı tanımlı. Biçem çıktısında temalar arası fark tek seçicide toplanır, bileşenler yalnız rol adını okur.

Sonraki Adım

Temel katman bu dersle kapanıyor: değerler adlandırıldı, ölçeklere bağlandı ve temalar tek kuraldan türetilip denetlendi. Ancak bir sistemin kullanıcısı belirteci değil bileşeni kullanır. Ödünç alma düğmesini yazan kişi --renk-eylem-ana değerini değil, “birincil düğme” bileşenini arar; bulamazsa kendi düğmesini yazar ve sistemin kapsamı sessizce daralır. Sonraki konu bu katmanı ele alır ve ilk dersi kataloğun kendisiyle başlar: hangi bileşenler sisteme girer, bir bileşenin olgunluk düzeyi nasıl ölçülür ve kataloğa girmeyen bir ihtiyaç nasıl karşılanır?

İ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