---
title: 'Tasarım Sistemleri'
source: 'https://academia.sh/tr/kurslar/tasarim-sistemleri'
language: tr
updated: '2026-08-17T04:41:12+00:00'
license: 'CC BY-SA 4.0'
lessons: 19
outcomes:
    - 'Tasarım dilini belirteçlere (token) çevirir'
    - 'Bileşen kataloğunu belge ve sürüm disipliniyle yönetir'
    - 'Sistem benimsenmesini ölçer ve yönetişimini kurar'
---

# Tasarım Sistemleri

Tekrarlanan tasarım kararlarını belirteçlere, bileşen kataloğuna ve yönetişim kurallarına dönüştürerek ölçeklenebilir ve denetlenebilir bir arayüz dili kuran kurs.

## Sistemin Kurulması

Tasarım sisteminin bileşen kütüphanesinden farkı, sistem ihtiyacının maliyet hesabıyla gerekçelendirilmesi, görsel denetim, iki başlangıç stratejisi ve tasarım dilinin yazılması.

### Tasarım Sistemi Nedir

Source: https://academia.sh/tr/kurslar/tasarim-sistemleri/tasarim-sistemi-nedir

Tasarım sisteminin bileşen kütüphanesinden farkı; kararların değer, bileşen, kalıp ve kural katmanlarına ayrılması ve bir kütüphanenin bu kararların kaçını yanıtladığının hesaplanması.

Buraya kadarki iki kurs katalog arayüzünün kararlarını tek tek doğru vermeyi öğretti.
Arayüz Tasarımının Temelleri kursu ızgarayı, aralık ölçeğini, tipografik ölçeği ve renk
rollerini kurdu; Kullanıcı Deneyimi ve Davranış Tasarımı kursu bu kararların hangi
araştırma bulgusuna dayandığını ve sonuçlarının nasıl ölçüleceğini gösterdi. Her iki
kursta da birim tek bir karardı: bu boşluk kaç piksel, bu akış kaç adım, bu değişiklik
görev süresini düşürdü mü.

Bir karar doğru verilebilir. Aynı kararın ikinci, onuncu ve yüzüncü kez aynı biçimde
verilmesi ayrı bir sorundur ve tek tek doğruluk bu sorunu çözmez. Katalog arayüzü artık
dört ekrandan ibaret değil: yönetim paneli, üye profili, raf yerleşim ekranı ve mobil
uygulama aynı kurumun ürünleri olarak aynı dili konuşmak zorunda. Bu ders, tekrarın kendisini
bir tasarım nesnesi hâline getiren yapıyı tanımlar ve onu en sık karıştırıldığı şeyden —
bileşen kütüphanesinden — ayırır.

#### Sistem, Kararların Adlandırılmış Hâlidir

**Tasarım sistemi (design system)**, bir kurumun arayüz kararlarını yeniden verilmek yerine
başvurulacak biçimde adlandıran, belgeleyen ve dağıtan yapıdır. Tanımın üç yüklemi de
zorunludur ve birbirinin yerine geçmez.

**Adlandırma** kararı bir addan okunabilir kılar. "Ödünç al düğmesinin zemini nötr 600
üzerine ana 600" bir karardır; `--eylem-ana` bir addır. Ad kullanıldığı sürece karar
yeniden verilmez, başvurulur.

**Belgeleme** kararın gerekçesini adla birlikte taşır. Renk Sistemi dersinde birincil ve
ikincil eylemin ton farkıyla ayrılamayacağı hesapla gösterilmişti. Bu bulgu yazılmazsa,
altı ay sonra bir başkası ikincil eyleme mor bir dolgu verir ve aynı hesabı baştan yapmak
zorunda kalır.

**Dağıtım** kararı kullanılabilir kılar. Bir tasarım dosyasında duran, kimsenin
kuramadığı bir ölçek karar değil temennidir.

**Bileşen kütüphanesi (component library)** ise bu yapının yalnız bir teslim biçimidir:
yeniden kullanılabilir arayüz parçalarının paketlenmiş hâli. Kütüphane sistemin çıktısıdır;
sistemin kendisi değildir. Aradaki farkı sezmenin en hızlı yolu, arayüzün gerçekten kaç
karar taşıdığını saymaktır.

#### Kararlar Dört Katmanda Durur

Katalog arayüzünün dört ekranında verilmiş kararlar dört katmana ayrılır. **Değer** katmanı
hangi ham büyüklüğün kullanılacağını söyler: boyut basamağı, renk rolü, boşluk adımı.
**Bileşen** katmanı tek bir parçanın iç yapısını tanımlar: düğmenin iç boşluğu, metin
alanının kenarlığı. **Kalıp** katmanı birden çok bileşenin bir arada nasıl dizildiğini
belirler: süzgeç paneliyle sonuç listesinin oranı, form alanı ile etiketinin ilişkisi.
**Kural** katmanı ise her yerde geçerli davranış sözleşmeleridir: yıkıcı eylem onay ister,
odak göstergesi kaldırılamaz.

Bu ayrımın burada kullanılan adı **arayüz kalıbı (interface pattern)** ile karışmasın diye
netleştirilir: kalıp, bileşenlerin arasındaki dizilişin adıdır; tek bir bileşenin iç
yapısı bileşen katmanına aittir.

Aşağıdaki program bu dört katmanı sayar ve bir bileşen kütüphanesinin hangilerini
yanıtladığını hesaplar.

```js
// kapsam.mjs — bir bilesen kutuphanesinin katalog arayuzundeki kararlarin kacini yanitladigi

// Katalog arayuzunun dort ekraninda fiilen verilmis kararlar.
// katman: deger  -> hangi ham degerin kullanilacagi (renk, aralik, boyut)
// katman: bilesen -> tek bir bilesenin ic yapisi
// katman: kalip   -> birden cok bilesenin bir arada nasil dizildigi
// katman: kural   -> her yerde gecerli davranis kurali
const KARARLAR = [
  { ad: "govde metni boyutu", katman: "deger", ekran: ["arama", "sonuc", "ayrinti", "onay"] },
  { ad: "baslik boyut basamagi", katman: "deger", ekran: ["arama", "sonuc", "ayrinti", "onay"] },
  { ad: "birincil eylem zemini", katman: "deger", ekran: ["arama", "sonuc", "ayrinti", "onay"] },
  { ad: "kenarlik rengi", katman: "deger", ekran: ["arama", "sonuc", "ayrinti"] },
  { ad: "kart ic boslugu", katman: "deger", ekran: ["sonuc", "ayrinti"] },
  { ad: "grup ici bosluk", katman: "deger", ekran: ["arama", "sonuc", "ayrinti", "onay"] },
  { ad: "kose yaricapi", katman: "deger", ekran: ["arama", "sonuc", "ayrinti", "onay"] },
  { ad: "odak halkasi kalinligi", katman: "deger", ekran: ["arama", "sonuc", "ayrinti", "onay"] },

  { ad: "dugme ic yapisi", katman: "bilesen", ekran: ["arama", "sonuc", "ayrinti", "onay"] },
  { ad: "metin alani ic yapisi", katman: "bilesen", ekran: ["arama"] },
  { ad: "kayit karti ic yapisi", katman: "bilesen", ekran: ["sonuc"] },
  { ad: "etiket ic yapisi", katman: "bilesen", ekran: ["sonuc", "ayrinti"] },
  { ad: "acilir katman ic yapisi", katman: "bilesen", ekran: ["onay"] },
  { ad: "sayfalama ic yapisi", katman: "bilesen", ekran: ["sonuc"] },

  { ad: "suzgec paneli ile liste orani", katman: "kalip", ekran: ["sonuc"] },
  { ad: "bos sonuc ekrani dizilisi", katman: "kalip", ekran: ["sonuc"] },
  { ad: "form alani ve etiket dizilisi", katman: "kalip", ekran: ["arama", "onay"] },
  { ad: "onay eyleminin yeri", katman: "kalip", ekran: ["onay"] },
  { ad: "kayit ustverisinin sirasi", katman: "kalip", ekran: ["sonuc", "ayrinti"] },

  { ad: "yikici eylem onay ister", katman: "kural", ekran: ["ayrinti", "onay"] },
  { ad: "odak gostergesi kaldirilamaz", katman: "kural", ekran: ["arama", "sonuc", "ayrinti", "onay"] },
  { ad: "hata iletisi alanin altinda", katman: "kural", ekran: ["arama", "onay"] },
  { ad: "yukleme durumu yer tutar", katman: "kural", ekran: ["sonuc", "ayrinti"] },
  { ad: "tarih bicimi", katman: "kural", ekran: ["sonuc", "ayrinti", "onay"] },
];

// Bir bilesen kutuphanesi yalniz "bilesen" katmanini paketler.
const KUTUPHANE_KATMANI = new Set(["bilesen"]);

const katmanlar = ["deger", "bilesen", "kalip", "kural"];
console.log("katman   karar sayisi  toplam kullanim  kutuphane yanitliyor mu");
let toplamKarar = 0;
let toplamKullanim = 0;
let kapsanan = 0;
for (const k of katmanlar) {
  const grup = KARARLAR.filter((d) => d.katman === k);
  const kullanim = grup.reduce((t, d) => t + d.ekran.length, 0);
  toplamKarar += grup.length;
  toplamKullanim += kullanim;
  if (KUTUPHANE_KATMANI.has(k)) kapsanan += grup.length;
  console.log(
    `${k.padEnd(8)} ${String(grup.length).padStart(12)} ${String(kullanim).padStart(16)}  ${KUTUPHANE_KATMANI.has(k) ? "evet" : "hayir"}`
  );
}
console.log(`toplam   ${String(toplamKarar).padStart(12)} ${String(toplamKullanim).padStart(16)}`);
console.log(`kutuphanenin kapsadigi karar orani: ${((100 * kapsanan) / toplamKarar).toFixed(1)}%`);

// Deger katmani adlandirilmadiginda: her deger, onu kullanan her bilesende yeniden yazilir.
const BILESENLER = KARARLAR.filter((d) => d.katman === "bilesen");
const degerKullanimi = {
  "govde metni boyutu": ["dugme ic yapisi", "metin alani ic yapisi", "kayit karti ic yapisi", "etiket ic yapisi", "acilir katman ic yapisi", "sayfalama ic yapisi"],
  "baslik boyut basamagi": ["kayit karti ic yapisi", "acilir katman ic yapisi"],
  "birincil eylem zemini": ["dugme ic yapisi", "acilir katman ic yapisi"],
  "kenarlik rengi": ["metin alani ic yapisi", "kayit karti ic yapisi", "etiket ic yapisi", "acilir katman ic yapisi"],
  "kart ic boslugu": ["kayit karti ic yapisi", "acilir katman ic yapisi"],
  "grup ici bosluk": ["dugme ic yapisi", "metin alani ic yapisi", "kayit karti ic yapisi", "etiket ic yapisi", "acilir katman ic yapisi", "sayfalama ic yapisi"],
  "kose yaricapi": ["dugme ic yapisi", "metin alani ic yapisi", "kayit karti ic yapisi", "etiket ic yapisi", "acilir katman ic yapisi"],
  "odak halkasi kalinligi": ["dugme ic yapisi", "metin alani ic yapisi", "kayit karti ic yapisi", "sayfalama ic yapisi"],
};

console.log("\ndeger karari             adlandirilmamis: kac bilesen  adlandirilmis: kac yer");
let adsiz = 0;
for (const [ad, kullananlar] of Object.entries(degerKullanimi)) {
  adsiz += kullananlar.length;
  console.log(`${ad.padEnd(24)} ${String(kullananlar.length).padStart(25)} ${"1".padStart(23)}`);
}
console.log(`${"toplam".padEnd(24)} ${String(adsiz).padStart(25)} ${String(Object.keys(degerKullanimi).length).padStart(23)}`);
console.log(`bilesen sayisi: ${BILESENLER.length}`);
console.log(`degisiklik yeri orani: ${(adsiz / Object.keys(degerKullanimi).length).toFixed(2)} kat`);

console.log("\ntek bir kararin degismesi (ornek: kose yaricapi 4 -> 8)");
console.log(`  adlandirilmamis: ${degerKullanimi["kose yaricapi"].length} bilesen dosyasi degisir`);
console.log("  adlandirilmis  : 1 belirtec tanimi degisir, bilesen dosyalari degismez");
```

```
katman   karar sayisi  toplam kullanim  kutuphane yanitliyor mu
deger               8               29  hayir
bilesen             6               10  evet
kalip               5                7  hayir
kural               5               13  hayir
toplam             24               59
kutuphanenin kapsadigi karar orani: 25.0%

deger karari             adlandirilmamis: kac bilesen  adlandirilmis: kac yer
govde metni boyutu                               6                       1
baslik boyut basamagi                            2                       1
birincil eylem zemini                            2                       1
kenarlik rengi                                   4                       1
kart ic boslugu                                  2                       1
grup ici bosluk                                  6                       1
kose yaricapi                                    5                       1
odak halkasi kalinligi                           4                       1
toplam                                          31                       8
bilesen sayisi: 6
degisiklik yeri orani: 3.88 kat

tek bir kararin degismesi (ornek: kose yaricapi 4 -> 8)
  adlandirilmamis: 5 bilesen dosyasi degisir
  adlandirilmis  : 1 belirtec tanimi degisir, bilesen dosyalari degismez
```

#### Kütüphane Kararların Dörtte Birini Kapsar

İlk tablo dersin ana bulgusunu veriyor. Yirmi dört karardan altısı bileşen katmanında;
yüzde 25. Bir bileşen kütüphanesi eksiksiz teslim edilse bile, arayüzün kararlarının dörtte
üçü hâlâ her ekranda yeniden verilir.

Bu oranın kendisi de yanıltıcı biçimde iyimser. İkinci sütun kullanım sayılarını gösteriyor:
altı bileşen toplam 10 kez kullanılıyor, sekiz değer kararı ise 29 kez. Değer katmanı hem
daha az sayıda hem de daha sık başvurulan kararlardan oluşur. Sık başvurulan bir kararın
tek yerde tutulmaması, tutarsızlığın en verimli üreticisidir.

Kural katmanı ayrı bir uyarı taşır. Beş kural 13 kez uygulanıyor ve hiçbiri bir bileşen
dosyasında yaşamıyor. "Yıkıcı eylem onay ister" kuralı bir düğmenin içine yazılamaz; düğme,
kendisine tıklandığında ne olacağını bilmez. Bu kurallar yalnız belge olarak var olabilir
ve belgelenmediklerinde her ekranda yeniden keşfedilirler.

#### Adlandırma Değişiklik Yerlerini Teke İndirir

İkinci tablo, değer katmanının adlandırılmamasının maliyetini sayıyor. Sekiz değer kararı,
altı bileşenin içinde toplam 31 ayrı yerde geçiyor. Aynı sekiz karar adlandırıldığında
tanım sayısı 8'e iner: değişiklik yeri sayısı 3.88 kat azalır.

Son blok bunu tek bir değişiklik üzerinden somutlaştırıyor. Köşe yarıçapının 4 pikselden
8 piksele çıkarılması, değer adlandırılmamışsa beş ayrı bileşen dosyasında beş ayrı düzenleme
demektir. Bu düzenlemelerin hepsinin bulunması gerekir; bulunamayan biri arayüzde tek başına
kalır ve tutarsızlığı üretir. Ad kullanıldığında değişen yer tektir ve bileşenler değişikliği
kendiliğinden devralır.

Bu, Özel Özellikler dersinde kurulan mekanizmanın sistem düzeyindeki gerekçesidir. Orada
bileşenin dışarıya açtığı ayar noktaları özel özellik olarak tanımlanıyordu; buradaki hesap,
o mimarinin neden bir kolaylık değil bir zorunluluk olduğunu gösteriyor.

#### Sistemin Kütüphaneden Beş Farkı

Sayılar tanımı keskinleştiriyor. Bir bileşen kütüphanesinin tasarım sistemine dönüşmesi için
beş şey daha gerekir.

**Değerler adlandırılır.** Bileşenlerin okuduğu sayılar ve renkler ayrı bir katmanda,
bileşenlerden bağımsız olarak tanımlanır. Bu katmanın adı **tasarım belirteci (design
token)** olacak ve sonraki konunun tamamını kaplayacak.

**Kalıplar yazılır.** İki bileşenin yan yana nasıl duracağı bileşenlerin içine yazılamaz;
ayrı bir belge olarak durur.

**Kurallar sözleşme hâline gelir.** Her yerde geçerli davranışlar, ihlal edildiğinde
gösterilebilir bir metne dayanmalıdır.

**Karar gerekçeleri saklanır.** Bir eşiğin neden 4.5 olduğu, bir oranın neden 1.25 seçildiği
kararla birlikte durur. Gerekçesiz karar, bir sonraki gözden geçirmede yeniden tartışılır.

**Değişim yönetilir.** Bir belirtecin değeri değiştiğinde onu kullanan her yerin etkilendiğini
gördük; bu, gücün kendisidir ve aynı zamanda riskin kendisidir. Sürümleme, katkı süreci ve
yönetişim bu riskin karşılığıdır.

#### Sistem Bir Ürün Değildir, Bir Ürün Ekidir

Son bir ayrım gerekir. Tasarım sistemi kendi başına kullanıcıya değer üretmez; ürünler üretir.
Sistemin ölçüsü kendi eksiksizliği değil, ürünlerin ondan ne kadar yararlandığıdır.

Bunun pratik sonucu, sistemin kendi başarısını **benimseme (adoption)** üzerinden ölçmesidir:
kaç ürün, kaç ekran, kararların yüzde kaçı sistemden geliyor. Yüzde 100 eksiksiz ama hiçbir
üründe kullanılmayan bir sistem, yüzde 40 eksik ama her ekranda kullanılan bir sistemden daha
başarısızdır. Benimseme ölçümü bu kursun son konusunda ayrıca ele alınır; burada gerekli olan,
sistemin varlık gerekçesinin kendisinde değil kullanımında olduğudur.

Bu ayrım kapsam kararını da belirler. Sistem, ürünlerin gerçekten tekrarladığı kararları
kapsar; tek bir ekranda bir kez geçen bir düzen, sisteme değil o ekrana aittir. Kapsamın
nasıl çizileceği bir sonraki dersin konusu olan gerekçelendirme hesabına bağlıdır.

#### Özet

- Tasarım sistemi, tekrarlanan arayüz kararlarının adlandırılmış, belgelenmiş ve dağıtılmış
  hâlidir; bileşen kütüphanesi bu yapının yalnız bir teslim biçimidir.
- Arayüz kararları dört katmanda durur: değer, bileşen, kalıp ve kural. Bileşen kütüphanesi
  yalnız bir katmanı paketler.
- Ölçülen arayüzde 24 karardan 6'sı bileşen katmanındadır; kütüphanenin kapsadığı oran yüzde
  25'tir ve değer katmanı hem daha az kararla hem daha çok kullanımla en sık başvurulan
  katmandır.
- Adlandırılmamış sekiz değer kararı altı bileşenin içinde 31 yerde geçer; adlandırma
  değişiklik yeri sayısını 3.88 kat azaltır.
- Kural katmanı hiçbir bileşen dosyasında yaşayamaz; yalnız belge olarak var olur ve
  belgelenmediğinde her ekranda yeniden keşfedilir.
- Sistemin ölçüsü kendi eksiksizliği değil ürünlerdeki benimsenmesidir; kapsam, ürünlerin
  gerçekten tekrarladığı kararlarla sınırlanır.

#### Sonraki Adım

Bu ders bir sistemin ne olduğunu tanımladı ve kütüphanenin kapsamadığı alanı ölçtü. Ancak
kapsanmayan bir alanın varlığı, o alanı kapatmanın karlı olduğunu kanıtlamaz. Sistem kurmak
bir maliyettir: belirteçlerin tanımlanması, bileşenlerin yazılması, belgenin sürdürülmesi ve
mevcut ekranların geçirilmesi zaman ister. Sonraki ders bu maliyeti ekran sayısı, ekip sayısı
ve değişiklik sıklığı cinsinden hesaplar; sistemli ve sistemsiz gidişatın hangi ekran sayısında
kesiştiğini bulur ve sistemin gerekçelendirilemediği durumları da adlandırır.

### Sistem İhtiyacının Gerekçelendirilmesi

Source: https://academia.sh/tr/kurslar/tasarim-sistemleri/sistem-ihtiyacinin-gerekcelendirilmesi

Sistemli ve sistemsiz gidişatın saat cinsinden karşılaştırılması, başabaş ekran sayısının hesaplanması, girdilere duyarlılık, ekip sayısının sapmaya etkisi ve tek bir kararın sisteme girme eşiği.

Önceki ders bir bileşen kütüphanesinin arayüz kararlarının yalnız dörtte birini kapsadığını
ölçtü. Kapsanmayan üç çeyreğin varlığı bir boşluğu gösterir; boşluğu doldurmanın karlı
olduğunu göstermez. Sistem kurmak bedava değildir: belirteç katmanının tanımlanması,
bileşenlerin yazılması, belgenin sürdürülmesi ve mevcut ekranların geçirilmesi ölçülebilir
bir emek ister.

Bu ders o emeği, karşılığında kurtarılan emekle aynı birimde karşılaştırır. Sorulan soru
"tutarlılık iyi midir" değildir; tutarlılığın iyi olduğu Yinelenme ve Tutarlılık dersinde
zaten gösterildi. Sorulan soru şudur: kaç ekrandan sonra sistem kendini öder, hangi girdi bu
noktayı en çok oynatır ve hangi koşullarda sistem kurmamak doğru karardır.

#### İki Gidişat, Tek Birim

Karşılaştırma için iki gidişat aynı birimde yazılır. **Sistemsiz gidişat**ta her yeni ekran,
sistemde karşılığı olmayan kararları sıfırdan verir; ayrıca zamanla bir **tutarsızlık borcu**
birikir, çünkü aynı karar farklı ekranlarda farklı yanıtlanır ve bu sapmaların sonradan
bulunup birleştirilmesi gerekir. **Sistemli gidişat**ta bir kurulum maliyeti peşin ödenir;
sonrasında kararların çoğuna başvurulur, küçük bir bölümü gerçekten yeni olur ve her ekran
sisteme bir bakım payı bindirir.

Girdiler bir kurumun kendi ölçümünden gelir. Aşağıdaki sayılar bu katalog kurumunun
ölçümüdür; başka bir kurumda başka sayılar çıkar. Taşınabilir olan hesabın kendisidir.

```js
// gerekce.mjs — sistemli ve sistemsiz gidisatin maliyet karsilastirmasi ve basabas noktasi

// Girdiler saat cinsindendir ve bu kurumun kendi olcumunden gelir; baska bir kurumda
// baska sayilar cikar. Hesabin kendisi tasinabilir, sayilar degildir.
const G = {
  kararYeni: 0.7,      // bir karari sifirdan vermenin maliyeti (secenek uretme + gozden gecirme + uygulama)
  kararBasvuru: 0.1,   // var olan bir karara basvurmanin maliyeti
  ekranBasinaKarar: 18, // Tasarim Sistemi Nedir dersinde: 24 karardan 18'i deger/kalip/kural katmaninda
  yeniKararOrani: 0.15, // sistemli gidisatta bile her ekranda gercekten yeni olan karar orani
  kurulum: 90,         // belirtec katmani + bilesen + belge + dagitim
  bakimOrani: 0.08,    // her ekran icin sisteme geri besleme bakim maliyeti (saat)
  sapmaOlasiligi: 0.2, // bir karar, yeni bir ekranda bagimsiz olarak yeniden verilirse sapma olasiligi
  sapmaDuzeltme: 1.5,  // sapmis bir degerin sonradan bulunup birlestirilmesi (arama + degisiklik + sinama)
};

function sistemsiz(ekran, g = G) {
  const karar = ekran * g.ekranBasinaKarar * g.kararYeni;
  // Her karar icin beklenen ayri deger sayisi: ilk ekran 1, sonraki her ekran p olasilikla yeni deger.
  const ayriDeger = 1 + (ekran - 1) * g.sapmaOlasiligi;
  const borc = g.ekranBasinaKarar * (ayriDeger - 1) * g.sapmaDuzeltme;
  return { karar, borc, toplam: karar + borc, ayriDeger };
}

function sistemli(ekran, g = G) {
  const yeni = ekran * g.ekranBasinaKarar * g.yeniKararOrani * g.kararYeni;
  const basvuru = ekran * g.ekranBasinaKarar * (1 - g.yeniKararOrani) * g.kararBasvuru;
  const bakim = ekran * g.bakimOrani * g.ekranBasinaKarar;
  return { kurulum: g.kurulum, yeni, basvuru, bakim, toplam: g.kurulum + yeni + basvuru + bakim };
}

console.log("ekran  sistemsiz karar  tutarsizlik borcu  sistemsiz toplam  sistemli toplam  fark");
for (const e of [1, 2, 3, 5, 8, 10, 15, 20, 30, 50]) {
  const a = sistemsiz(e);
  const b = sistemli(e);
  console.log(
    `${String(e).padStart(5)} ${a.karar.toFixed(1).padStart(16)} ${a.borc.toFixed(1).padStart(18)} ` +
      `${a.toplam.toFixed(1).padStart(17)} ${b.toplam.toFixed(1).padStart(16)} ${(a.toplam - b.toplam).toFixed(1).padStart(8)}`
  );
}

function basabas(g = G) {
  for (let e = 1; e <= 2000; e++) {
    if (sistemsiz(e, g).toplam >= sistemli(e, g).toplam) return e;
  }
  return null;
}
console.log(`\nbasabas ekran sayisi (temel girdilerle): ${basabas()}`);

// Duyarlilik: hangi girdi basabas noktasini ne kadar oynatiyor?
console.log("\ngirdi                  deger  basabas ekran");
const denemeler = [
  ["kurulum", [45, 90, 180, 360]],
  ["sapmaOlasiligi", [0.05, 0.1, 0.2, 0.4]],
  ["ekranBasinaKarar", [6, 12, 18, 30]],
  ["kararYeni", [0.35, 0.7, 1.4]],
];
for (const [anahtar, degerler] of denemeler) {
  for (const d of degerler) {
    const g = { ...G, [anahtar]: d };
    console.log(`${anahtar.padEnd(20)} ${String(d).padStart(6)} ${String(basabas(g)).padStart(14)}`);
  }
}

// Ekip sayisinin sapma olasiligina etkisi: her ek ekip bagimsiz bir yorum kaynagidir.
// q, tek bir ekibin bir karari farkli yorumlama olasiligi.
const q = 0.08;
console.log("\nekip  sapma olasiligi  basabas ekran  20 ekranda beklenen ayri deger");
for (const n of [1, 2, 3, 4, 6, 8]) {
  const p = 1 - Math.pow(1 - q, n);
  const g = { ...G, sapmaOlasiligi: p };
  const ayri = 1 + (20 - 1) * p;
  console.log(
    `${String(n).padStart(4)} ${p.toFixed(3).padStart(16)} ${String(basabas(g)).padStart(14)} ${ayri.toFixed(2).padStart(31)}`
  );
}

// Tek bir kararin sisteme girmesi ne zaman karli? u kez kullanilan bir karar icin:
//   sistemsiz = u * kararYeni,  sistemli = tanim + u * kararBasvuru
const TANIM = 1.5; // bir karari adlandirma, belgeleme ve yayinlama maliyeti
console.log("\nkullanim  sistemsiz  sistemli  karli mi");
for (const u of [1, 2, 3, 4, 6, 10]) {
  const a = u * G.kararYeni;
  const b = TANIM + u * G.kararBasvuru;
  console.log(`${String(u).padStart(8)} ${a.toFixed(2).padStart(10)} ${b.toFixed(2).padStart(9)}  ${a >= b ? "evet" : "hayir"}`);
}
console.log(`esik kullanim sayisi: ${(TANIM / (G.kararYeni - G.kararBasvuru)).toFixed(2)} -> ${Math.ceil(TANIM / (G.kararYeni - G.kararBasvuru))} kez`);

// Geriye donuk duzeltme: sistem gec kuruldugunda birikmis borc ne kadar?
console.log("\nsistemin kuruldugu ekran  o ana kadar birikmis borc (saat)  borcu kapatma / kurulum orani");
for (const e of [1, 5, 10, 20, 40]) {
  const borc = sistemsiz(e).borc;
  console.log(`${String(e).padStart(24)} ${borc.toFixed(1).padStart(32)} ${(borc / G.kurulum).toFixed(2).padStart(29)}`);
}
```

```
ekran  sistemsiz karar  tutarsizlik borcu  sistemsiz toplam  sistemli toplam  fark
    1             12.6                0.0              12.6             94.9    -82.3
    2             25.2                5.4              30.6             99.7    -69.1
    3             37.8               10.8              48.6            104.6    -56.0
    5             63.0               21.6              84.6            114.3    -29.7
    8            100.8               37.8             138.6            128.9      9.7
   10            126.0               48.6             174.6            138.6     36.0
   15            189.0               75.6             264.6            162.9    101.7
   20            252.0              102.6             354.6            187.2    167.4
   30            378.0              156.6             534.6            235.8    298.8
   50            630.0              264.6             894.6            333.0    561.6

basabas ekran sayisi (temel girdilerle): 8

girdi                  deger  basabas ekran
kurulum                  45              4
kurulum                  90              8
kurulum                 180             15
kurulum                 360             28
sapmaOlasiligi         0.05             11
sapmaOlasiligi          0.1              9
sapmaOlasiligi          0.2              8
sapmaOlasiligi          0.4              6
ekranBasinaKarar          6             21
ekranBasinaKarar         12             11
ekranBasinaKarar         18              8
ekranBasinaKarar         30              5
kararYeni              0.35             13
kararYeni               0.7              8
kararYeni               1.4              5

ekip  sapma olasiligi  basabas ekran  20 ekranda beklenen ayri deger
   1            0.080             10                            2.52
   2            0.154              8                            3.92
   3            0.221              7                            5.20
   4            0.284              7                            6.39
   6            0.394              6                            8.48
   8            0.487              5                           10.25

kullanim  sistemsiz  sistemli  karli mi
       1       0.70      1.60  hayir
       2       1.40      1.70  hayir
       3       2.10      1.80  evet
       4       2.80      1.90  evet
       6       4.20      2.10  evet
      10       7.00      2.50  evet
esik kullanim sayisi: 2.50 -> 3 kez

sistemin kuruldugu ekran  o ana kadar birikmis borc (saat)  borcu kapatma / kurulum orani
                       1                              0.0                          0.00
                       5                             21.6                          0.24
                      10                             48.6                          0.54
                      20                            102.6                          1.14
                      40                            210.6                          2.34
```

#### Başabaş Sekizinci Ekranda

Birinci tablo iki eğriyi yan yana koyuyor. Sistemli gidişat ilk ekranda 94.9 saatle başlıyor;
sistemsiz gidişat 12.6 saatte bitiriyor. Beşinci ekranda fark hâlâ sistemsizin lehine: 84.6
karşı 114.3. Sekizinci ekranda işaret dönüyor ve yirminci ekrana gelindiğinde sistemli
gidişat 167.4 saat önde.

Farkın kaynağı eğimlerdir. Sistemsiz gidişatın ekran başına maliyeti 12.6 saat karar artı
büyüyen bir borç; sistemli gidişatın ekran başına maliyeti 4.9 saat. Kurulum maliyeti tek
seferliktir ve sabit kalır, dolayısıyla ekran sayısı büyüdükçe payı küçülür.

Tutarsızlık borcu sütunu ayrıca okunmalıdır. Elli ekranda borç 264.6 saate ulaşıyor; bu,
sistemli gidişatın **toplam** maliyetinin (333.0) dörtte üçüne yakın. Borç kalemi görünmez
olduğu için genellikle hesaba katılmaz: kimse "bu hafta 3 saat tutarsızlık düzelttim" diye
kayıt tutmaz. Görünmemesi yok olduğu anlamına gelmez.

#### Karar Kurulum Maliyetine En Duyarlıdır

İkinci tablo hangi girdinin sonucu ne kadar oynattığını gösteriyor ve tartışmanın nereye
odaklanması gerektiğini söylüyor.

**Kurulum maliyeti** en belirleyici girdi: 45 saatten 360 saate çıkıldığında başabaş noktası
4'ten 28 ekrana taşınıyor, yani sekiz katlık bir girdi değişimi yedi katlık bir sonuç değişimi
üretiyor. Bu, sistemin **kapsamının** en önemli karar olduğu anlamına gelir. Küçük kurulan bir
sistem erken öder; her şeyi kapsamaya çalışan bir sistem, ödemeye başlamadan önce terk edilir.

**Ekran başına karar sayısı** ikinci sırada: 6 karardan 30 karara çıkıldığında başabaş 21'den
5'e iniyor. Bu sayı arayüzün karmaşıklığıdır ve seçilmez, ölçülür. Az kararlı bir arayüz —
tek bir okuma ekranı, tek bir form — sistemden az yararlanır.

**Sapma olasılığı** beklenenden az duyarlı çıkıyor: 0.05'ten 0.40'a, yani sekiz katına
çıkarıldığında başabaş yalnız 11'den 6'ya iniyor. Tutarsızlık korkusu bir sistemi
gerekçelendirmenin en zayıf yoludur; asıl gerekçe tekrar eden karar hacmidir.

#### Ekip Sayısı Sapmayı Çarpar

Üçüncü tablo tek bir girdiyi ayrı ele alıyor: ekip sayısı. Her ek ekip bağımsız bir yorum
kaynağıdır. Tek bir ekibin bir kararı farklı yorumlama olasılığı yüzde 8 alındığında, sekiz
ekipte toplam sapma olasılığı yüzde 48.7'ye çıkıyor.

Son sütun bunun somut karşılığını veriyor: tek bir karar — diyelim kart iç boşluğu — yirmi
ekranda tek ekiple ortalama 2.52 ayrı değere, sekiz ekiple 10.25 ayrı değere dağılıyor. Bir
kurumda "kart iç boşluğu" diye bir şey yoktur artık; on ayrı kart iç boşluğu vardır ve
hiçbiri diğerinden haberdar değildir.

Buradaki kural şudur: **sistem, ekip sınırlarını aşan kararlar için kurulur.** Tek ekibin
tek üründe verdiği kararlar için sistemin sağladığı fayda, ekibin kendi belleğiyle zaten
sağlanır. Sistemin asıl işi bellek değil, **ortak** bellektir.

#### Her Karar Sisteme Girmez

Dördüncü tablo kapsamı karara bağlayan aritmetiği veriyor. Bir kararı adlandırmak,
belgelemek ve yayımlamak 1.5 saat tutuyor; her kullanımda 0.1 saat başvuru maliyeti kalıyor.
Sistemsiz durumda her kullanım 0.7 saat.

Eşik kullanım sayısı, tanım maliyetinin kullanım başına tasarrufa bölümüdür: 1.5 / (0.7 − 0.1)
= 2.5, yani **üç kez**. İki kez kullanılan bir karar sisteme alındığında zarar ettirir; üç
kez kullanıldığında ödemeye başlar.

Bu, önceki dersteki kapsam ilkesini bir sayıya bağlar. Bir ekranda bir kez geçen bir düzen
sisteme girmez. Sisteme giren şey, üç ayrı yerde aynı biçimde sorulan sorudur. Kural aynı
zamanda bir sabır talebi taşır: bir karar ilk kez verildiğinde sisteme alınmaz, üçüncü kez
karşılaşıldığında alınır. Erken soyutlama, henüz doğrulanmamış bir kararı dondurmak demektir.

#### Geç Kurulan Sistem Borcu Devralır

Son tablo zamanlama sorusunu yanıtlıyor. Sistem kırkıncı ekranda kurulduğunda, o ana kadar
birikmiş tutarsızlık borcu 210.6 saat: kurulum maliyetinin 2.34 katı. Bu borç sistem
kurulunca kendiliğinden silinmez; her sapmış değerin bulunması, hangisinin doğru olduğuna
karar verilmesi ve değiştirilmesi gerekir.

Yirminci ekranda oran 1.14, onuncu ekranda 0.54. Eğri doğrusal büyüdüğü için geciktirmenin
bedeli de doğrusaldır; ancak borcun kapatılması kurulumdan farklı olarak **paralel
yapılamaz**, çünkü her sapmanın hangi bağlamda oluştuğunun anlaşılması gerekir.

Buradan sistemin gerekçelendirilemediği durumlar da okunur. Ekran sayısı başabaş noktasının
altında kalacak bir ürün, tek ekipli ve kısa ömürlü bir çalışma, ya da kararların henüz
doğrulanmadığı bir keşif aşaması: bu üç durumda sistem kurmak, karşılığı alınmayan bir
maliyettir. Böyle bir durumda doğru karar sistem kurmamak değil, **sistemi ertelemek ve
envanteri tutmaktır** — hangi kararın kaç kez tekrarlandığını kaydetmek, üçüncü tekrar
geldiğinde adlandırmayı ucuza yapar.

#### Özet

- Sistem kararı bir maliyet karşılaştırmasıdır: kurulum ve bakım bir yanda, yeniden karar
  verme ve tutarsızlık borcu diğer yanda; ikisi de saat cinsinden yazılabilir.
- Ölçülen girdilerle başabaş noktası sekizinci ekrandır; ondan önce sistemsiz gidişat, ondan
  sonra sistemli gidişat ucuzdur.
- Başabaş noktası en çok kurulum maliyetine duyarlıdır; bu, kapsamın en önemli karar olduğunu
  ve dar kurulan bir sistemin erken ödediğini gösterir.
- Sapma olasılığı beklenenden az duyarlıdır; sistemi gerekçelendiren şey tutarsızlık korkusu
  değil tekrar eden karar hacmidir.
- Ekip sayısı sapmayı çarpar: aynı karar sekiz ekipte yirmi ekranda ortalama 10.25 ayrı
  değere dağılır; sistem ekip sınırlarını aşan kararlar için kurulur.
- Bir karar, tanım maliyetinin kullanım başına tasarrufa bölümü kadar — burada üç kez —
  kullanıldığında sisteme girmeye hak kazanır; geciktirilen kurulum birikmiş borcu devralır.

#### Sonraki Adım

Hesap sistemin kurulması gerektiğini söylediğinde ilk iş kapsamı belirlemektir ve kapsam
tahminle çizilemez. Hangi kararın kaç kez tekrarlandığı, hangi değerin kaç ayrı biçimde
kullanıldığı ve bu değerlerin kaçının gerçekten ayrı bir karar olduğu ancak var olan arayüz
sayılarak bilinir. Sonraki ders bu sayımı yapar: katalog ürünlerinin biçem kaynağından renk,
aralık, yazı boyutu ve köşe yarıçapı değerlerini çıkarır, yakın-yinelemeleri eşiklerle
kümeleyerek gerçek karar sayısını bulur ve envanterin hangi bölümünün karşılıksız çeşitlilik
olduğunu gösterir.

### Görsel Denetim

Source: https://academia.sh/tr/kurslar/tasarim-sistemleri/gorsel-denetim

Var olan arayüzlerin biçem kaynağından değer envanterinin makineyle çıkarılması, yakın-yinelemelerin eşiklerle kümelemesi, bağlama kuralının sayıya etkisi ve ürün bazında sapma haritası.

Önceki ders sistemin ne zaman kendini ödediğini hesapladı ve kapsamın en belirleyici karar
olduğunu gösterdi. Kapsam, hangi kararların sisteme gireceğini söyler; bunu bilmek için de
hangi kararların fiilen tekrarlandığını bilmek gerekir. Tahminle çizilen bir kapsam ya
kullanılmayan belirteçler üretir ya da en sık kullanılan kararı dışarıda bırakır.

**Görsel denetim (visual audit)**, var olan arayüzlerin fiilen kullandığı değerlerin
sayılmasıdır. Denetimin ayırt edici özelliği, kaynağının bellek değil **eser** olmasıdır:
kimseye "kart iç boşluğu kaç piksel" diye sorulmaz, biçem kaynağından okunur. Bu ders o
okumayı yapan hesabı kurar ve envanterin hangi bölümünün gerçek karar, hangi bölümünün
karşılıksız çeşitlilik olduğunu ayırır.

#### Denetim Eserden Okunur

Katalog kurumunun dört ürünü var: katalog arayüzü, yönetim paneli, üye profili ve raf
yerleşim ekranı. Dördü ayrı zamanlarda, kısmen ayrı ekiplerce yazıldı. Yinelenme ve
Tutarlılık dersinde katalog arayüzünün dört ekranı elle sayılmıştı; burada sayım dört ürünün
tamamına genişler ve elle değil, kaynağı ayrıştırarak yapılır.

Elle sayımla makine sayımı arasındaki fark yalnız hız değildir. Elle sayım, sayanın gözüne
çarpan değerleri bulur; makine sayımı, kimsenin bakmadığı bildirimleri de bulur. Denetimin
değeri tam buradadır: aranan şey zaten bilinen tutarsızlık değil, bilinmeyen tutarsızlıktır.

Sayım beş özellik sınıfı üzerinde yapılır: aralık, yazı boyutu, köşe yarıçapı, kenar
kalınlığı ve renk. İlk dördünde yakınlık ölçütü piksel farkıdır ve eşikler Yinelenme ve
Tutarlılık dersindeki ayırt edilebilirlik eşikleridir. Renkte ölçüt iki parçalıdır: iki renk
hem birbirine karşı kontrast oranı 1.10'un altındaysa hem de en büyük kanal farkı 16'nın
altındaysa aynı kararı taşıyor sayılır. Tek başına kontrast oranı yetmez, çünkü farklı
tonlardaki iki renk aynı bağıl parlaklığa sahip olabilir.

```js
// denetim.mjs — dort katalog urununun bicem kaynagindan envanter cikarma ve yakin-yineleme kumeleme

// Dort urunun bicem kaynagindan alinmis bildirimler. Her satirin onunde urun kodu var:
// K katalog, Y yonetim paneli, U uye profili, R raf yerlesim ekrani.
const KAYNAK = `
K .arama-alani { padding: 8px 12px; font-size: 14px; border-radius: 4px; border: 1px solid #b7bcc2; color: #212327; }
K .arama-dugmesi { padding: 8px 16px; font-size: 14px; border-radius: 4px; background: #275ea5; color: #ffffff; }
K .kayit-karti { padding: 16px; border-radius: 8px; border: 1px solid #d3d6d9; background: #ffffff; }
K .kayit-basligi { font-size: 20px; color: #212327; margin-bottom: 4px; }
K .kayit-yazari { font-size: 16px; color: #5e656e; margin-bottom: 8px; }
K .kayit-ustverisi { font-size: 13px; color: #757e8a; }
K .kayit-etiketi { padding: 2px 8px; font-size: 12px; border-radius: 12px; background: #dee9f7; color: #15335b; }
K .sonuc-listesi { gap: 24px; padding: 32px 24px; }
K .sayfalama { gap: 8px; font-size: 14px; padding: 24px 0; }
K .onay-katmani { padding: 24px; border-radius: 8px; background: #ffffff; border: 1px solid #d3d6d9; }
K .onay-uyarisi { font-size: 14px; color: #ab3321; margin-top: 8px; }
Y .panel-baslik { font-size: 24px; color: #1f2226; margin-bottom: 16px; }
Y .panel-kart { padding: 14px; border-radius: 6px; border: 1px solid #d5d8db; background: #fdfdfd; }
Y .panel-tablo { font-size: 13px; color: #333333; }
Y .panel-tablo-basligi { font-size: 12px; color: #6b7280; padding: 6px 10px; }
Y .panel-satiri { padding: 10px 12px; border-bottom: 1px solid #e9eaec; }
Y .panel-dugmesi { padding: 6px 14px; font-size: 13px; border-radius: 3px; background: #2a5fa8; color: #ffffff; }
Y .panel-ikincil-dugme { padding: 6px 14px; font-size: 13px; border-radius: 3px; border: 1px solid #b7bcc2; color: #275ea5; }
Y .panel-uyari { font-size: 12px; color: #a83224; padding: 10px; border-radius: 3px; }
Y .panel-bolum { gap: 20px; padding: 20px; }
U .profil-basligi { font-size: 22px; color: #212327; margin-bottom: 12px; }
U .profil-karti { padding: 20px; border-radius: 8px; border: 1px solid #d3d6d9; background: #ffffff; }
U .odunc-satiri { padding: 12px 16px; font-size: 15px; color: #22252a; border-bottom: 1px solid #e9eaec; }
U .odunc-tarihi { font-size: 13px; color: #5e656e; }
U .gecikme-uyarisi { font-size: 13px; color: #ab3321; padding: 8px 12px; border-radius: 4px; background: #fbeae7; }
U .profil-dugmesi { padding: 10px 18px; font-size: 15px; border-radius: 6px; background: #275ea5; color: #ffffff; }
U .profil-bolumu { gap: 16px; padding: 28px 24px; }
R .raf-basligi { font-size: 18px; color: #212327; margin-bottom: 8px; }
R .raf-hucresi { padding: 12px; border-radius: 4px; border: 2px solid #b7bcc2; }
R .raf-kodu { font-size: 12px; color: #757e8a; }
R .raf-dolu { background: #e9eaec; border: 2px solid #969da6; }
R .raf-secili { border: 2px solid #3075cf; background: #dee9f7; }
R .raf-izgarasi { gap: 6px; padding: 18px; }
R .raf-uyarisi { font-size: 13px; color: #b03526; padding: 8px; border-radius: 4px; }
`;

// --- cikarma -----------------------------------------------------------------
const UZUNLUK_OZELLIK = {
  aralik: /^(padding|margin-top|margin-bottom|gap)$/,
  "yazi boyutu": /^font-size$/,
  "kose yaricapi": /^border-radius$/,
};
const SINIFLAR = ["aralik", "yazi boyutu", "kose yaricapi", "kenar kalinligi", "renk"];
const envanter = Object.fromEntries(SINIFLAR.map((s) => [s, []]));

for (const satir of KAYNAK.trim().split("\n")) {
  const urun = satir[0];
  const govde = satir.slice(satir.indexOf("{") + 1, satir.lastIndexOf("}"));
  for (const bildirim of govde.split(";")) {
    const i = bildirim.indexOf(":");
    if (i < 0) continue;
    const ozellik = bildirim.slice(0, i).trim();
    const deger = bildirim.slice(i + 1).trim();

    for (const [sinif, kalip] of Object.entries(UZUNLUK_OZELLIK)) {
      if (!kalip.test(ozellik)) continue;
      for (const p of deger.split(/\s+/)) {
        const m = p.match(/^(\d+(?:\.\d+)?)px$/);
        if (m) envanter[sinif].push({ deger: Number(m[1]), urun });
      }
    }
    if (/^border(-bottom)?$/.test(ozellik)) {
      const m = deger.match(/^(\d+(?:\.\d+)?)px/);
      if (m) envanter["kenar kalinligi"].push({ deger: Number(m[1]), urun });
    }
    for (const m of deger.matchAll(/#([0-9a-f]{6})/g)) {
      envanter.renk.push({ deger: "#" + m[1], urun });
    }
  }
}

// --- yakinlik olcutleri -------------------------------------------------------
const ESIK = { aralik: 2, "yazi boyutu": 1, "kose yaricapi": 2, "kenar kalinligi": 0.5 };
const cz = (h) => [1, 3, 5].map((i) => parseInt(h.slice(i, i + 2), 16));
function kanal(v) {
  const s = v / 255;
  return s <= 0.04045 ? 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);
}
const kanalFarki = (a, b) => Math.max(...a.map((v, i) => Math.abs(v - b[i])));

// Iki deger ayni karari tasiyor mu? Renkte olcut hem kontrast orani hem kanal farkidir.
function yakin(sinif, a, b) {
  if (sinif !== "renk") return Math.abs(a - b) <= ESIK[sinif];
  return kontrast(cz(a), cz(b)) < 1.1 && kanalFarki(cz(a), cz(b)) < 16;
}
function sirala(sinif, degerler) {
  const tekil = [...new Set(degerler)];
  return sinif === "renk"
    ? tekil.sort((a, b) => parlaklik(cz(b)) - parlaklik(cz(a)))
    : tekil.sort((a, b) => a - b);
}

// Zincirleme baglama: bir degere yakin tek bir uye bulmak kumeye katilmaya yeter.
function zincirleme(sinif, degerler) {
  const kumeler = [];
  for (const d of sirala(sinif, degerler)) {
    const k = kumeler.find((k) => k.some((o) => yakin(sinif, d, o)));
    if (k) k.push(d);
    else kumeler.push([d]);
  }
  return kumeler;
}

// Cap sinirli baglama: kumeye katilmak icin kumedeki HER uyeye yakin olmak gerekir.
function capSinirli(sinif, degerler) {
  const kumeler = [];
  for (const d of sirala(sinif, degerler)) {
    const k = kumeler.find((k) => k.every((o) => yakin(sinif, d, o)));
    if (k) k.push(d);
    else kumeler.push([d]);
  }
  return kumeler;
}

console.log("ozellik          kullanim  ayri deger  zincirleme kume  cap sinirli kume");
let toplamKullanim = 0;
let toplamAyri = 0;
let toplamZincir = 0;
let toplamCap = 0;
const capKumeleri = {};
for (const sinif of SINIFLAR) {
  const degerler = envanter[sinif].map((k) => k.deger);
  const z = zincirleme(sinif, degerler);
  const c = capSinirli(sinif, degerler);
  capKumeleri[sinif] = c;
  const ayri = new Set(degerler).size;
  toplamKullanim += degerler.length;
  toplamAyri += ayri;
  toplamZincir += z.length;
  toplamCap += c.length;
  console.log(
    `${sinif.padEnd(15)} ${String(degerler.length).padStart(9)} ${String(ayri).padStart(11)} ` +
      `${String(z.length).padStart(16)} ${String(c.length).padStart(17)}`
  );
}
console.log(
  `${"toplam".padEnd(15)} ${String(toplamKullanim).padStart(9)} ${String(toplamAyri).padStart(11)} ` +
    `${String(toplamZincir).padStart(16)} ${String(toplamCap).padStart(17)}`
);
console.log(
  `karsiliksiz cesitlilik (cap sinirli): ${toplamAyri - toplamCap} deger, ` +
    `${((100 * (toplamAyri - toplamCap)) / toplamAyri).toFixed(1)}%`
);

console.log("\ncap sinirli kumeler (ayni karar, farkli yazim)");
for (const sinif of SINIFLAR) {
  for (const k of capKumeleri[sinif]) {
    if (k.length > 1) console.log(`  ${sinif.padEnd(15)} ${k.join(", ")}`);
  }
}

// --- urun bazinda sapma -------------------------------------------------------
// Bir kumenin en cok kullanilan uyesi "ana yazim"; digerleri sapma sayilir.
const sayac = {};
for (const sinif of SINIFLAR) {
  for (const kayit of envanter[sinif]) {
    const a = `${sinif}:${kayit.deger}`;
    sayac[a] = (sayac[a] || 0) + 1;
  }
}
const anaYazim = new Map();
for (const sinif of SINIFLAR) {
  for (const k of capKumeleri[sinif]) {
    const en = k.reduce((a, b) => ((sayac[`${sinif}:${b}`] || 0) > (sayac[`${sinif}:${a}`] || 0) ? b : a));
    for (const uye of k) anaYazim.set(`${sinif}:${uye}`, en);
  }
}
console.log("\nurun  kullanim  ana yazimdan sapan  sapma orani");
for (const urun of ["K", "Y", "U", "R"]) {
  let toplam = 0;
  let sapan = 0;
  for (const sinif of SINIFLAR) {
    for (const kayit of envanter[sinif].filter((k) => k.urun === urun)) {
      toplam++;
      if (anaYazim.get(`${sinif}:${kayit.deger}`) !== kayit.deger) sapan++;
    }
  }
  console.log(
    `${urun.padStart(4)} ${String(toplam).padStart(9)} ${String(sapan).padStart(19)} ${((100 * sapan) / toplam).toFixed(1).padStart(12)}%`
  );
}
```

```
ozellik          kullanim  ayri deger  zincirleme kume  cap sinirli kume
aralik                 45          13                4                 8
yazi boyutu            22           9                5                 7
kose yaricapi          14           5                2                 3
kenar kalinligi        11           2                2                 2
renk                   43          23               15                15
toplam                135          52               28                35
karsiliksiz cesitlilik (cap sinirli): 17 deger, 32.7%

cap sinirli kumeler (ayni karar, farkli yazim)
  aralik          2, 4
  aralik          6, 8
  aralik          10, 12
  aralik          14, 16
  aralik          18, 20
  yazi boyutu     12, 13
  yazi boyutu     14, 15
  kose yaricapi   3, 4
  kose yaricapi   6, 8
  renk            #ffffff, #fdfdfd
  renk            #e9eaec, #dee9f7
  renk            #d5d8db, #d3d6d9
  renk            #b03526, #ab3321, #a83224
  renk            #2a5fa8, #275ea5
  renk            #22252a, #212327, #1f2226

urun  kullanim  ana yazimdan sapan  sapma orani
   K        46                   3          6.5%
   Y        37                  20         54.1%
   U        31                   6         19.4%
   R        21                   5         23.8%
```

#### Bağlama Kuralı Sayıyı Belirler

Birinci tablonun iki küme sütunu aynı veriden iki farklı sonuç üretiyor: 28 ve 35. Fark
küçük görünüyor ama tek bir satırda toplanıyor. Aralık sınıfında zincirleme bağlama 4 küme,
çap sınırlı bağlama 8 küme buluyor.

Nedeni, **zincirleme bağlama**nın kümeye katılmak için tek bir yakın üye aramasıdır. Aralık
değerleri 2, 4, 6, 8, 10, 12, 14, 16, 18, 20 biçiminde ikişer ikişer ilerlediği için her
değer bir öncekine yakındır ve zincir kopmaz. Sonuçta 2 ile 20 aynı kümeye düşer; oysa 2
piksellik bir boşlukla 20 piksellik bir boşluk aynı karar değildir.

**Çap sınırlı bağlama** kümeye katılmak için kümedeki her üyeye yakın olmayı şart koşar. Bu,
kümenin genişliğini eşikle sınırlar ve zinciri kırar. Aynı veride 2 ile 4 aynı kümede kalır ve 6
yeni bir küme açar.

Bu ayrım denetim raporunun güvenilirliğini doğrudan etkiler. Zincirleme sayı, yakın-yineleme
sorununu olduğundan büyük gösterir: "on üç aralık değeri aslında dört karar" cümlesi
denetimden çıkan en çarpıcı ama en yanlış cümledir. Doğru sayı sekizdir ve karşılıksız
çeşitlilik toplamda 52 değerden 17'sidir, yani yüzde 32.7.

#### Kümeleme Öneri Üretir, Karar Vermez

İkinci blok kümelerin içeriğini gösteriyor ve burada ölçütün sınırı görünür hâle geliyor.

Renk kümelerinin çoğu doğru: `#22252a`, `#212327` ve `#1f2226` üç ürünün üç ayrı koyu metin
rengidir ve birbirinden ayırt edilemez. `#b03526`, `#ab3321`, `#a83224` üçlüsü aynı biçimde
üç ayrı hata kırmızısıdır. `#2a5fa8` ile `#275ea5` iki ayrı birincil eylem mavisidir.
Bunların hepsi tek bir karar olmalıydı ve üç kez verildi.

Bir küme ise yanlış: `#e9eaec` ile `#dee9f7`. Birincisi nötr bir gri, ikincisi soluk bir
mavi zemin. Bağıl parlaklıkları birbirine yakın olduğu ve kanal farkı eşiğin altında kaldığı
için ölçüt bunları aynı sayıyor; oysa ikisi ayrı işler yapıyor — biri dolu raf hücresinin
zemini, diğeri seçili kaydın vurgusu. Ölçüt bunu bilemez, çünkü ölçüt yalnızca değere bakar,
değerin taşıdığı role bakmaz.

Buradan çıkan kural denetimin tamamı için geçerlidir: **kümeleme bir öneri listesidir, bir
karar listesi değil.** Her küme insan tarafından okunur ve iki soru sorulur: bu değerler aynı
işi mi yapıyor ve birleştirilirlerse kaybolan bir ayrım var mı. Birinci soruya hayır yanıtı
gelen küme bölünür; ikinci soruya evet yanıtı gelen küme kayda geçirilir.

#### Sapma Tek Yerde Toplanır

Üçüncü tablo denetimin en doğrudan eyleme dönüşen çıktısı. Her kümenin en çok kullanılan
üyesi **ana yazım** kabul ediliyor; diğer üyeler sapma sayılıyor.

Sonuç eşit dağılmıyor. Katalog arayüzünün 46 kullanımından yalnız 3'ü sapma: yüzde 6.5.
Yönetim panelinin 37 kullanımından 20'si sapma: yüzde 54.1. Üye profili yüzde 19.4, raf
ekranı yüzde 23.8.

Bu dağılım kapsam ve sıralama kararını birlikte veriyor. Yönetim paneli tek başına toplam
sapmanın büyük bölümünü taşıyor; sisteme geçişte önce onun düzeltilmesi, diğer üç üründen
daha fazla kazanç sağlar. Aynı zamanda bir soru açıyor: yönetim paneli neden bu kadar
uzakta? Yanıt genellikle ya ayrı bir ekipte yazılmış olmasıdır ya da başka bir zamanda —
iki durumda da düzeltme yalnız değerleri değiştirmekle bitmez, o ekibin ya da o dönemin
kararlarının neden farklılaştığının anlaşılmasını gerektirir.

Katalog arayüzünün düşük sapma oranı da ayrıca okunur: ana yazımlar zaten büyük ölçüde onun
değerleridir, çünkü en çok kullanım oradadır. Bu, "en sık kullanılan değer doğrudur"
varsayımının denetime gizlice girdiği yerdir. Varsayım her zaman doğru değildir; en sık
kullanılan değer, en çok ekran yazılmış ürünün değeridir — kalitesinin değil, hacminin
sonucudur.

#### Denetimin Teslim Ettiği Şey

Bir görsel denetim üç belge üretir ve üçü de sonraki adımların girdisidir.

**Değer envanteri**: her özellik sınıfı için kullanılan değerler, kullanım sayılarıyla
birlikte. Ölçek kurulurken hangi değerin korunacağını bu sayılar belirler.

**Küme raporu**: yakın-yineleme kümeleri ve her küme için verilmiş insan kararı —
birleştirilecek mi, bölünecek mi, kayda geçirilecek mi.

**Sapma haritası**: hangi ürünün ana yazımdan ne kadar uzakta olduğu. Geçiş sırasını ve
maliyet tahminini bu harita verir.

Denetimin teslim etmediği şey ise ölçeğin kendisidir. Envanter neyin kullanıldığını söyler,
neyin kullanılması gerektiğini söylemez. İkisi arasındaki geçiş ayrı bir karardır ve iki
farklı yoldan yapılabilir.

#### Özet

- Görsel denetim, arayüzün fiilen kullandığı değerlerin biçem kaynağından makineyle
  sayılmasıdır; kaynağı bellek değil eserdir ve aradığı şey bilinmeyen tutarsızlıktır.
- Yakın-yineleme ölçütü sınıfa göre değişir: uzunluklarda piksel farkı, renkte hem kontrast
  oranı hem en büyük kanal farkı birlikte kullanılır.
- Bağlama kuralı sonucu belirler: zincirleme bağlama 13 aralık değerini 4 kümede toplar ve
  yakın-yinelemeyi abartır; çap sınırlı bağlama 8 küme bulur.
- Ölçülen dört üründe 52 ayrı değerin 17'si karşılıksız çeşitliliktir; yüzde 32.7'lik bu pay
  bedelsiz kapatılabilir.
- Kümeleme öneri üretir; her küme rolüyle birlikte okunur, çünkü ölçüt değere bakar, değerin
  işine bakmaz.
- Sapma haritası geçiş sırasını verir: dört üründen biri toplam sapmanın büyük bölümünü
  taşıyorsa geçiş oradan başlar.

#### Sonraki Adım

Envanter elde olduğunda iki yol açılır. Birinci yol, ölçeği ilkelerden kurup mevcut değerleri
ona oturtmaktır; ikinci yol, mevcut değerlerin en sık kullanılanlarını ölçek ilan edip
oradan devam etmektir. İki yolun geçiş maliyeti de, ürettiği ölçeğin kalitesi de farklıdır ve
ikisi de hesaplanabilir. Sonraki ders bu iki stratejiyi aynı envanter üzerinde uygular; kaç
değerin gözle görülür biçimde değişeceğini, kaç bileşenin dokunulacağını ve türetilen
ölçeklerin Aralık Ölçeği dersindeki ölçütleri geçip geçmediğini karşılaştırır.

### Sıfırdan ve Mevcuttan Kurma

Source: https://academia.sh/tr/kurslar/tasarim-sistemleri/sifirdan-ve-mevcuttan-kurma

Aynı envanter üzerinde ilkeden kurma ile mevcuttan türetme stratejilerinin geçiş maliyeti ve ölçek kalitesi bakımından karşılaştırılması, karma yolun istisna kaydıyla kurulması ve dondurmaya dayalı aşamalı geçiş.

Görsel denetim envanteri verdi: dört üründe 52 ayrı değer, bunların 17'si karşılıksız
çeşitlilik. Envanter neyin kullanıldığını söylüyor; neyin kullanılması gerektiğini
söylemiyor. Bu ders aradaki geçişi kurar ve geçişin iki farklı yoldan yapılabileceğini
gösterir.

**İlkeden kurma**, ölçeği envanterden bağımsız olarak, Aralık Ölçeği ve Tipografik Ölçek
derslerindeki ölçütlerden türetir; sonra mevcut değerleri bu ölçeğe oturtur. **Mevcuttan
türetme**, en çok kullanılan değerleri ölçek ilan eder; ölçek envanterin özetidir. İki yol
farklı geçiş maliyeti ve farklı ölçek kalitesi üretir ve bu iki büyüklük birbirinden
bağımsızdır.

#### İki Strateji, Tek Envanter

Karşılaştırma üç sayısal özellik sınıfında yapılır: aralık, yazı boyutu ve köşe yarıçapı.
Renk ayrı ele alınır, çünkü rengin ölçeği bir sayı dizisi değil rol tablosudur ve geçişi
kayma değil eşleşme denetimiyle ölçülür.

Her strateji iki ölçüt kümesiyle değerlendirilir. **Ölçek kalitesi**: adım sayısı, ayırt
edilebilirlik eşiğinin altında kalan komşu çift sayısı ve gruplama için gereken oranı 2 ve
üzeri olan komşu çift sayısı. **Geçiş maliyeti**: kaç kullanımın değeri kayar, bu kaymaların
kaçı ayırt edilebilirlik eşiğini aşar ve en büyük kayma nedir.

```js
// strateji.mjs — ayni envanter uzerinde iki baslangic stratejisinin karsilastirilmasi

// Gorsel Denetim dersinde cikarilan envanter: deger -> kullanim sayisi.
const ENVANTER = {
  aralik: { 2: 1, 4: 1, 6: 4, 8: 9, 10: 4, 12: 6, 14: 3, 16: 5, 18: 2, 20: 3, 24: 5, 28: 1, 32: 1 },
  "yazi boyutu": { 12: 4, 13: 7, 14: 4, 15: 2, 16: 1, 18: 1, 20: 1, 22: 1, 24: 1 },
  "kose yaricapi": { 3: 3, 4: 5, 6: 2, 8: 3, 12: 1 },
};
const ESIK = { aralik: 2, "yazi boyutu": 1, "kose yaricapi": 2 };

// A stratejisi: olcek ilkelerden kurulur. Aralik olcegi karma olcektir (Aralik Olcegi dersi),
// yazi boyutu 16 tabanli 1.25 oranli olcektir (Tipografik Olcek dersi).
const ILKEDEN = {
  aralik: [4, 8, 12, 16, 24, 32, 48, 64, 96],
  "yazi boyutu": [-1, 0, 1, 2, 3].map((n) => Math.round(16 * Math.pow(1.25, n))),
  "kose yaricapi": [4, 8],
};

// B stratejisi: olcek mevcuttan turetilir. En cok kullanilan degerler, kullanimin
// belirli bir oranini kapsayana kadar secilir.
const KAPSAMA_HEDEFI = 0.8;
function mevcuttan(dagilim, hedef) {
  const toplam = Object.values(dagilim).reduce((t, v) => t + v, 0);
  const sirali = Object.entries(dagilim)
    .map(([d, n]) => [Number(d), n])
    .sort((a, b) => b[1] - a[1] || a[0] - b[0]);
  const secilen = [];
  let birikim = 0;
  for (const [d, n] of sirali) {
    if (birikim / toplam >= hedef) break;
    secilen.push(d);
    birikim += n;
  }
  return secilen.sort((a, b) => a - b);
}
const MEVCUTTAN = Object.fromEntries(
  Object.entries(ENVANTER).map(([s, d]) => [s, mevcuttan(d, KAPSAMA_HEDEFI)])
);

// --- olcut hesaplari ----------------------------------------------------------
const enYakin = (d, olcek) => olcek.reduce((a, b) => (Math.abs(b - d) < Math.abs(a - d) ? b : a));

function olcekKalitesi(olcek, esik) {
  let ayirtEdilemeyen = 0;
  let gruplayan = 0;
  for (let i = 1; i < olcek.length; i++) {
    if (olcek[i] - olcek[i - 1] <= esik) ayirtEdilemeyen++;
    if (olcek[i] / olcek[i - 1] >= 2) gruplayan++;
  }
  return { adim: olcek.length, ayirtEdilemeyen, gruplayan };
}

function gecisMaliyeti(dagilim, olcek, esik) {
  let kullanim = 0;
  let kayan = 0;
  let gorunur = 0;
  let enBuyuk = 0;
  let toplamKayma = 0;
  for (const [d, n] of Object.entries(dagilim)) {
    const deger = Number(d);
    const hedef = enYakin(deger, olcek);
    const kayma = Math.abs(hedef - deger);
    kullanim += n;
    if (kayma > 0) kayan += n;
    if (kayma > esik) gorunur += n;
    toplamKayma += kayma * n;
    enBuyuk = Math.max(enBuyuk, kayma);
  }
  return { kullanim, kayan, gorunur, enBuyuk, ortKayma: toplamKayma / kullanim };
}

for (const [ad, olcekler] of [["A ilkeden", ILKEDEN], ["B mevcuttan", MEVCUTTAN]]) {
  console.log(`--- ${ad} ---`);
  console.log("ozellik          olcek                          adim  ayirt-edilemeyen  gruplayan");
  for (const sinif of Object.keys(ENVANTER)) {
    const k = olcekKalitesi(olcekler[sinif], ESIK[sinif]);
    console.log(
      `${sinif.padEnd(15)} ${olcekler[sinif].join(",").padEnd(30)} ${String(k.adim).padStart(4)} ` +
        `${String(k.ayirtEdilemeyen).padStart(17)} ${String(k.gruplayan).padStart(10)}`
    );
  }
  console.log("ozellik          kullanim  kayan  gozle gorulur  en buyuk kayma  ort kayma");
  let tk = 0;
  let tkayan = 0;
  let tgorunur = 0;
  for (const sinif of Object.keys(ENVANTER)) {
    const g = gecisMaliyeti(ENVANTER[sinif], olcekler[sinif], ESIK[sinif]);
    tk += g.kullanim;
    tkayan += g.kayan;
    tgorunur += g.gorunur;
    console.log(
      `${sinif.padEnd(15)} ${String(g.kullanim).padStart(9)} ${String(g.kayan).padStart(6)} ` +
        `${String(g.gorunur).padStart(14)} ${String(g.enBuyuk).padStart(15)} ${g.ortKayma.toFixed(2).padStart(10)}`
    );
  }
  console.log(
    `${"toplam".padEnd(15)} ${String(tk).padStart(9)} ${String(tkayan).padStart(6)} ${String(tgorunur).padStart(14)}` +
      `   kayan oran ${((100 * tkayan) / tk).toFixed(1)}%  gorunur oran ${((100 * tgorunur) / tk).toFixed(1)}%\n`
  );
}

// --- karma yol: ilkeden olcek + kayda gecirilmis istisna -----------------------
// Bir ham deger olcege gorunur bicimde kaymiyorsa oturur; kayiyorsa ya olcege bir adim
// eklenir ya da deger degistirilir. Adim eklemenin olcutu kullanim sayisidir.
const ISTISNA_ESIGI = 3;
console.log("--- karma yol: ilkeden olcek, gorunur kayanlar icin karar ---");
console.log("ozellik          ham deger  kullanim  hedef  kayma  karar");
const eklenen = {};
for (const sinif of Object.keys(ENVANTER)) {
  eklenen[sinif] = [];
  for (const [d, n] of Object.entries(ENVANTER[sinif])) {
    const deger = Number(d);
    const hedef = enYakin(deger, ILKEDEN[sinif]);
    const kayma = Math.abs(hedef - deger);
    if (kayma <= ESIK[sinif]) continue;
    const karar = n >= ISTISNA_ESIGI ? "olcege adim ekle" : "degeri oturt";
    if (n >= ISTISNA_ESIGI) eklenen[sinif].push(deger);
    console.log(
      `${sinif.padEnd(15)} ${String(deger).padStart(10)} ${String(n).padStart(9)} ${String(hedef).padStart(6)} ` +
        `${String(kayma).padStart(6)}  ${karar}`
    );
  }
}
console.log("\nozellik          genisletilmis olcek            adim  ayirt-edilemeyen  gorunur kalan");
for (const sinif of Object.keys(ENVANTER)) {
  const olcek = [...new Set([...ILKEDEN[sinif], ...eklenen[sinif]])].sort((a, b) => a - b);
  const k = olcekKalitesi(olcek, ESIK[sinif]);
  const g = gecisMaliyeti(ENVANTER[sinif], olcek, ESIK[sinif]);
  console.log(
    `${sinif.padEnd(15)} ${olcek.join(",").padEnd(30)} ${String(k.adim).padStart(4)} ` +
      `${String(k.ayirtEdilemeyen).padStart(17)} ${String(g.gorunur).padStart(14)}`
  );
}

// --- asamali gecis: dondurma sonrasi kapsama ----------------------------------
// Dondurma: yeni bildirimler yalniz olcekten gelir. Eski bildirimler ancak o dosyaya
// baska bir is icin dokunuldugunda gecer. r, bir donemde dokunulan eski bildirim orani.
console.log("\ndonem  r=0.10  r=0.20  r=0.35   (olcege uyan bildirim orani)");
const baslangic = 0.6; // dondurma aninda zaten olcege uyan bildirim orani
for (const donem of [0, 1, 2, 3, 4, 6, 8]) {
  const satir = [0.1, 0.2, 0.35].map((r) => (1 - (1 - baslangic) * Math.pow(1 - r, donem)).toFixed(3));
  console.log(`${String(donem).padStart(5)} ${satir[0].padStart(7)} ${satir[1].padStart(7)} ${satir[2].padStart(7)}`);
}
```

```
--- A ilkeden ---
ozellik          olcek                          adim  ayirt-edilemeyen  gruplayan
aralik          4,8,12,16,24,32,48,64,96          9                 0          1
yazi boyutu     13,16,20,25,31                    5                 0          0
kose yaricapi   4,8                               2                 0          1
ozellik          kullanim  kayan  gozle gorulur  en buyuk kayma  ort kayma
aralik                 45     18              4               4       0.98
yazi boyutu            22     13              2               2       0.68
kose yaricapi          14      6              1               4       0.79
toplam                 81     37              7   kayan oran 45.7%  gorunur oran 8.6%

--- B mevcuttan ---
ozellik          olcek                          adim  ayirt-edilemeyen  gruplayan
aralik          6,8,10,12,14,16,24                7                 5          0
yazi boyutu     12,13,14,15,16                    5                 4          0
kose yaricapi   3,4,6,8                           4                 3          0
ozellik          kullanim  kayan  gozle gorulur  en buyuk kayma  ort kayma
aralik                 45      9              6               8       0.76
yazi boyutu            22      4              4               8       0.91
kose yaricapi          14      1              1               4       0.29
toplam                 81     14             11   kayan oran 17.3%  gorunur oran 13.6%

--- karma yol: ilkeden olcek, gorunur kayanlar icin karar ---
ozellik          ham deger  kullanim  hedef  kayma  karar
aralik                  20         3     16      4  olcege adim ekle
aralik                  28         1     24      4  degeri oturt
yazi boyutu             18         1     16      2  degeri oturt
yazi boyutu             22         1     20      2  degeri oturt
kose yaricapi           12         1      8      4  degeri oturt

ozellik          genisletilmis olcek            adim  ayirt-edilemeyen  gorunur kalan
aralik          4,8,12,16,20,24,32,48,64,96      10                 0              1
yazi boyutu     13,16,20,25,31                    5                 0              2
kose yaricapi   4,8                               2                 0              1

donem  r=0.10  r=0.20  r=0.35   (olcege uyan bildirim orani)
    0   0.600   0.600   0.600
    1   0.640   0.680   0.740
    2   0.676   0.744   0.831
    3   0.708   0.795   0.890
    4   0.738   0.836   0.929
    6   0.787   0.895   0.970
    8   0.828   0.933   0.987
```

#### Kayan Değer Sayısı Yanlış Ölçüttür

İki stratejinin toplam satırları ilk bakışta beklenen yönde: ilkeden kurmada kullanımların
yüzde 45.7'si kayıyor, mevcuttan türetmede yalnız yüzde 17.3'ü. Envanterin özeti olan bir
ölçeğin envantere daha yakın olması şaşırtıcı değildir.

Bir sonraki sütun bu okumayı tersine çeviriyor. Gözle görülür biçimde değişen kullanım
oranı ilkeden kurmada yüzde 8.6, mevcuttan türetmede yüzde 13.6. Yani envanterden türetilen
ölçek, daha az değeri kaydırıyor ama **daha çok değeri gözle görülür biçimde** kaydırıyor.

Nedeni dağılımın kuyruğundadır. Mevcuttan türetme en çok kullanılan değerleri seçer;
kullanımın yüzde 80'i küçük değerlerde toplandığı için ölçek 6 ile 24 arasına sıkışır. Büyük
değerler — 28 ve 32 piksellik bölüm boşlukları — ölçeğin dışında kalır ve 24'e çekilir; 20
piksel de 16'ya iner. Küçük değerlerde neredeyse hiç kayma olmadığı için toplam kayan sayısı
düşük görünür, ama kayanların hepsi büyük kaymadır: en büyük kayma 8 piksel.

İlkeden kurulan ölçekte durum terstir. Çok sayıda değer 1–2 piksel kayar — bunlar ayırt
edilebilirlik eşiğinin altındadır ve karşılıksız kapanır. Gerçekten değişen yalnız yedi
kullanımdır.

Buradan çıkan ölçüt şudur: **geçiş maliyeti kayan değer sayısıyla değil, eşiği aşan kayma
sayısıyla ölçülür.** Bu ayrım Yinelenme ve Tutarlılık dersindeki bulgunun bir sonucudur:
tutarsızlığın büyük bölümü görünmez ve görünmeyen tutarsızlık bedelsiz düzeltilir.

#### Envanterin Özeti Bir Ölçek Değildir

Ölçek kalitesi tabloları ikinci farkı gösteriyor ve bu fark tartışmaya kapalıdır.

İlkeden kurulan üç ölçekte ayırt edilebilirlik eşiğinin altında kalan komşu çift sayısı
sıfır. Mevcuttan türetilen ölçeklerde aynı sayı sırasıyla 5, 4 ve 3; toplam 12. Yazı boyutu
ölçeği 12, 13, 14, 15, 16 biçiminde ilerliyor — beş basamağın hiçbiri diğerinden ayırt
edilemiyor. Böyle bir ölçek karar sayısını düşürmez, yalnız var olan kararsızlığı
belgelendirir.

Gruplama sütunu aynı sonucu ikinci kez veriyor. İlkeden kurulan aralık ölçeğinde oranı 2 ve
üzeri olan bir komşu çift var; mevcuttan türetilende hiç yok. Yakınlık ve Gruplama dersindeki
kural bir grup sınırının grup içi boşluğun en az iki katı olmasını istiyordu; mevcuttan
türetilen ölçekle bu kural komşu adımlarla ifade edilemez.

Sonuç şudur: envanterin en sık kullanılan değerleri bir ölçek oluşturmaz. Ölçeğin işi
seçenek kısmaktır; en sık kullanılanları seçmek, en sık verilen kararsız kararları meşru
kılmaktır.

#### Karma Yol Ölçeği Korur, İstisnayı Kayda Geçirir

Üçüncü blok iki stratejinin arasındaki yolu kuruyor. Ölçek ilkeden kurulur; ardından gözle
görülür biçimde kayan her ham değer tek tek ele alınır ve iki karardan biri verilir.

Ölçüt kullanım sayısıdır. Sistem İhtiyacının Gerekçelendirilmesi dersinde bir kararın sisteme
girmesi için gereken en az kullanım sayısı üç olarak hesaplanmıştı; aynı eşik burada ölçeğe
adım ekleme ölçütü olarak kullanılır. Üç ve üzeri kullanımı olan bir ham değer, ölçeğin
eksik olduğunu gösterir; bir ya da iki kullanımı olan değer ölçeğe oturtulur.

Tabloda tek bir değer eşiği geçiyor: 20 piksellik aralık, üç kullanımla. Ölçeğe eklenir ve
aralık ölçeği 4, 8, 12, 16, 20, 24, 32, 48, 64, 96 hâline gelir. Genişletilmiş ölçekte ayırt
edilemeyen komşu çift sayısı hâlâ sıfır — ekleme ölçeği bozmadı — ve gözle görülür kalan
kullanım aralıkta 4'ten 1'e indi.

Diğer dört değer — 28 piksellik aralık, 12 piksellik köşe yarıçapı ile 18 ve 22 piksellik yazı
boyutu — birer kullanımla ölçeğe oturtulur. Bu kararlar Yinelenme ve Tutarlılık dersindeki
**istisna kaydına** girer: hangi değerin neye oturtulduğu ve hangi ekranın etkilendiği yazılır.
Kayıt tutulmazsa aynı değer altı ay sonra geri gelir ve kimse neden kaldırıldığını bilmez.

#### Dondurma Geçişi Zamana Yayar

Son tablo geçişin nasıl uygulanacağını ölçüyor. Bütün bildirimleri tek seferde değiştirmek en
hızlı yoldur ve en riskli yoldur: değişikliğin tamamı aynı anda yayına girer ve bir hata her
ekranı birden etkiler.

Alternatif, **dondurma**dır: belirli bir andan sonra yazılan her yeni bildirim ölçekten
gelmek zorundadır; eski bildirimler ancak o dosyaya başka bir iş için dokunulduğunda geçer.
Dondurma anında bildirimlerin yüzde 60'ı zaten ölçeğe uyuyorsa, kapsama oranı dönem başına
dokunulan eski bildirim oranına bağlı olarak artar.

Tablo üç hızı karşılaştırıyor. Dönem başına eski bildirimlerin yüzde 10'una dokunuluyorsa
sekiz dönem sonunda kapsama 0.828'de kalıyor; yüzde 35'ine dokunuluyorsa aynı sürede 0.987'ye
çıkıyor. Yüzde 20'lik orta hızda sekiz dönem sonunda 0.933.

İki sonuç okunur. Birincisi, dondurma yalnız başına yeterlidir ancak yavaş çalışan bir
üründe çok yavaştır; dokunma oranı düşük olan ürünler için ayrıca planlı bir geçiş gerekir.
İkincisi, kapsama hiçbir zaman tam 1'e ulaşmaz — üstel yaklaşma son yüzde birkaçı hep
bırakır. Bu son artık, geçişin bir noktada elle bitirilmesi gerektiği anlamına gelir.

#### Hangi Strateji Ne Zaman

Karar üç girdiye bakar. Ürün yeniyse ya da yerleşik taban küçükse ilkeden kurma tek makul yoldur;
geçiş maliyeti zaten yoktur ve ölçek kalitesi tam alınır. Yerleşik taban büyükse ve görünür
değişikliğe tolerans düşükse — sözleşmeye bağlı bir görünüm, sık kullanılan bir iç araç — karma
yol seçilir: ölçek ilkeden kurulur ve kullanımı eşiği geçen ham değerler için adım eklenir.

Mevcuttan türetme ise ölçek kurma stratejisi olarak değil, **envanterin okunması** olarak
kalır. En sık kullanılan değerler ölçeği vermez; ölçeğin hangi bölgesinde yoğunlaşma olduğunu
verir. İlkeden kurulan bir ölçeğin alt bölgesi neden ince, üst bölgesi neden kaba olmalıdır
sorusunun yanıtı bu dağılımdadır.

#### Özet

- Aynı envanter üzerinde ilkeden kurma kullanımların yüzde 45.7'sini kaydırır ama yalnız
  yüzde 8.6'sını gözle görülür biçimde değiştirir; mevcuttan türetme yüzde 17.3'ünü kaydırır
  ve yüzde 13.6'sını görünür biçimde değiştirir.
- Geçiş maliyeti kayan değer sayısıyla değil eşiği aşan kayma sayısıyla ölçülür; envanterden
  türetilen ölçek küçük değerleri korur, büyük değerleri keser.
- Envanterin en sık kullanılan değerleri bir ölçek oluşturmaz: mevcuttan türetilen üç ölçekte
  ayırt edilemeyen komşu çift sayısı 12, gruplama oranını sağlayan çift sayısı sıfırdır.
- Karma yol ölçeği ilkeden kurar ve gözle görülür kayan ham değerleri kullanım sayısına göre
  ayırır; üç ve üzeri kullanımı olan değer ölçeğe adım ekletir, azı ölçeğe oturur ve istisna
  kaydına girer.
- Dondurma geçişi zamana yayar; kapsama üstel olarak yaklaşır ve tam 1'e ulaşmaz, dolayısıyla
  son artık planlı biçimde kapatılır.
- Yeni ya da küçük tabanlı üründe ilkeden kurma, büyük ve değişikliğe kapalı tabanda karma
  yol seçilir; mevcuttan türetme bir ölçek stratejisi değil bir okuma aracıdır.

#### Sonraki Adım

Ölçekler kurulduğunda sistemin sayısal iskeleti hazır olur, ama iskelet neyin doğru
görüneceğini söylemez. Aynı ölçekle iki ayrı arayüz kurulabilir ve ikisi de kurumun sesini
taşımayabilir. Bir kararı sayıya bağlanamayan durumlarda neyin belirlediği ayrı bir sorudur:
düğmenin dolgulu mu çerçeveli mi olacağı, bir hata iletisinin özür mü yönerge mi olduğu,
arayüzün sessiz mi teşvik edici mi konuştuğu. Sonraki ders bu kararların dayanağını kurar;
ilkelerin hangi koşulda karar verdirdiğini, hangi ilkenin boşta kaldığını ve ses tonu
sözleşmesinin nasıl denetleneceğini bir matris üzerinde hesaplar.

### Tasarım Dili

Source: https://academia.sh/tr/kurslar/tasarim-sistemleri/tasarim-dili

Tasarım ilkelerinin ayırt edicilik testiyle sınanması, ilke–karar matrisinden boşta ilke ve dayanaksız karar çıkarımı, ilke çatışmalarının okunması ve ses tonu sözleşmesinin denetlenmesi.

Önceki dört ders sistemin sayısal iskeletini kurdu: kapsam, gerekçe, envanter ve ölçek.
İskelet neyin kullanılabileceğini söylüyor, hangi seçeneğin doğru olduğunu söylemiyor. Aynı
aralık ölçeğiyle iki ayrı arayüz kurulabilir; biri yoğun ve sessiz, diğeri ferah ve
yönlendirici olur. İkisi de ölçeğe uyar.

**Tasarım dili (design language)**, ölçeklerin bıraktığı bu boşluğu dolduran karar
dayanaklarıdır: ilkeler, ses tonu ve marka ifadesi. Bu dersin sorusu, bu dayanakların
denetlenebilir olup olmadığıdır. Bir ilke ancak bir seçeneği reddedebiliyorsa ilkedir; bir
ses tonu ancak ihlali gösterilebiliyorsa sözleşmedir.

#### İlke, Tersi Savunulabilen Cümledir

Bir tasarım ilkesinin işi karar verdirmektir. Karar verdirmenin ölçütü şudur: ilkenin tersi
de savunulabilir bir konum olmalıdır. "Kullanıcıyı önemseriz" bir ilke değildir, çünkü
tersini savunan kimse yoktur; hiçbir seçeneği elemez, dolayısıyla hiçbir tartışmayı kapatmaz.
"Kayıt önce, araç sonra" bir ilkedir, çünkü tersi — arayüzün önce kendi araçlarını göstermesi
— gerçekten savunulan bir konumdur.

Aynı ölçüt tutarlılık için de geçerlidir. Yinelenme ve Tutarlılık dersinde tutarlılığın
değeri gösterildi, ama tutarlılık bir tasarım **ilkesi** değildir; sistemin varlık koşuludur.
İlkeler sistemin içinde hangi seçeneğin alınacağını belirler.

İkinci ölçüt ilkenin kararlara değdiğinin gösterilmesidir. Aşağıdaki program iki denetimi
birden yapar: ilkeleri tersinin savunulabilirliğiyle sınar ve ilkelerle kararlar arasındaki
örtüşmeyi çıkarır. Üçüncü blokta ses tonu sözleşmesi denetlenir.

```js
// dil.mjs — tasarim ilkelerinin ayirt ediciligi, ilke-karar ortusmesi ve ses tonu denetimi

// 1) Ayirt edicilik: bir ilkenin tersi savunulabiliyorsa ilke bir karar verdirir.
// "tersi" alani, ilkenin karsitinin yazili halidir; savunulabilir alani insan degerlendirmesidir.
const ILKELER = [
  { ad: "kayit once", tersi: "arac once", savunulabilir: true },
  { ad: "sessiz arayuz", tersi: "yonlendiren arayuz", savunulabilir: true },
  { ad: "geri alinabilirlik", tersi: "onaya dayali koruma", savunulabilir: true },
  { ad: "yogun liste", tersi: "ferah liste", savunulabilir: true },
  { ad: "kullaniciyi onemseriz", tersi: "kullaniciyi onemsemeyiz", savunulabilir: false },
  { ad: "tutarli olmak", tersi: "tutarsiz olmak", savunulabilir: false },
  { ad: "erisilebilirlik pazarlik disi", tersi: "erisilebilirlik ozellige gore", savunulabilir: true },
];

console.log("ilke                           tersi                          ayirt edici mi");
for (const i of ILKELER) {
  console.log(`${i.ad.padEnd(30)} ${i.tersi.padEnd(30)} ${i.savunulabilir ? "evet" : "HAYIR"}`);
}
const ayirtEdici = ILKELER.filter((i) => i.savunulabilir);
console.log(`ayirt edici ilke sayisi: ${ayirtEdici.length} / ${ILKELER.length}`);

// 2) Ilke-karar matrisi. Her karar icin hangi ilkenin hangi yonde etki ettigi yazilir.
// yon: +1 karari destekler, -1 karsi cikar.
const KARARLAR = [
  { ad: "kayit basligi kapak gorselinden buyuk", etki: { "kayit once": 1, "yogun liste": 1 } },
  { ad: "sonuc listesi yogun dizilir", etki: { "yogun liste": 1, "sessiz arayuz": 1 } },
  { ad: "odunc alma tek tiklamayla, geri alinabilir", etki: { "geri alinabilirlik": 1, "sessiz arayuz": 1 } },
  { ad: "kayit silme onay ister", etki: { "geri alinabilirlik": -1, "sessiz arayuz": -1 } },
  { ad: "suzgec paneli her zaman acik", etki: { "yogun liste": -1, "kayit once": 1 } },
  { ad: "odak gostergesi her bilesende", etki: { "erisilebilirlik pazarlik disi": 1 } },
  { ad: "bos durumda oneri listesi gosterilir", etki: { "sessiz arayuz": -1 } },
  { ad: "kapak gorselleri kucuk kucuk resim olarak", etki: { "kayit once": 1, "yogun liste": 1 } },
  { ad: "ust menu sabit kalir", etki: {} },
  { ad: "sayfalama yerine surekli akis", etki: {} },
];

console.log("\nilke                           destekledigi  karsi ciktigi  toplam");
for (const i of ILKELER) {
  const arti = KARARLAR.filter((k) => k.etki[i.ad] === 1).length;
  const eksi = KARARLAR.filter((k) => k.etki[i.ad] === -1).length;
  console.log(
    `${i.ad.padEnd(30)} ${String(arti).padStart(12)} ${String(eksi).padStart(14)} ${String(arti + eksi).padStart(7)}`
  );
}

const bostaIlke = ILKELER.filter((i) => !KARARLAR.some((k) => i.ad in k.etki));
const dayanaksiz = KARARLAR.filter((k) => Object.keys(k.etki).length === 0);
const catisan = KARARLAR.filter((k) => {
  const y = Object.values(k.etki);
  return y.includes(1) && y.includes(-1);
});
const gerilim = KARARLAR.filter((k) => Object.values(k.etki).every((v) => v === -1) && Object.keys(k.etki).length > 0);

console.log(`\nbosta kalan ilke (hicbir karari belirlemedi): ${bostaIlke.map((i) => i.ad).join(", ") || "yok"}`);
console.log(`dayanaksiz karar (hicbir ilkeye baglanmadi): ${dayanaksiz.map((k) => k.ad).join(", ") || "yok"}`);
console.log(`ic catisma tasiyan karar: ${catisan.map((k) => k.ad).join(", ") || "yok"}`);
console.log(`ilkelere ragmen verilmis karar: ${gerilim.map((k) => k.ad).join(", ") || "yok"}`);

// 3) Ses tonu sozlesmesinin denetimi. Kurallar denetlenebilir olmak zorundadir.
const METINLER = [
  { yer: "arama bos sonuc", metin: "Aramaniz sonuc vermedi. Yazar adiyla ya da ISBN ile arayin." },
  { yer: "odunc basarili", metin: "Odunc alindi. Son teslim 12 Mart." },
  { yer: "odunc hatasi", metin: "Uzgunuz, maalesef bir hata olustu. Lutfen tekrar deneyin." },
  { yer: "gecikme uyarisi", metin: "Teslim tarihi gecti. Uzatmak icin uzat dugmesini kullanin." },
  { yer: "alan dogrulama", metin: "Gecersiz giris yaptiniz." },
  { yer: "silme onayi", metin: "Kayit silinecek. Bu islem geri alinamaz. Silmek icin onaylayin." },
  { yer: "raf bos durumu", metin: "Bu rafta kayit yok. Kayit eklemek icin rafi secip ekle dugmesini kullanin ve ardindan listeden bir eser secin." },
];

const YASAK = ["uzgunuz", "maalesef", "gecersiz", "yaptiniz", "lutfen"];
const EYLEM_FIILI = ["arayin", "kullanin", "onaylayin", "secin", "deneyin", "ekleyin"];
const EN_COK_KELIME = 14;

console.log("\nyer               kelime  yasak sozcuk             eylem  unlem  sonuc");
let ihlalli = 0;
for (const m of METINLER) {
  const kucuk = m.metin.toLowerCase();
  const kelime = m.metin.trim().split(/\s+/).length;
  const yasak = YASAK.filter((y) => kucuk.includes(y));
  const eylem = EYLEM_FIILI.some((f) => kucuk.includes(f));
  const unlem = m.metin.includes("!");
  const ihlaller = [];
  if (kelime > EN_COK_KELIME) ihlaller.push("uzun");
  if (yasak.length) ihlaller.push("yasak");
  if (!eylem) ihlaller.push("eylemsiz");
  if (unlem) ihlaller.push("unlem");
  if (ihlaller.length) ihlalli++;
  console.log(
    `${m.yer.padEnd(17)} ${String(kelime).padStart(6)}  ${(yasak.join(",") || "-").padEnd(23)} ` +
      `${(eylem ? "var" : "YOK").padEnd(6)} ${(unlem ? "var" : "-").padEnd(6)} ${ihlaller.join("+") || "gecti"}`
  );
}
console.log(`ihlal tasiyan metin: ${ihlalli} / ${METINLER.length}`);
```

```
ilke                           tersi                          ayirt edici mi
kayit once                     arac once                      evet
sessiz arayuz                  yonlendiren arayuz             evet
geri alinabilirlik             onaya dayali koruma            evet
yogun liste                    ferah liste                    evet
kullaniciyi onemseriz          kullaniciyi onemsemeyiz        HAYIR
tutarli olmak                  tutarsiz olmak                 HAYIR
erisilebilirlik pazarlik disi  erisilebilirlik ozellige gore  evet
ayirt edici ilke sayisi: 5 / 7

ilke                           destekledigi  karsi ciktigi  toplam
kayit once                                3              0       3
sessiz arayuz                             2              2       4
geri alinabilirlik                        1              1       2
yogun liste                               3              1       4
kullaniciyi onemseriz                     0              0       0
tutarli olmak                             0              0       0
erisilebilirlik pazarlik disi             1              0       1

bosta kalan ilke (hicbir karari belirlemedi): kullaniciyi onemseriz, tutarli olmak
dayanaksiz karar (hicbir ilkeye baglanmadi): ust menu sabit kalir, sayfalama yerine surekli akis
ic catisma tasiyan karar: suzgec paneli her zaman acik
ilkelere ragmen verilmis karar: kayit silme onay ister, bos durumda oneri listesi gosterilir

yer               kelime  yasak sozcuk             eylem  unlem  sonuc
arama bos sonuc       10  -                       var    -      gecti
odunc basarili         6  -                       YOK    -      eylemsiz
odunc hatasi           8  uzgunuz,maalesef,lutfen var    -      yasak
gecikme uyarisi        8  -                       var    -      gecti
alan dogrulama         3  gecersiz,yaptiniz       YOK    -      yasak+eylemsiz
silme onayi            9  -                       var    -      gecti
raf bos durumu        18  -                       var    -      uzun
ihlal tasiyan metin: 4 / 7
```

#### İki Denetim, Dört Bulgu

İlk tablo yedi aday ilkeden ikisini eliyor. "Kullanıcıyı önemseriz" ve "tutarlı olmak"
tersleri savunulamadığı için ayırt edici değil. İkinci tablo aynı iki ilkeyi ikinci kez
eliyor: hiçbir kararı desteklemiyor ve hiçbirine karşı çıkmıyorlar, toplam etkileri sıfır.
İki bağımsız ölçütün aynı iki ilkeyi işaretlemesi rastlantı değildir; tersi savunulamayan bir
cümle zaten hiçbir seçeneği eleyemez.

**Boşta kalan ilke** listeye alınmaz. Bir ilkeler belgesinde her satırın bir bedeli vardır:
okunma zamanı, ezberlenme yükü ve yeni gelen birinin onu ciddiye alarak boşuna uygulamaya
çalışması. Etkisiz bir ilke belgeyi seyreltir ve etkili olanların da ciddiyetini düşürür.

**Dayanaksız karar** ters yöndeki bulgudur: üst menünün sabit kalması ve sayfalama yerine
sürekli akış kullanılması hiçbir ilkeye bağlanamıyor. Bu, kararların yanlış olduğu anlamına
gelmez; dayanaklarının yazılı olmadığı anlamına gelir. İki olasılık vardır. Ya kararlar
gerçekten dayanaksızdır ve ilk itirazda düşer, ya da ilkeler listesi eksiktir ve yazılmamış
bir ilke bu kararları belirlemektedir. İkinci durumda eksik ilke bulunup yazılır; denetimin
en verimli çıktısı budur.

#### Çatışma Bir Kusur Değildir

Üçüncü bulgu, süzgeç panelinin her zaman açık durmasıdır. Bu karar bir ilkeyi destekliyor
(kayıt önce: süzgeçler kayda ulaşmayı hızlandırır) ve bir ilkeye karşı çıkıyor (yoğun liste:
panel liste alanını daraltır).

Böyle bir çatışma sistemin kusuru değil, ilkelerin çalıştığının kanıtıdır. Çatışmayan
ilkeler kümesi genellikle birbirinden bağımsız cümlelerdir ve gerçek ödünleşimleri
adlandırmaz. Gerekli olan, çatışmayı kaldırmak değil **öncelik sırasını** yazmaktır: hangi
ilke hangisine üstün gelir. Katalog kurumunda erişilebilirlik pazarlık dışıdır, sonra kayıt
önce gelir, yoğunluk en sonda kalır. Bu sıra yazıldığında süzgeç paneli kararı tartışmasız
hâle gelir.

Dördüncü bulgu ayrı bir kategoridir: kayıt silmenin onay istemesi ve boş durumda öneri
listesi gösterilmesi kararları yalnızca ilkelere **karşı** duruyor. Bu, kararların
gerekçesinin ilkeler dışında bir yerden geldiğini gösterir — silme onayı bir veri kaybı
riskinden, öneri listesi bir kullanılabilirlik bulgusundan gelir. Bunlar meşru gerekçelerdir,
ancak ilkeler belgesinde görünmedikleri için kayda geçirilmeleri gerekir. Aksi hâlde ilkeleri
uygulayan biri bu kararları "sisteme aykırı" bulup kaldırır.

#### Ses Tonu Sözleşmeye Çevrilir

Son tablo tasarım dilinin ikinci parçasını ele alıyor. **Ses tonu (tone of voice)** çoğu
belgede sıfatlarla anlatılır — açık, saygılı, yardımsever — ve bu sıfatlar hiçbir metni
reddedemez. Sözleşmeye çevrilmesi, sıfatların denetlenebilir kurallara indirgenmesi demektir:
en fazla kaç kelime, hangi sözcükler kullanılmaz, her ileti bir eylem önerir mi, ünlem
kullanılır mı.

Yedi metinden dördü ihlal taşıyor. "Ödünç hatası" iletisi üç yasak sözcük içeriyor: özür ve
rica kalıpları bilgi taşımaz, iletiyi uzatır ve hatanın kullanıcıdan kaynaklandığı izlenimini
azaltmak yerine belirsizleştirir. "Alan doğrulama" iletisi hem yasaklı sözcük taşıyor hem de
ne yapılacağını söylemiyor: üç kelimede iki ihlal. "Raf boş durumu" metni on sekiz kelimeyle
sınırı aşıyor.

Dördüncü ihlal ise kuralın kendisini sorgulatıyor. "Ödünç alındı. Son teslim 12 Mart."
iletisi eylem önermediği için işaretleniyor, ama bu ileti bir onaydır; kullanıcıdan bir şey
istemez. Kural burada yanlış pozitif üretiyor.

Bu, ses tonu denetiminin genel sınırıdır. Kurallar metnin **türünü** bilmez. Düzeltme, kuralı
gevşetmek değil türe bağlamaktır: eylem önerisi zorunluluğu hata ve boş durum iletileri için
geçerlidir, onay iletileri için değildir. Denetim kuralları, uygulandıkları metin türüyle
birlikte tanımlanmadığında ya gereksiz uyarı üretir ya da gerçek ihlalleri kaçırır.

#### Dilin Belirteçlerde Karşılığı

Tasarım dilinin son parçası marka ifadesidir ve burada sistemin sayısal katmanına bağlanır.
Bir ilkenin sistemde karşılığı olması, o ilkenin bir belirteç ailesini kısıtlaması demektir.

"Yoğun liste" ilkesi aralık ölçeğinin hangi adımlarının liste bağlamında kullanılacağını
kısıtlar. "Sessiz arayüz" renk sisteminin doygunluk seçimini ve vurgu rollerinin sayısını
kısıtlar. "Erişilebilirlik pazarlık dışı" kontrast eşiklerini bir tercih olmaktan çıkarır.

Bazı ilkelerin ise belirteç karşılığı yoktur: "geri alınabilirlik" bir sayı değil bir
davranış kuralıdır ve önceki derste tanımlanan kural katmanında yaşar. Bu ayrımın yapılması
gerekir, çünkü belirteç katmanına sığmayan bir ilkeyi belirteçle ifade etme çabası, anlamı
taşımayan belirteçler üretir.

Marka ifadesi bu iki kanalın toplamıdır: sayısal katmanda kısıtlanmış değerler, kural
katmanında yazılmış davranışlar. Kurumun sesi bunların ikisinden birden çıkar; tek başına
bir renk ya da tek başına bir cümle kalıbı marka üretmez.

#### Özet

- Bir tasarım ilkesi ancak tersi savunulabilir bir konumsa ilkedir; tersi savunulamayan cümle
  hiçbir seçeneği eleyemez ve ilke–karar matrisinde sıfır etki gösterir. Boşta kalan ilke
  belgeden çıkarılır; dayanaksız karar ya gerekçesizdir ya da yazılmamış bir ilkeye işaret
  eder ve ikinci durum denetimin en verimli çıktısıdır.
- İlke çatışması kusur değil, ilkelerin gerçek ödünleşimleri adlandırdığının kanıtıdır;
  çözüm çatışmayı kaldırmak değil öncelik sırasını yazmaktır.
- Yalnızca ilkelere karşı duran kararlar başka bir gerekçeden gelir ve o gerekçe kayda
  geçirilmezse sonradan sisteme aykırı sayılıp kaldırılır.
- Ses tonu, sıfatlardan denetlenebilir kurallara indirgendiğinde sözleşme olur: kelime
  sınırı, yasak sözcük listesi, eylem önerisi ve noktalama kuralı.
- Denetim kuralları metin türüne bağlanmadığında yanlış pozitif üretir; onay iletisinden eylem
  önerisi beklemek buna örnektir.
- Bir ilkenin sistemde karşılığı ya bir belirteç ailesini kısıtlamasıdır ya da kural
  katmanında yazılmasıdır; ikisine de sığmayan ilke uygulanamaz.

#### Sonraki Adım

Sistemin kuruluş kararları burada tamamlanıyor: kapsam çizildi, gerekçe hesaplandı, envanter
çıkarıldı, ölçek stratejisi seçildi ve tasarım dili denetlenebilir hâle geldi. Bundan sonrası
bu kararların makinede yaşayan karşılığıdır. Ölçek adımlarının, renk rollerinin ve düzen
kararlarının adlandırılmış değerlere dönüşmesi gerekiyor; bu adların hangi katmanda durduğu,
birbirine nasıl başvurduğu ve hangi başvuruların yasak olduğu ayrı bir mimari sorudur.
Sonraki konu belirteçlerle başlar: ilk ders üç katmanı tanımlar, katmanlar arası başvuru
kurallarını koyar ve belirteç grafiğini döngü, boşta kalan belirteç ve katman ihlali için
çözümler.

## Belirteçler ve Temel Katman

Tasarım belirteçlerinin ilkel, anlamsal ve bileşen katmanları; renk, tipografi, aralık ve boyut belirteçleri; kesme noktası ile ızgara kararlarının sistemleşmesi ve tema türetimi.

### Tasarım Belirteçleri

Source: https://academia.sh/tr/kurslar/tasarim-sistemleri/tasarim-belirtecleri

Belirteçlerin ilkel, anlamsal ve bileşen katmanlarına ayrılması, katmanlar arası başvuru kurallarının konması ve belirteç grafiğinin döngü, tanımsız başvuru, katman ihlali ile boşta kalan belirteç için çözümlenmesi.

Sistemin kuruluş kararları tamamlandı: kapsam çizildi, ölçekler seçildi, tasarım dili
denetlenebilir hâle geldi. Bu kararların hepsi şu ana kadar metin olarak duruyor. Bir arayüz
metni okuyamaz; adlandırılmış değerler okur.

**Tasarım belirteci (design token)**, bir tasarım kararının makine tarafından okunabilen
adlandırılmış karşılığıdır. Tasarım Sistemi Nedir dersinde adlandırmanın değişiklik yeri
sayısını 3.88 kat azalttığı hesaplanmıştı; bu ders o adların nasıl düzenleneceğini kurar.
Sorun tek bir liste tutmak değildir — tek bir liste, bir süre sonra hangi adın hangi amaçla
konduğu belirsizleşen bir sözlüğe döner. Belirteçler katmanlanır ve katmanlar arası
başvurular kurala bağlanır.

#### Üç Katman, İki Kural

**İlkel katman** ham değerleri taşır: renk basamakları, ölçek adımları, boyut basamakları.
İlkel belirteç bir sayıdır ya da bir renktir; bir amaç taşımaz. `notr-500` bir gri tonudur ve
nerede kullanılacağını söylemez.

**Anlamsal katman** bir işi adlandırır ve bir ilkel belirtece bağlar. `kenarlik` bir iştir;
hangi gri olduğu ikinci sorudur. Renk Sistemi dersinde bu ayrım renkte kurulmuştu; belirteç
mimarisi aynı ayrımı aralık, boyut ve yarıçap için de kurar.

**Bileşen katmanı** tek bir bileşenin ayar noktalarını açar ve bir anlamsal belirtece bağlar.
`dugme-zemin` bir ayar noktasıdır. Özel Özellikler dersinde bileşenin dışarıya açtığı adlara
**biçem arayüzü** deniyordu; bileşen belirteçleri o arayüzün sistem düzeyindeki karşılığıdır.

Katmanlar arasındaki başvurular iki kurala bağlıdır. İlkel katman hiçbir belirtece başvurmaz;
yalnız değişmez değer taşır. Bileşen katmanı yalnız anlamsal katmana başvurur; ilkel katmana
doğrudan başvurmak yasaktır. Anlamsal katman ilkel ya da başka bir anlamsal belirtece
başvurabilir.

İkinci kuralın gerekçesi Renk Sistemi dersinde verilmişti: bir bileşen `--notr-600` yazdığı
anda sistem delinir, çünkü o bileşen artık rol yeniden atamasından ve tema değişiminden
etkilenmez. Kural yazılı olduğunda ihlali denetlenebilir hâle gelir.

```js
// belirtec.mjs — uc katmanli belirtec grafiginin cozumlenmesi ve denetimi

// Katman kurallari:
//   ilkel    -> yalniz degismez deger tasir, baska belirtece basvuramaz
//   anlamsal -> ilkel ya da anlamsal belirtece basvurur
//   bilesen  -> yalniz anlamsal belirtece basvurur
const IZIN = { ilkel: [], anlamsal: ["ilkel", "anlamsal"], bilesen: ["anlamsal"] };

const BELIRTECLER = {
  // --- ilkel katman ---
  "notr-000": { katman: "ilkel", deger: "#ffffff" },
  "notr-500": { katman: "ilkel", deger: "#757e8a" },
  "notr-600": { katman: "ilkel", deger: "#5e656e" },
  "notr-900": { katman: "ilkel", deger: "#212327" },
  "ana-100": { katman: "ilkel", deger: "#dee9f7" },
  "ana-600": { katman: "ilkel", deger: "#275ea5" },
  "ana-800": { katman: "ilkel", deger: "#15335b" },
  "adim-1": { katman: "ilkel", deger: "4px" },
  "adim-2": { katman: "ilkel", deger: "8px" },
  "adim-3": { katman: "ilkel", deger: "12px" },
  "adim-4": { katman: "ilkel", deger: "16px" },
  "adim-6": { katman: "ilkel", deger: "24px" },
  "basamak-0": { katman: "ilkel", deger: "16px" },
  "basamak-1": { katman: "ilkel", deger: "20px" },
  "yaricap-1": { katman: "ilkel", deger: "4px" },

  // --- anlamsal katman ---
  yuzey: { katman: "anlamsal", deger: "{notr-000}" },
  "metin-birincil": { katman: "anlamsal", deger: "{notr-900}" },
  "metin-ikincil": { katman: "anlamsal", deger: "{notr-600}" },
  kenarlik: { katman: "anlamsal", deger: "{notr-500}" },
  "eylem-ana": { katman: "anlamsal", deger: "{ana-600}" },
  "eylem-ana-uzeri": { katman: "anlamsal", deger: "{notr-000}" },
  "vurgu-soluk": { katman: "anlamsal", deger: "{ana-100}" },
  "vurgu-soluk-uzeri": { katman: "anlamsal", deger: "{ana-800}" },
  "aralik-grup-ici": { katman: "anlamsal", deger: "{adim-2}" },
  "aralik-blok-arasi": { katman: "anlamsal", deger: "{adim-4}" },
  "aralik-bolum-arasi": { katman: "anlamsal", deger: "{adim-6}" },
  "govde-boyutu": { katman: "anlamsal", deger: "{basamak-0}" },
  "baslik-boyutu": { katman: "anlamsal", deger: "{basamak-1}" },
  "yaricap-varsayilan": { katman: "anlamsal", deger: "{yaricap-1}" },
  "uyari-zemini": { katman: "anlamsal", deger: "{uyari-kenarligi}" },   // dongu
  "uyari-kenarligi": { katman: "anlamsal", deger: "{uyari-zemini}" },   // dongu
  "aralik-sikisik": { katman: "anlamsal", deger: "{adim-1}" },          // hicbir yerden basvurulmuyor

  // --- bilesen katmani ---
  "dugme-zemin": { katman: "bilesen", deger: "{eylem-ana}" },
  "dugme-metin": { katman: "bilesen", deger: "{eylem-ana-uzeri}" },
  "dugme-ic-bosluk-y": { katman: "bilesen", deger: "{aralik-grup-ici}" },
  "dugme-ic-bosluk-x": { katman: "bilesen", deger: "{aralik-blok-arasi}" },
  "dugme-yaricap": { katman: "bilesen", deger: "{yaricap-varsayilan}" },
  "kart-zemin": { katman: "bilesen", deger: "{yuzey}" },
  "kart-kenarlik": { katman: "bilesen", deger: "{notr-500}" },          // katman ihlali: ilkele dogrudan
  "kart-ic-bosluk": { katman: "bilesen", deger: "{aralik-blok-arasi}" },
  "kayit-baslik-boyutu": { katman: "bilesen", deger: "{baslik-boyutu}" },
  "kayit-ustveri-rengi": { katman: "bilesen", deger: "{metin-ucuncul}" }, // tanimsiz basvuru
  "etiket-zemin": { katman: "bilesen", deger: "{vurgu-soluk}" },
  "etiket-metin": { katman: "bilesen", deger: "{vurgu-soluk-uzeri}" },
};

const BASVURU = /^\{([\w-]+)\}$/;
const hedef = (ad) => {
  const m = BELIRTECLER[ad].deger.match(BASVURU);
  return m ? m[1] : null;
};

// --- cozumleme ----------------------------------------------------------------
function coz(ad) {
  const gorulen = new Set();
  let su = ad;
  const zincir = [ad];
  while (true) {
    if (!(su in BELIRTECLER)) return { deger: null, neden: `tanimsiz: ${su}`, zincir };
    if (gorulen.has(su)) return { deger: null, neden: "dongu", zincir };
    gorulen.add(su);
    const h = hedef(su);
    if (h === null) return { deger: BELIRTECLER[su].deger, neden: null, zincir };
    su = h;
    zincir.push(su);
  }
}

// --- denetimler ---------------------------------------------------------------
const kirik = [];
const katmanIhlali = [];
for (const ad of Object.keys(BELIRTECLER)) {
  const c = coz(ad);
  if (c.neden) kirik.push({ ad, ...c });
  const h = hedef(ad);
  if (h === null) continue;
  const kendi = BELIRTECLER[ad].katman;
  const hedefKatman = h in BELIRTECLER ? BELIRTECLER[h].katman : null;
  if (hedefKatman && !IZIN[kendi].includes(hedefKatman)) {
    katmanIhlali.push({ ad, kendi, h, hedefKatman });
  }
}

// Bilesen bicem dosyalarinda dogrudan okunan belirtecler. Her anlamsal belirtecin bir
// bilesen belirteci karsiligi olmak zorunda degildir; bilesen katmani yalniz ayar noktasi
// acilan yerlerde kurulur.
const DOGRUDAN_KULLANIM = new Set([
  "metin-birincil", "metin-ikincil", "kenarlik", "govde-boyutu", "aralik-bolum-arasi",
  "aralik-grup-ici", "yuzey",
]);

// Basvurulma sayisi: bir belirtece kac belirtecten basvuruluyor?
const gelen = Object.fromEntries(Object.keys(BELIRTECLER).map((a) => [a, 0]));
for (const ad of Object.keys(BELIRTECLER)) {
  const h = hedef(ad);
  if (h && h in gelen) gelen[h]++;
}
// Bilesen katmani yaprak sayilir; ilkel ve anlamsal katmanda gelen basvurusu olmayan
// belirtec bosta kalmistir.
const bosta = Object.keys(BELIRTECLER).filter(
  (a) => BELIRTECLER[a].katman !== "bilesen" && gelen[a] === 0 && !DOGRUDAN_KULLANIM.has(a)
);

const sayim = { ilkel: 0, anlamsal: 0, bilesen: 0 };
for (const a of Object.keys(BELIRTECLER)) sayim[BELIRTECLER[a].katman]++;
console.log("katman     belirtec sayisi");
for (const k of ["ilkel", "anlamsal", "bilesen"]) console.log(`${k.padEnd(10)} ${String(sayim[k]).padStart(15)}`);
console.log(`toplam     ${String(Object.keys(BELIRTECLER).length).padStart(15)}`);

console.log("\nbilesen belirteci        zincir                                              deger");
for (const ad of Object.keys(BELIRTECLER).filter((a) => BELIRTECLER[a].katman === "bilesen")) {
  const c = coz(ad);
  console.log(
    `${ad.padEnd(24)} ${c.zincir.join(" > ").padEnd(51)} ${c.deger === null ? "COZULEMEDI (" + c.neden + ")" : c.deger}`
  );
}

console.log("\ncozulemeyen belirtecler");
for (const k of kirik) console.log(`  ${k.ad.padEnd(24)} ${k.neden.padEnd(24)} zincir: ${k.zincir.join(" > ")}`);

console.log("\nkatman ihlalleri");
for (const i of katmanIhlali) {
  console.log(`  ${i.ad} (${i.kendi}) -> ${i.h} (${i.hedefKatman});  izin verilen: ${IZIN[i.kendi].join(", ")}`);
}

console.log("\nbosta kalan belirtecler (ne belirtecten ne bicem dosyasindan basvuru var)");
for (const a of bosta) console.log(`  ${a.padEnd(24)} katman: ${BELIRTECLER[a].katman}`);

// --- zincir uzunlugu dagilimi --------------------------------------------------
const uzunluk = {};
for (const ad of Object.keys(BELIRTECLER)) {
  const c = coz(ad);
  if (c.neden) continue;
  uzunluk[c.zincir.length] = (uzunluk[c.zincir.length] || 0) + 1;
}
console.log("\nzincir uzunlugu  belirtec sayisi");
for (const u of Object.keys(uzunluk).sort((a, b) => a - b)) {
  console.log(`${u.padStart(15)} ${String(uzunluk[u]).padStart(16)}`);
}

// --- katman gecisleri ----------------------------------------------------------
const gecis = {};
for (const ad of Object.keys(BELIRTECLER)) {
  const h = hedef(ad);
  if (!h || !(h in BELIRTECLER)) continue;
  const anahtar = `${BELIRTECLER[ad].katman} -> ${BELIRTECLER[h].katman}`;
  gecis[anahtar] = (gecis[anahtar] || 0) + 1;
}
console.log("\nkatman gecisi           basvuru  izinli mi");
for (const [g, n] of Object.entries(gecis).sort()) {
  const [kaynak, varis] = g.split(" -> ");
  console.log(`${g.padEnd(23)} ${String(n).padStart(7)}  ${IZIN[kaynak].includes(varis) ? "evet" : "HAYIR"}`);
}
```

```
katman     belirtec sayisi
ilkel                   15
anlamsal                17
bilesen                 12
toplam                  44

bilesen belirteci        zincir                                              deger
dugme-zemin              dugme-zemin > eylem-ana > ana-600                   #275ea5
dugme-metin              dugme-metin > eylem-ana-uzeri > notr-000            #ffffff
dugme-ic-bosluk-y        dugme-ic-bosluk-y > aralik-grup-ici > adim-2        8px
dugme-ic-bosluk-x        dugme-ic-bosluk-x > aralik-blok-arasi > adim-4      16px
dugme-yaricap            dugme-yaricap > yaricap-varsayilan > yaricap-1      4px
kart-zemin               kart-zemin > yuzey > notr-000                       #ffffff
kart-kenarlik            kart-kenarlik > notr-500                            #757e8a
kart-ic-bosluk           kart-ic-bosluk > aralik-blok-arasi > adim-4         16px
kayit-baslik-boyutu      kayit-baslik-boyutu > baslik-boyutu > basamak-1     20px
kayit-ustveri-rengi      kayit-ustveri-rengi > metin-ucuncul                 COZULEMEDI (tanimsiz: metin-ucuncul)
etiket-zemin             etiket-zemin > vurgu-soluk > ana-100                #dee9f7
etiket-metin             etiket-metin > vurgu-soluk-uzeri > ana-800          #15335b

cozulemeyen belirtecler
  uyari-zemini             dongu                    zincir: uyari-zemini > uyari-kenarligi > uyari-zemini
  uyari-kenarligi          dongu                    zincir: uyari-kenarligi > uyari-zemini > uyari-kenarligi
  kayit-ustveri-rengi      tanimsiz: metin-ucuncul  zincir: kayit-ustveri-rengi > metin-ucuncul

katman ihlalleri
  kart-kenarlik (bilesen) -> notr-500 (ilkel);  izin verilen: anlamsal

bosta kalan belirtecler (ne belirtecten ne bicem dosyasindan basvuru var)
  adim-3                   katman: ilkel
  aralik-sikisik           katman: anlamsal

zincir uzunlugu  belirtec sayisi
              1               15
              2               16
              3               10

katman gecisi           basvuru  izinli mi
anlamsal -> anlamsal          2  evet
anlamsal -> ilkel            15  evet
bilesen -> anlamsal          10  evet
bilesen -> ilkel              1  HAYIR
```

#### Grafik Dört Kusur Türü Üretir

Belirteç kümesi bir grafiktir: düğümler adlar, kenarlar başvurulardır. Grafik olarak
görüldüğü anda üzerinde denetim yapılabilir hâle gelir ve dört ayrı kusur türü ortaya çıkar.

**Döngü.** `uyari-zemini` ile `uyari-kenarligi` birbirine başvuruyor. İkisi de hiçbir değer
üretmiyor. Özel Özellikler dersinde CSS düzeyinde aynı davranış gösterilmişti: birbirine
başvuran iki özel özellik tanımsız sayılır. Fark şudur ki oradaki sonuç çalışma zamanında
ortaya çıkar; buradaki denetim yayından önce çalışır.

**Tanımsız başvuru.** `kayit-ustveri-rengi` belirteci `metin-ucuncul` adına başvuruyor ve
böyle bir belirteç yok. Bu genellikle bir adın sonradan değiştirilmesinden ya da bir
belirtecin taslakta kalmasından doğar. Denetim çalışmadığında bileşen sessizce yanlış renge
düşer, çünkü tanımsız bir özel özellik hesaplanan değerde geçersizlik üretir ve renk ana
ögeden kalıtılır.

**Katman ihlali.** `kart-kenarlik` bileşen katmanında ama doğrudan `notr-500` ilkel
belirtecine başvuruyor. Sonuç doğru bir renk üretiyor — çıktıda `#757e8a` görünüyor — ve bu
kusuru tehlikeli kılan da budur. Değer doğru olduğu için hiçbir görsel belirti vermez.
Etkisi ancak tema değiştiğinde ortaya çıkar: kart kenarlığı koyu temada açık temanın grisinde
kalır, çünkü rol katmanını atlamıştır.

**Boşta kalan belirteç.** `adim-3` ve `aralik-sikisik` ne bir belirteçten ne bir biçem
dosyasından başvuru alıyor. Bunlar zararsız görünür ama sistemin okunmasını zorlaştırır: bir
liste ne kadar çok kullanılmayan ad taşırsa, kullananların hangisini seçeceği o kadar
belirsizleşir. Boşta kalan belirteç ya kaldırılır ya da neden durduğu yazılır.

#### Her Anlamsal Belirteç Bileşen Belirteci İstemez

Boşta kalan belirteç denetimi bir ayrım gerektiriyor ve bu ayrım mimari bir karardır.

`metin-birincil`, `kenarlik`, `govde-boyutu` gibi anlamsal belirteçlerin hiçbir bileşen
belirteci karşılığı yok. Bunlar biçem dosyalarında doğrudan okunuyor. Kod bunları
`DOGRUDAN_KULLANIM` kümesiyle biliyor ve boşta kalan sayılmıyorlar.

Ayrım şudur: bileşen katmanı **her** anlamsal belirteç için kurulmaz, yalnız bir **ayar
noktası** açılan yerlerde kurulur. `dugme-zemin` vardır, çünkü düğmenin varyantları zemini
değiştirir. `kayit-baslik-boyutu` vardır, çünkü kayıt kartının yoğun ve seyrek sürümleri
başlık boyutunu farklı seçer. Buna karşılık gövde metninin rengi hiçbir bileşende ayarlanmaz;
onun için bileşen belirteci açmak, hiçbir zaman kullanılmayacak bir ayar noktası üretir.

Katman geçişi tablosu bu dengeyi sayıyor: 10 bileşen belirteci anlamsal katmana başvuruyor,
15 anlamsal belirteç ilkel katmana. Bileşen katmanı anlamsal katmandan küçüktür ve olması
gereken de budur. Bileşen katmanı anlamsal katmanla aynı boyuta ulaştıysa, ya her rol
gereksiz yere sarmalanmıştır ya da anlamsal katman eksik kurulmuştur.

#### Zincir Uzunluğu Bir Ödünleşimdir

Zincir uzunluğu dağılımı üç değer gösteriyor: 15 belirteç doğrudan değer taşıyor, 16'sı bir
adım sonra, 10'u iki adım sonra çözülüyor. Grafiğin derinliği üçle sınırlı.

Bu bir tesadüf değil, katman sayısının doğrudan sonucudur. Üç katmanlı bir sistemde en uzun
zincir bileşen → anlamsal → ilkel olur. Anlamsal katmanın kendi içinde başvuru yapması bu
sınırı esnetir — çıktıda iki anlamsal-anlamsal başvuru var — ve her esneme bir bedel taşır.

Bedel, izlenebilirliktir. Bir bileşenin neden o rengi aldığını anlamak için zincirin tamamı
takip edilir. Üç adımlık bir zincir okunabilir; altı adımlık bir zincirde hangi adımın
değiştirilmesi gerektiği belirsizleşir. Uzun zincir aynı zamanda bir işaret verir: aynı rol
birden çok kez sarmalanmışsa, ara adımlardan biri gereksizdir.

Buna karşılık zinciri tümüyle düzleştirmek de yanlıştır. Bileşen belirteçlerinin doğrudan
ilkel değerlere bağlandığı bir sistem, katman ihlalinin genelleştirilmiş hâlidir ve tema
geçişini imkânsız kılar. Doğru hedef en kısa zincir değil, **kural içinde en kısa** zincirdir.

#### Yazılı Biçim ve Ad Sözleşmesi

Belirteçlerin biçemdeki karşılığı özel özelliklerdir. Katman ayrımı ad önekiyle görünür
kılınır; Özel Özellikler dersindeki `--kart-` öneki sözleşmesi burada üç katmana genişler.

```css
:root {
  /* ilkel */
  --notr-500: #757e8a;
  --ana-600: #275ea5;
  --adim-2: 8px;
  --adim-4: 16px;

  /* anlamsal */
  --kenarlik: var(--notr-500);
  --eylem-ana: var(--ana-600);
  --aralik-grup-ici: var(--adim-2);
  --aralik-blok-arasi: var(--adim-4);
}

.dugme {
  /* bilesen: yalniz anlamsal adlara basvurur */
  --dugme-zemin: var(--eylem-ana);
  --dugme-ic-bosluk-y: var(--aralik-grup-ici);
  --dugme-ic-bosluk-x: var(--aralik-blok-arasi);

  background-color: var(--dugme-zemin);
  padding: var(--dugme-ic-bosluk-y) var(--dugme-ic-bosluk-x);
}
```

Bileşen belirteçlerinin kök ögede değil bileşenin kendi kuralında bildirilmesi bir kapsam
kararıdır. Bildirim bileşende durduğunda ad yalnız o alt ağaçta geçerli olur ve kök öge, her
bileşenin ayar noktalarını taşıyan bir listeye dönüşmez. Özel Özellikler dersindeki
hesaplanma anı kuralı burada da geçerlidir: `--dugme-zemin` düğme ögesinde hesaplanır,
dolayısıyla düğmenin bir üst ögesinde `--eylem-ana` yeniden tanımlanırsa düğme bunu devralır.

Ad sözleşmesinin son parçası ilkel katmanın adlandırmasıdır. İlkel adlar rol taşımaz, konum
taşır: `notr-500`, `adim-2`, `basamak-1`. Tipografik Ölçek dersinde basamak numarasının
anlam taşıyan addan daha dayanıklı olduğu gösterilmişti; aynı gerekçe bütün ilkel katman için
geçerlidir. Anlamı taşıyan tek katman anlamsal katmandır ve zaten adı bunu söyler.

#### Özet

- Tasarım belirteci, bir tasarım kararının makine tarafından okunabilen adlandırılmış
  karşılığıdır; belirteçler tek liste olarak değil katmanlı bir grafik olarak düzenlenir.
- Üç katman vardır: ilkel ham değeri, anlamsal işi, bileşen katmanı ayar noktasını taşır.
  İlkel katman başvuru yapmaz; bileşen katmanı yalnız anlamsal katmana başvurabilir.
- Grafik dört kusur türü üretir: döngü, tanımsız başvuru, katman ihlali ve boşta kalan
  belirteç. Katman ihlali en tehlikelisidir, çünkü doğru değer üretir ve ancak tema
  değiştiğinde görünür.
- Bileşen katmanı her anlamsal belirteç için kurulmaz, yalnız ayar noktası açılan yerlerde
  kurulur; bileşen katmanının anlamsal katmandan küçük kalması beklenir.
- Zincir uzunluğu katman sayısının sonucudur; uzun zincir izlenebilirliği düşürür, tümüyle
  düzleştirmek tema geçişini imkânsız kılar. Hedef kural içinde en kısa zincirdir.
- İlkel adlar konum taşır, anlamı yalnız anlamsal katman taşır; bileşen belirteçleri kök
  ögede değil bileşenin kendi kuralında bildirilir.

#### Sonraki Adım

Katman mimarisi kurulduğunda sıra katmanların içini doldurmaya gelir ve renk bu işin en çok
kısıt taşıyan alanıdır. Bir renk belirteci yalnız bir ad ve bir değer değildir: hangi zemin
üzerinde kullanılabileceği, hangi eşiği geçmesi gerektiği ve hangi durumda hangi türevine
geçileceği de tanımın parçasıdır. Sonraki ders renk rollerini bir sözleşme olarak yazar,
hangi ön planın hangi zemine düşebileceğini bildirir ve bu bildirimi kontrast oranıyla
otomatik denetler.

### Renk Belirteçleri

Source: https://academia.sh/tr/kurslar/tasarim-sistemleri/renk-belirtecleri

Renk rollerinin bir sözleşme olarak yazılması, yasal ön plan–zemin çiftlerinin bildirilmesi, durum türevlerinin ayrım eşiğiyle sınanması ve sözleşmenin her temada otomatik denetlenmesi.

Önceki ders belirteçlerin katmanlarını ve katmanlar arası başvuru kurallarını kurdu. O
mimaride bir renk belirteci bir addan ve bir başvurudan ibaretti: `kenarlik` adı `notr-500`
basamağına bağlanıyordu. Bu, bir rengin taşıdığı bilginin yalnız bir bölümüdür.

Bir renk belirtecinin geri kalanı **nerede kullanılabileceğidir**. `metin-ikincil` rolü hangi
zeminlerin üzerine düşebilir, hangi eşiği geçmek zorundadır, hangi durumda hangi türevine
geçilir. Bu bilgi yazılmadığında her bileşen kendi denetimini yapar ya da hiç yapmaz. Bu
ders renk belirteçlerini eşleşmeleriyle birlikte tanımlar ve tanımı denetlenebilir bir
sözleşmeye çevirir.

#### Sözleşme, Rol Listesinden Fazlasıdır

Renk Sistemi dersinde roller basamaklara bağlanmış ve eşleşmeler elle listelenip
denetlenmişti. Sistem düzeyinde iki şey değişir.

Birincisi, eşleşme listesi bir **sözleşme (contract)** hâline gelir: bir bileşen yalnız
sözleşmede yazılı bir çifti kullanabilir. Sözleşmede olmayan bir ön plan–zemin birleşimi bir
tercih değil, bir ihlaldir. İkincisi, sözleşme her tema için yeniden çalıştırılır; tek bir
temada geçmek yeterli değildir.

Rol kümesi de büyür. Renk Sistemi dersindeki on rolün üzerine dört küme eklenir: durum türevleri
(üzerine gelme, basılı, devre dışı, seçili), üçüncü metin düzeyi, işlevsel renk çiftleri ve odak
halkası. Etkileşim Durumları dersinde durumların ölçülebilir biçimde ayrılması gerektiği ve bu
kursta benimsenen ayrım eşiğinin 1.2 kontrast oranı olduğu belirlenmişti; sözleşme bu eşiği de
denetler.

Sözleşmenin üçüncü parçası **ölçütsüz eşleşmelerdir**. Devre dışı bir denetimin metni,
erişilebilirlik ölçütlerinin kontrast koşulundan muaftır. Bu muafiyet sözleşmede eşiğin
yokluğu olarak değil, eşiğin açıkça `null` yazılması olarak görünür: karar verilmiş ve kayda
geçirilmiştir.

```js
// renk.mjs — renk belirteclerinin sozlesme olarak yazilmasi ve sozlesmenin 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.04045 ? 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: aileler ve basamak acikliklari (Renk Sistemi 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] };
const ilkel = (aile, basamak) => hslRgb(AILE[aile][0], AILE[aile][1], ACIKLIK[basamak]);

// Anlamsal katman: her rol bir isi adlandirir ve bir ilkel basamaga baglanir.
const ROL = {
  yuzey: ["notr", "000"],
  "yuzey-ikincil": ["notr", "050"],
  "yuzey-secili": ["ana", "100"],
  kenarlik: ["notr", "500"],
  "metin-birincil": ["notr", "900"],
  "metin-ikincil": ["notr", "600"],
  "metin-ucuncul": ["notr", "500"],
  "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"],
};
const renk = (rol) => ilkel(...ROL[rol]);

console.log("rol                      aile    basamak  onaltilik");
for (const [ad, [aile, b]] of Object.entries(ROL)) {
  console.log(`${ad.padEnd(24)} ${aile.padEnd(7)} ${b.padStart(7)}  ${onaltilik(renk(ad))}`);
}

// Sozlesme: hangi on plan hangi zemine dusebilir ve hangi esik gecerlidir.
// esik null ise olcut uygulanmaz; bunun gerekcesi sozlesmede yazili olmak zorundadir.
const SOZLESME = [
  ["metin-birincil", "yuzey", 4.5],
  ["metin-birincil", "yuzey-ikincil", 4.5],
  ["metin-ikincil", "yuzey", 4.5],
  ["metin-ucuncul", "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],
  ["eylem-devre-disi-uzeri", "eylem-devre-disi", null],
  ["odak-halkasi", "yuzey", 3.0],
  ["basari-metin", "basari-zemin", 4.5],
  ["uyari-metin", "uyari-zemin", 4.5],
  ["hata-metin", "hata-zemin", 4.5],
];

console.log("\non plan                  zemin                  kontrast   esik  sonuc");
let kalan = 0;
for (const [on, zemin, esik] of SOZLESME) {
  const k = kontrast(renk(on), renk(zemin));
  const sonuc = esik === null ? "olcut yok" : k >= esik ? "gecti" : "KALDI";
  if (esik !== null && k < esik) kalan++;
  console.log(
    `${on.padEnd(24)} ${zemin.padEnd(22)} ${k.toFixed(2).padStart(7)}:1 ${(esik === null ? "-" : esik.toFixed(1)).padStart(6)}  ${sonuc}`
  );
}
console.log(`esigi gecemeyen eslesme: ${kalan} / ${SOZLESME.filter((s) => s[2] !== null).length}`);

// Durum turevleri: her durum rolu taban rolden ayirt edilebilmelidir.
// Olcut, Etkilesim Durumlari dersinde benimsenen 1.2 kontrast oranidir.
const DURUM_ESIGI = 1.2;
const DURUMLAR = [
  ["eylem-ana-uzerine", "eylem-ana"],
  ["eylem-ana-basili", "eylem-ana-uzerine"],
  ["eylem-devre-disi", "yuzey"],
  ["yuzey-secili", "yuzey"],
  ["yuzey-ikincil", "yuzey"],
];
console.log("\ndurum rolu               taban rol              kontrast  esik  sonuc");
for (const [durum, taban] of DURUMLAR) {
  const k = kontrast(renk(durum), renk(taban));
  console.log(
    `${durum.padEnd(24)} ${taban.padEnd(22)} ${k.toFixed(3).padStart(8)} ${DURUM_ESIGI.toFixed(1).padStart(5)}  ${k >= DURUM_ESIGI ? "gecti" : "KALDI"}`
  );
}

// Metin-ucuncul rolu icin: hangi notr basamak 4.5 esigini ilk saglar?
console.log("\nnotr basamak  yuzeye karsi  4.5 esigi  3.0 esigi (buyuk metin)");
for (const b of ["300", "400", "500", "600", "700"]) {
  const k = kontrast(ilkel("notr", b), renk("yuzey"));
  console.log(
    `${b.padStart(12)} ${k.toFixed(2).padStart(13)} ${(k >= 4.5 ? "gecti" : "KALDI").padStart(10)} ${(k >= 3.0 ? "gecti" : "KALDI").padStart(23)}`
  );
}

// Ikincil yuzey icin: hangi notr basamak 1.2 ayrim esigini ilk saglar?
console.log("\nnotr basamak  yuzeye karsi  1.2 ayrim esigi  uzerindeki metin-ikincil");
for (const b of ["050", "100", "200", "300"]) {
  const z = ilkel("notr", b);
  const ay = kontrast(z, renk("yuzey"));
  const mt = kontrast(renk("metin-ikincil"), z);
  console.log(
    `${b.padStart(12)} ${ay.toFixed(3).padStart(13)} ${(ay >= 1.2 ? "gecti" : "KALDI").padStart(16)} ` +
      `${(mt.toFixed(2) + ":1 " + (mt >= 4.5 ? "gecti" : "KALDI")).padStart(24)}`
  );
}

// Koyu tema karsiligi: Koyu Tema dersinde korunum hesabiyla turetilmis degerler.
const KOYU = {
  yuzey: "#212327",
  "yuzey-ikincil": "#34383d",
  "metin-birincil": "#ffffff",
  "metin-ikincil": "#989fa8",
  kenarlik: "#7b848f",
  "eylem-ana": "#89a9d2",
  "eylem-ana-uzeri": "#212327",
  "hata-metin": "#dd9589",
};
const eksik = Object.keys(ROL).filter((r) => !(r in KOYU));
console.log(`\nkoyu tema karsiligi tanimli rol: ${Object.keys(KOYU).length} / ${Object.keys(ROL).length}`);
console.log("koyu karsiligi eksik roller:");
for (const r of eksik) console.log(`  ${r}`);

// Sozlesmenin koyu temada denetlenebilen bolumu
const cz = (h) => [1, 3, 5].map((i) => parseInt(h.slice(i, i + 2), 16));
console.log("\nkoyu temada denetlenebilen eslesmeler");
let denetlenen = 0;
for (const [on, zemin, esik] of SOZLESME) {
  if (esik === null || !(on in KOYU) || !(zemin in KOYU)) continue;
  denetlenen++;
  const k = kontrast(cz(KOYU[on]), cz(KOYU[zemin]));
  console.log(
    `  ${on.padEnd(24)} ${zemin.padEnd(20)} ${k.toFixed(2).padStart(6)}:1 ${esik.toFixed(1).padStart(5)}  ${k >= esik ? "gecti" : "KALDI"}`
  );
}
console.log(`denetlenen: ${denetlenen} / ${SOZLESME.filter((s) => s[2] !== null).length}`);
```

```
rol                      aile    basamak  onaltilik
yuzey                    notr        000  #ffffff
yuzey-ikincil            notr        050  #f7f7f8
yuzey-secili             ana         100  #dee9f7
kenarlik                 notr        500  #757e8a
metin-birincil           notr        900  #212327
metin-ikincil            notr        600  #5e656e
metin-ucuncul            notr        500  #757e8a
eylem-ana                ana         600  #275ea5
eylem-ana-uzeri          notr        000  #ffffff
eylem-ana-uzerine        ana         700  #1e4980
eylem-ana-basili         ana         800  #15335b
eylem-devre-disi         notr        200  #d3d6d9
eylem-devre-disi-uzeri   notr        500  #757e8a
odak-halkasi             ana         600  #275ea5
basari-zemin             basari      100  #dff6e9
basari-metin             basari      800  #195733
uyari-zemin              uyari       100  #fcefd9
uyari-metin              uyari       800  #684508
hata-zemin               hata        100  #f8e0dd
hata-metin               hata        700  #852819

on plan                  zemin                  kontrast   esik  sonuc
metin-birincil           yuzey                    15.74:1    4.5  gecti
metin-birincil           yuzey-ikincil            14.70:1    4.5  gecti
metin-ikincil            yuzey                     5.89:1    4.5  gecti
metin-ucuncul            yuzey                     4.11:1    4.5  KALDI
metin-birincil           yuzey-secili             12.82:1    4.5  gecti
kenarlik                 yuzey                     4.11:1    3.0  gecti
eylem-ana                yuzey                     6.50:1    3.0  gecti
eylem-ana-uzeri          eylem-ana                 6.50:1    4.5  gecti
eylem-ana-uzeri          eylem-ana-uzerine         9.05:1    4.5  gecti
eylem-ana-uzeri          eylem-ana-basili         12.68:1    4.5  gecti
eylem-devre-disi-uzeri   eylem-devre-disi          2.82:1      -  olcut yok
odak-halkasi             yuzey                     6.50:1    3.0  gecti
basari-metin             basari-zemin              7.54:1    4.5  gecti
uyari-metin              uyari-zemin               7.57:1    4.5  gecti
hata-metin               hata-zemin                7.21:1    4.5  gecti
esigi gecemeyen eslesme: 1 / 14

durum rolu               taban rol              kontrast  esik  sonuc
eylem-ana-uzerine        eylem-ana                 1.393   1.2  gecti
eylem-ana-basili         eylem-ana-uzerine         1.401   1.2  gecti
eylem-devre-disi         yuzey                     1.459   1.2  gecti
yuzey-secili             yuzey                     1.228   1.2  gecti
yuzey-ikincil            yuzey                     1.071   1.2  KALDI

notr basamak  yuzeye karsi  4.5 esigi  3.0 esigi (buyuk metin)
         300          1.91      KALDI                   KALDI
         400          2.74      KALDI                   KALDI
         500          4.11      KALDI                   gecti
         600          5.89      gecti                   gecti
         700          8.39      gecti                   gecti

notr basamak  yuzeye karsi  1.2 ayrim esigi  uzerindeki metin-ikincil
         050         1.071            KALDI             5.51:1 gecti
         100         1.204            gecti             4.90:1 gecti
         200         1.459            gecti             4.04:1 KALDI
         300         1.912            gecti             3.08:1 KALDI

koyu tema karsiligi tanimli rol: 8 / 20
koyu karsiligi eksik roller:
  yuzey-secili
  metin-ucuncul
  eylem-ana-uzerine
  eylem-ana-basili
  eylem-devre-disi
  eylem-devre-disi-uzeri
  odak-halkasi
  basari-zemin
  basari-metin
  uyari-zemin
  uyari-metin
  hata-zemin

koyu temada denetlenebilen eslesmeler
  metin-birincil           yuzey                 15.74:1   4.5  gecti
  metin-birincil           yuzey-ikincil         11.80:1   4.5  gecti
  metin-ikincil            yuzey                  5.89:1   4.5  gecti
  kenarlik                 yuzey                  4.15:1   3.0  gecti
  eylem-ana                yuzey                  6.50:1   3.0  gecti
  eylem-ana-uzeri          eylem-ana              6.50:1   4.5  gecti
denetlenen: 6 / 14
```

#### Üçüncü Metin Düzeyi Renkte Kurulamıyor

Sözleşme denetimi on dört ölçütlü eşleşmeden birini düşürüyor: `metin-ucuncul` rolü yüzeye
karşı 4.11:1 kontrast üretiyor ve 4.5 eşiğini geçemiyor.

Üçüncü tablo düzeltmenin seçeneklerini veriyor. Nötr ailede 4.5 eşiğini geçen ilk basamak
600; ama 600 zaten `metin-ikincil` rolünün basamağıdır. Yani üçüncü metin düzeyi, ikinci
düzeyle aynı renge oturmadan bu ölçekte var olamaz.

Bu bulgu Görsel Hiyerarşi dersindeki iki kanal kuralının doğrudan sonucudur. Renk kanalı üç
düzey taşıyamıyorsa, üçüncü düzey başka bir kanalda kurulur: boyut, ağırlık ya da konum.
Tipografik Ölçek dersinde üstveri düzeyi zaten hem boyutta hem kontrastta ayrılmıştı; renk
tarafında üçüncü bir ton eklemeye gerek yoktur.

Tablonun son sütunu bir başka yol daha gösteriyor: nötr 500 basamağı büyük metin için geçerli
olan 3.0 eşiğini geçiyor. Üçüncü düzey yalnızca büyük metinde kullanılacaksa rol korunabilir;
ancak bu, rolün adına bir koşul eklemeyi gerektirir. Koşullu bir rol, koşulu sözleşmede
yazılı olmadığı sürece kullanılmamalıdır — çünkü onu kullanan bileşen koşulu bilmez.

#### İkincil Yüzey Bir Ayrım Üretmiyor

Durum türevi tablosu ikinci bulguyu veriyor ve bu bulgu ilkinden daha sessizdir. Dört durum
rolü ayrım eşiğini geçiyor: üzerine gelme 1.393, basılı 1.401, devre dışı 1.459, seçili
1.228. Beşincisi geçemiyor: `yuzey-ikincil` rolü yüzeye karşı yalnız 1.071 kontrast üretiyor.

Bu, arayüzde ikinci bir yüzey düzeyi olduğu varsayılarak yazılmış her kuralın karşılıksız olduğu
anlamına gelir. Bir yan panel, bir tablo başlığı ya da bir kart zemininin nötr 050 ile ayrıldığı
yerlerde ayrım gerçekte yoktur; ölçek adımı harcanmış, ama görsel sonuç üretilmemiştir. Yinelenme
ve Tutarlılık dersindeki karşılıksız çeşitlilik kavramının renk tarafındaki karşılığı budur.

Son tablo düzeltmeyi ölçüyor ve bir ödünleşim gösteriyor. Nötr 100 ayrım eşiğini geçen ilk
basamak (1.204) ve üzerindeki ikincil metin hâlâ 4.90:1 ile eşiği geçiyor. Nötr 200'e
çıkıldığında yüzey ayrımı güçleniyor (1.459) ama ikincil metin 4.04'e düşüp eşiği kaybediyor.

Buradan sistem düzeyinde bir kural çıkar: **bir yüzey rolünün koyulaştırılması, üzerindeki
bütün metin rollerinin yeniden denetlenmesini gerektirir.** Yüzey tek başına bir karar
değildir; üzerine düşen her rolle birlikte bir karardır. Sözleşmenin işi tam olarak bu
bağımlılığı görünür kılmaktır.

#### Ölçütsüz Eşleşme de Bir Karardır

Devre dışı eylemin metni zeminine karşı 2.82:1 kontrast taşıyor ve tabloda "ölçüt yok"
yazıyor. Bu satır sözleşmenin en çok yanlış anlaşılan parçasıdır.

Ölçütün yokluğu bir ihmal değil, yazılmış bir karardır. Devre dışı bir denetim etkileşime
kapalıdır ve erişilebilirlik ölçütleri bu durumu kontrast koşulundan muaf tutar. Muafiyetin
sözleşmede açıkça yazılması iki iş görür: denetim aracı o satırı hata olarak bildirmez ve
okuyan kişi bunun unutulmuş bir denetim olmadığını görür.

Bunun bedeli de kayda geçirilir. Devre dışı bir düğmenin neden devre dışı olduğu renkle
anlaşılamaz; düşük kontrast tek başına bir bilgi taşımaz. Mikro Metin dersindeki kural burada
tekrarlanır: durumun gerekçesi metinle verilir. Sözleşmenin ölçüt yazmadığı yerde başka bir
sözleşme devreye girer.

#### Sözleşme Her Temada Yeniden Çalışır

Son iki blok bu dersin sistem düzeyindeki asıl bulgusunu veriyor. Yirmi rolün yalnız
sekizinin koyu tema karşılığı tanımlı; on ikisi eksik. Sözleşmenin on dört ölçütlü
eşleşmesinden yalnız altısı koyu temada denetlenebiliyor.

Denetlenebilen altı eşleşmenin hepsi geçiyor — Koyu Tema dersinde korunum hesabıyla
türetildikleri için bu beklenen sonuçtur. Sorun geçen eşleşmelerde değil, **denetlenemeyen**
sekiz eşleşmededir. Koyu temada bir seçili yüzey rolü yoktur; bileşenler o rolü kullandığında
tanımsız bir belirtece başvururlar ve önceki dersteki tanımsız başvuru kusuru oluşur.

Bu, eksikliğin nasıl bulunacağını da gösteriyor: rol kümesi ile tema kümesinin çarpımı
üzerinden gidilir. Yirmi rol ve iki tema kırk hücre demektir; on ikisi boştur. Rapor
üretilmediğinde bu boşluklar ancak kullanıcı koyu temaya geçtiğinde ortaya çıkar.

İşlevsel renklerin tamamının eksik olması ayrıca dikkat çekicidir. Başarı, uyarı ve hata
çiftleri açık temada tanımlanmış ve denetlenmiştir; koyu temada hiçbiri yok. İşlevsel renkler
en az kullanılan ama en kritik anlarda görünen rollerdir; eksiklikleri en geç fark edilir.

#### Sözleşmenin Yazılı Biçimi

Roller ve eşleşmeler ayrı dosyalarda durur. Roller biçemde özel özellik olarak, eşleşmeler
denetimin okuduğu bir veri dosyası olarak yazılır.

```css
:root {
  /* ilkel */
  --notr-000: #ffffff;
  --notr-100: #e9eaec;
  --notr-600: #5e656e;
  --notr-900: #212327;
  --ana-600: #275ea5;
  --ana-700: #1e4980;
  --ana-800: #15335b;

  /* anlamsal */
  --yuzey: var(--notr-000);
  --yuzey-ikincil: var(--notr-100);
  --metin-birincil: var(--notr-900);
  --metin-ikincil: var(--notr-600);
  --eylem-ana: var(--ana-600);
  --eylem-ana-uzerine: var(--ana-700);
  --eylem-ana-basili: var(--ana-800);
  --eylem-ana-uzeri: var(--notr-000);
}
```

Eşleşme sözleşmesinin biçemde karşılığı yoktur ve olmamalıdır. CSS bir kontrast oranı
hesaplayamaz; sözleşme ancak derleme zamanında ya da sınama aşamasında çalışan bir denetim
tarafından okunabilir. Bu, belirteç sisteminin biçemden bağımsız bir veri katmanı olmasının
başlıca gerekçesidir: değerler biçeme dönüşür, kurallar denetime.

İkinci ayrıntı, `yuzey-ikincil` rolünün nötr 050 yerine nötr 100'e taşınmış olmasıdır.
Denetim bu düzeltmeyi zorunlu kıldı; yazılı biçim düzeltilmiş hâli taşır. Denetimin bulduğu
bir kusurun kaynakta düzeltilmemesi, denetimi bir rapor üreticisine indirger.

#### Özet

- Renk belirteci bir ad ve bir değerden ibaret değildir; hangi zeminlerin üzerine
  düşebileceği ve hangi eşiği geçmesi gerektiği tanımın parçasıdır.
- Eşleşme listesi bir sözleşmedir: sözleşmede olmayan ön plan–zemin birleşimi tercih değil
  ihlaldir ve sözleşme her tema için yeniden çalıştırılır.
- Ölçek her rolü karşılayamaz: üçüncü metin düzeyi bu ölçekte kurulamıyor (nötr 500 eşiği
  geçemiyor, nötr 600 ikinci düzeyle çakışıyor) ve ikincil yüzey nötr 050'de 1.071
  kontrastla ayrılamıyor. Karşılanamayan rol boyut ya da ağırlık kanalına taşınır; yüzeyin
  koyulaştırılması ise üzerindeki bütün metin rollerinin yeniden denetlenmesini gerektirir.
- Ölçütsüz eşleşme yazılmış bir karardır; muafiyet sözleşmede açıkça belirtilir ve taşıdığı
  bilgi eksikliği metinle karşılanır.
- Yirmi rolün koyu tema karşılığı yalnız sekizinde tanımlı; ölçütlü on dört eşleşmenin altısı
  denetlenebiliyor. Eksik hücreler rol ve tema kümesinin çarpımından bulunur.
- Değerler biçeme, kurallar denetime dönüşür; CSS kontrast oranı hesaplayamadığı için sözleşme
  biçemin dışında yaşar.

#### Sonraki Adım

Renk tarafında sözleşme, eşleşme kavramıyla kuruldu: bir rolün doğruluğu tek başına değil,
başka bir rolle birlikte ölçülüyordu. Tipografi tarafında bağımlılık başka bir biçim alır.
Bir metin düzeyi tek bir sayı değildir: boyut, satır yüksekliği, ağırlık ve harf aralığı
birlikte bir karar oluşturur ve bu dördü ayrı belirteçlere dağıtıldığında yanlış birleşimler
kurulabilir. Sonraki ders tipografi belirteçlerini bileşik belirteç olarak tanımlar, satır
kutusu yüksekliklerini aralık ızgarasına oturtur ve düzeylerin ayrım eşiğini denetler.

### Tipografi Belirteçleri

Source: https://academia.sh/tr/kurslar/tasarim-sistemleri/tipografi-belirtecleri

Metin düzeylerinin bileşik belirteç olarak tanımlanması, satır kutusunun aralık ızgarasına oturtulması, ızgara seçiminin çarpan sapmasıyla ödünleşimi ve düzeylerin ayrım eşiğiyle denetlenmesi.

Renk belirteçleri bir bağımlılık gösterdi: bir rolün doğruluğu tek başına değil, üzerine
düştüğü zeminle birlikte ölçülüyordu. Tipografide bağımlılık bir rolün içine taşınır.

Bir metin düzeyi tek bir sayı değildir. Kayıt başlığının boyutu 20 pikselse, satır yüksekliği,
ağırlığı ve harf aralığı da o kararın parçasıdır. Bu dört alan ayrı belirteçlere dağıtılıp
bileşenlerde tek tek birleştirildiğinde iki şey olur: yanlış birleşimler kurulabilir ve
düzeyin tamamı hiçbir yerde görünmez. Bu ders tipografiyi **bileşik belirteç** olarak tanımlar
ve ikinci bir kısıt ekler — satır kutusu, aralık ölçeğinin ızgarasına oturmak zorundadır.

#### Bileşik Belirteç ve Izgara Kısıtı

**Bileşik belirteç (composite token)**, birden çok alanı tek bir ad altında toplayan
belirteçtir. `kayit-basligi` bir boyut değil, bir kayıttır: boyut basamağı, ağırlık, satır
kutusu ve harf aralığı.

Bileşik olmanın gerekçesi tutarlılık değil **geçerliliktir**. Satır Uzunluğu ve Aralığı
dersinde satır aralığı çarpanının yazı tipinin çıkıntı ve iniş toplamının altına inemeyeceği
gösterilmişti. Boyut ve çarpan ayrı belirteçler olduğunda bu koşul her birleşimde yeniden
sınanmalıdır; bileşik belirteçte koşul bir kez sınanır ve kayda geçer.

İkinci kısıt aralık ölçeğinden gelir. Bir metin bloğunun kapladığı dikey ölçü satır kutusu
yüksekliğidir; bir kartın toplam yüksekliği bu kutuların ve aralarındaki boşlukların
toplamıdır. Aralık ölçeği 4 piksel tabanlı olduğu için satır kutuları 4'ün katı değilse
toplamlar ızgaradan kayar. Kısıt şudur: **satır kutusu, aralık ölçeğinin taban adımının katı
olmalıdır.** Bu, çarpanın serbest seçilemeyeceği anlamına gelir; çarpan hedeflenir, kutu
yuvarlanır ve gerçek çarpan bu yuvarlamadan çıkar.

```js
// tipografi.mjs — bilesik tipografi belirtecleri, satir kutusunun izgaraya oturtulmasi ve ayrim denetimi

const TABAN = 16;          // govde boyutu (px)
const ORAN = 1.25;         // Tipografik Olcek dersinde secilen oran
const IZGARA = 4;          // Aralik Olcegi dersindeki karma olcegin taban adimi (px)
const CIKINTI_INIS = 1.051; // yazi tipinin bildirdigi cikinti+inis (em); alt sinir
const HEDEF_CARPAN = 1.5;  // govde metni icin secilen satir araligi carpani

const ADIMLAR = [-1, 0, 1, 2, 3];
const boyut = (n) => Math.round(TABAN * Math.pow(ORAN, n));

console.log("adim  boyut  hedef kutu  izgaraya oturan kutu  gercek carpan  sapma  cikinti-inis");
const KUTU = {};
for (const n of ADIMLAR) {
  const b = boyut(n);
  const hedef = b * HEDEF_CARPAN;
  const kutu = Math.round(hedef / IZGARA) * IZGARA;
  const carpan = kutu / b;
  KUTU[n] = kutu;
  console.log(
    `${String(n).padStart(4)} ${String(b).padStart(6)} ${hedef.toFixed(2).padStart(11)} ` +
      `${String(kutu).padStart(21)} ${carpan.toFixed(4).padStart(14)} ${(carpan - HEDEF_CARPAN).toFixed(4).padStart(7)} ` +
      `${(carpan >= CIKINTI_INIS ? "gecti" : "KALDI").padStart(13)}`
  );
}

// Izgara secimi bir odunlesimdir: kaba izgara toplamlari duzenler, carpan sapmasini buyutur.
console.log("\nizgara  en buyuk carpan sapmasi  carpan araligi        kart toplami  izgaraya oturuyor mu");
for (const g of [1, 2, 4, 8]) {
  const carpanlar = ADIMLAR.map((n) => (Math.round((boyut(n) * HEDEF_CARPAN) / g) * g) / boyut(n));
  const sapma = Math.max(...carpanlar.map((c) => Math.abs(c - HEDEF_CARPAN)));
  const kutu = Object.fromEntries(ADIMLAR.map((n) => [n, Math.round((boyut(n) * HEDEF_CARPAN) / g) * g]));
  const kartToplam = 16 + kutu[1] + 8 + 2 * kutu[0] + 8 + kutu[-1] + 16;
  console.log(
    `${String(g).padStart(6)} ${sapma.toFixed(4).padStart(22)}  ${Math.min(...carpanlar).toFixed(3)} - ${Math.max(...carpanlar).toFixed(3)} ` +
      `${String(kartToplam).padStart(19)} ${(kartToplam % 4 === 0 ? "evet" : "HAYIR").padStart(21)}`
  );
}

// Bilesik belirtecler: bir metin duzeyi tek bir sayi degil, dort alanli bir kayittir.
// Harf araligi kurali: taban boyutta 0, her basamak yukarida -0.005em, her basamak asagida +0.005em.
const harfAraligi = (n) => -0.005 * n;

const BICEMLER = [
  { ad: "sayfa-basligi", adim: 3, agirlik: 700, bolge: "sayfa" },
  { ad: "bolum-basligi", adim: 2, agirlik: 700, bolge: "sayfa" },
  { ad: "kayit-basligi", adim: 1, agirlik: 600, bolge: "kayit" },
  { ad: "govde", adim: 0, agirlik: 400, bolge: "kayit" },
  { ad: "govde-guclu", adim: 0, agirlik: 600, bolge: "kayit" },
  { ad: "dugme-metni", adim: 0, agirlik: 600, bolge: "eylem" },
  { ad: "ustveri", adim: -1, agirlik: 400, bolge: "kayit" },
  { ad: "etiket", adim: -1, agirlik: 600, bolge: "kayit" },
];

console.log("\nbicem            boyut  agirlik  satir kutusu  carpan  harf araligi (em)  harf araligi (px)");
for (const b of BICEMLER) {
  const px = boyut(b.adim);
  const em = harfAraligi(b.adim);
  console.log(
    `${b.ad.padEnd(16)} ${String(px).padStart(5)} ${String(b.agirlik).padStart(8)} ${String(KUTU[b.adim]).padStart(13)} ` +
      `${(KUTU[b.adim] / px).toFixed(3).padStart(7)} ${em.toFixed(3).padStart(18)} ${(em * px).toFixed(3).padStart(18)}`
  );
}

// Ayrim denetimi: ayni bolgede birlikte gorunen bicemler ayirt edilebilmelidir.
// Olcut, Gorsel Hiyerarsi dersindeki iki kanal kuralidir: boyut orani >= 1.15 ya da
// agirlik farki >= 200.
const BOYUT_ESIGI = 1.15;
const AGIRLIK_ESIGI = 200;
console.log("\nbolge   bicem cifti                      boyut orani  agirlik farki  gecen kanal");
let zayif = 0;
for (const bolge of [...new Set(BICEMLER.map((b) => b.bolge))]) {
  const grup = BICEMLER.filter((b) => b.bolge === bolge);
  for (let i = 0; i < grup.length; i++) {
    for (let j = i + 1; j < grup.length; j++) {
      const a = grup[i];
      const b = grup[j];
      const bo = Math.max(boyut(a.adim), boyut(b.adim)) / Math.min(boyut(a.adim), boyut(b.adim));
      const af = Math.abs(a.agirlik - b.agirlik);
      const gecen = [];
      if (bo >= BOYUT_ESIGI) gecen.push("boyut");
      if (af >= AGIRLIK_ESIGI) gecen.push("agirlik");
      if (!gecen.length) zayif++;
      console.log(
        `${bolge.padEnd(7)} ${(a.ad + " / " + b.ad).padEnd(32)} ${bo.toFixed(3).padStart(11)} ` +
          `${String(af).padStart(14)}  ${gecen.join("+") || "ZAYIF"}`
      );
    }
  }
}
console.log(`ayrim uretmeyen cift: ${zayif}`);

// Kayit kartinin dikey olcusu: satir kutulari ve aralik adimlari toplami izgaraya oturmali.
const ARALIK = { "grup-ici": 8, "blok-arasi": 16, "bolum-arasi": 24 };
const KART = [
  { tur: "aralik", ad: "blok-arasi" },
  { tur: "bicem", ad: "kayit-basligi", satir: 1 },
  { tur: "aralik", ad: "grup-ici" },
  { tur: "bicem", ad: "govde", satir: 2 },
  { tur: "aralik", ad: "grup-ici" },
  { tur: "bicem", ad: "ustveri", satir: 1 },
  { tur: "aralik", ad: "blok-arasi" },
];
console.log("\nkayit karti dikey olcu");
let toplam = 0;
for (const p of KART) {
  if (p.tur === "aralik") {
    toplam += ARALIK[p.ad];
    console.log(`  aralik  ${p.ad.padEnd(16)} ${String(ARALIK[p.ad]).padStart(5)} px`);
  } else {
    const b = BICEMLER.find((x) => x.ad === p.ad);
    const y = KUTU[b.adim] * p.satir;
    toplam += y;
    console.log(`  metin   ${(p.ad + " x" + p.satir).padEnd(16)} ${String(y).padStart(5)} px  (kutu ${KUTU[b.adim]})`);
  }
}
console.log(`  toplam                           ${String(toplam).padStart(5)} px   ${IZGARA} px izgarasina oturuyor mu: ${toplam % IZGARA === 0 ? "evet" : "HAYIR"}`);

// Ayni kart, satir kutusu izgaraya oturtulmamis olsaydi ne olurdu?
console.log("\nizgaraya oturtulmamis satir kutulariyla ayni kart");
let ham = 0;
for (const p of KART) {
  if (p.tur === "aralik") ham += ARALIK[p.ad];
  else {
    const b = BICEMLER.find((x) => x.ad === p.ad);
    ham += boyut(b.adim) * HEDEF_CARPAN * p.satir;
  }
}
console.log(`  toplam ${ham.toFixed(1)} px   izgaraya oturuyor mu: ${ham % IZGARA === 0 ? "evet" : "HAYIR"}   fark: ${(toplam - ham).toFixed(1)} px`);
```

```
adim  boyut  hedef kutu  izgaraya oturan kutu  gercek carpan  sapma  cikinti-inis
  -1     13       19.50                    20         1.5385  0.0385         gecti
   0     16       24.00                    24         1.5000  0.0000         gecti
   1     20       30.00                    32         1.6000  0.1000         gecti
   2     25       37.50                    36         1.4400 -0.0600         gecti
   3     31       46.50                    48         1.5484  0.0484         gecti

izgara  en buyuk carpan sapmasi  carpan araligi        kart toplami  izgaraya oturuyor mu
     1                 0.0385  1.500 - 1.538                 146                 HAYIR
     2                 0.0385  1.484 - 1.538                 146                 HAYIR
     4                 0.1000  1.440 - 1.600                 148                  evet
     8                 0.2692  1.231 - 1.600                 144                  evet

bicem            boyut  agirlik  satir kutusu  carpan  harf araligi (em)  harf araligi (px)
sayfa-basligi       31      700            48   1.548             -0.015             -0.465
bolum-basligi       25      700            36   1.440             -0.010             -0.250
kayit-basligi       20      600            32   1.600             -0.005             -0.100
govde               16      400            24   1.500              0.000              0.000
govde-guclu         16      600            24   1.500              0.000              0.000
dugme-metni         16      600            24   1.500              0.000              0.000
ustveri             13      400            20   1.538              0.005              0.065
etiket              13      600            20   1.538              0.005              0.065

bolge   bicem cifti                      boyut orani  agirlik farki  gecen kanal
sayfa   sayfa-basligi / bolum-basligi          1.240              0  boyut
kayit   kayit-basligi / govde                  1.250            200  boyut+agirlik
kayit   kayit-basligi / govde-guclu            1.250              0  boyut
kayit   kayit-basligi / ustveri                1.538            200  boyut+agirlik
kayit   kayit-basligi / etiket                 1.538              0  boyut
kayit   govde / govde-guclu                    1.000            200  agirlik
kayit   govde / ustveri                        1.231              0  boyut
kayit   govde / etiket                         1.231            200  boyut+agirlik
kayit   govde-guclu / ustveri                  1.231            200  boyut+agirlik
kayit   govde-guclu / etiket                   1.231              0  boyut
kayit   ustveri / etiket                       1.000            200  agirlik
ayrim uretmeyen cift: 0

kayit karti dikey olcu
  aralik  blok-arasi          16 px
  metin   kayit-basligi x1    32 px  (kutu 32)
  aralik  grup-ici             8 px
  metin   govde x2            48 px  (kutu 24)
  aralik  grup-ici             8 px
  metin   ustveri x1          20 px  (kutu 20)
  aralik  blok-arasi          16 px
  toplam                             148 px   4 px izgarasina oturuyor mu: evet

izgaraya oturtulmamis satir kutulariyla ayni kart
  toplam 145.5 px   izgaraya oturuyor mu: HAYIR   fark: 2.5 px
```

#### Yuvarlama Çarpanı Dağıtıyor

Birinci tablo kısıtın bedelini gösteriyor. Hedef çarpan bütün basamaklarda 1.5; gerçekleşen
çarpanlar 1.44 ile 1.60 arasında dağılıyor.

En büyük sapma birinci basamakta: 20 piksellik metnin hedef kutusu 30 piksel, 4'e
yuvarlandığında 32 oluyor ve çarpan 1.60'a çıkıyor. İkinci basamakta yuvarlama aşağı
gidiyor: 25 pikselin hedef kutusu 37.5, yuvarlanmış kutu 36, çarpan 1.44.

Sapmanın yönü keyfî değil, boyutun ızgaraya olan uzaklığına bağlıdır. Boyut ızgaranın katıysa
— 16 ve 4'ün katı — hedef kutu da katı olur ve sapma sıfır kalır. Tipografik Ölçek dersinde
yuvarlamanın komşu basamak oranını bozduğu gösterilmişti; burada aynı yuvarlama ikinci kez,
bu kez satır aralığı üzerinde etkisini gösteriyor. Kaynak aynıdır: sürekli bir dizinin tam
sayı ızgarasına oturtulması.

Son sütun bir güvence veriyor. Beş basamağın hiçbirinde çarpan, yazı tipinin dayattığı 1.051
alt sınırının altına inmiyor. Bu sınır aşılsaydı yuvarlama kabul edilemez olurdu; ızgara
uyumu, satırların üst üste binmesini haklı çıkarmaz.

#### Izgara Seçimi Bir Ödünleşimdir

İkinci tablo kısıtın kendisini sorguluyor. Dört ızgara adayı, iki ölçütle karşılaştırılıyor:
çarpan sapması ve kart toplamının ızgaraya oturması.

Bir ve iki piksellik ızgaralarda çarpan sapması küçük kalıyor (0.0385) ama kart toplamı 146
piksel çıkıyor ve 4 piksellik aralık ızgarasına oturmuyor. Bu, ince ızgaranın gerçek maliyetidir:
satır kutuları düzgün görünür, ama bileşen yükseklikleri aralık ölçeğiyle uyuşmaz ve iki
bileşen alt alta konduğunda hizalar kayar.

Sekiz piksellik ızgarada durum tersine dönüyor. Kart toplamı oturuyor (144) ama çarpan aralığı
1.231 ile 1.600 arasına açılıyor; sapma 0.2692. 1.231 çarpanı, aynı ölçeğin bir basamağında
metnin belirgin biçimde sıkışması demektir. Kaba ızgara toplamları düzenler, tipografiyi bozar.

Dört piksel iki ölçütü birlikte karşılayan tek aday: sapma 0.10 ile sınırlı, kart toplamı 148
ile ızgaraya oturuyor. Seçimin gerekçesi estetik değil, iki kısıtın kesişimidir.

#### Harf Aralığı Boyuta Bağlı Bir Türevdir

Üçüncü tablo bileşik belirteçlerin tam hâlini veriyor. Harf aralığı sütunu, tek tek seçilmek
yerine basamaktan türetiliyor: taban boyutta sıfır, her basamak yukarıda 0.005 em daha sıkı,
her basamak aşağıda 0.005 em daha açık.

Kuralın gerekçesi optiktir. Aynı harf aralığı büyük boyutta daha geniş görünür, çünkü aralık
boyutla birlikte büyür; küçük boyutta ise harfler birbirine yaklaşır ve seçilebilirlik düşer.
Türetme bu iki eğilimi ters yönde dengeler.

Piksel sütunu türetmenin sonucunu somutlaştırıyor: sayfa başlığında −0.465 piksel, üstveride
+0.065 piksel. Değerler em cinsinden yazıldığı için bu dönüşüm kendiliğinden olur ve boyut
değiştiğinde yeniden hesaplanmaz. Bu, belirtecin em cinsinden tanımlanmasının gerekçesidir;
piksel cinsinden yazılsaydı her basamak için ayrı bir değer tutulması gerekirdi.

#### Ayrım Denetimi Bölge İçinde Yapılır

Dördüncü tablo, Görsel Hiyerarşi dersindeki iki kanal kuralını biçem kümesine uyguluyor. On
bir çiftin hepsi en az bir kanalda ayrılıyor; ayrım üretmeyen çift yok.

Denetimin **bölge** kısıtı burada belirleyicidir. Tipografik Ölçek dersinde komşuluk denetiminin
düzeyleri sıralı bir liste sandığı ve hiyerarşinin yalnız aynı anda görülen ögeler arasında
anlamlı olduğu belirtilmişti. Bu hesap o uyarıyı kurala çeviriyor: her biçemin bir bölgesi var ve
karşılaştırma yalnız aynı bölgedeki çiftler arasında yapılıyor. `dugme-metni` eylem bölgesinde tek
başına duruyor ve hiçbir çifte girmiyor; `govde-guclu` ile aynı boyut ve ağırlıkta olması bir
sorun değildir, çünkü ikisi aynı görsel alanda yarışmaz.

İki çift yalnız ağırlık kanalında ayrılıyor: `govde` ile `govde-guclu`, `ustveri` ile
`etiket`. Boyut oranları 1.000. Bu kabul edilebilir bir tasarımdır, ama bir koşul taşır:
ağırlık farkının gerçekten çizilebilmesi gerekir. Yazı Tipi Seçimi dersinde değişken yazı
tipinin ara ağırlıkları ürettiği gösterilmişti; sabit ağırlıklı bir aile kullanıldığında 600
ağırlığı yoksa tarayıcı 700'e yuvarlar ve `etiket` ile `sayfa-basligi` aynı ağırlığa düşer.
Tek kanala dayanan bir ayrım, o kanalın gerçekten var olmasına bağlıdır.

#### Toplamlar Izgarada Kapanıyor

Son iki blok kısıtın neden konduğunu kanıtlıyor. Kayıt kartı üç metin bloğu ve dört aralık
adımından oluşuyor; ızgaraya oturtulmuş satır kutularıyla toplam 148 piksel, 4'ün katı.

Aynı kart, satır kutuları yuvarlanmamış hâlde 145.5 piksel. Yarım piksellik bir artık, tek bir
kartta görünmez. Sonuç listesinde on kart alt alta dizildiğinde artık beş piksele çıkar ve
kartların yanındaki süzgeç paneliyle hizalar kayar. Izgara Sistemleri dersinde yatay eksende
gösterilen sorunun dikey eksendeki karşılığı budur: türetilmeyen bir ölçü, tekrar edildikçe
biriken bir hata üretir.

Kartın toplam yüksekliği aynı zamanda bir yan çıktı sağlıyor: 148 piksel, Yoğunluk Kararları
dersindeki ekran başına kayıt hesabının girdisidir. Tipografi belirteçleri ızgaraya
oturtulduğunda bu hesap sabit kalır; oturtulmadığında her kart biraz farklı yükseklik alır ve
hesap yaklaşık olur.

#### Yazılı Biçim

Bileşik belirteçler biçemde tek bir sınıf olarak yazılır; alanlar ayrı özel özelliklerden
gelir ama birlikte uygulanır.

```css
:root {
  --basamak--1: 13px;
  --basamak-0: 16px;
  --basamak-1: 20px;

  --kutu--1: 20px;
  --kutu-0: 24px;
  --kutu-1: 32px;
}

.bicem-kayit-basligi {
  font-size: var(--basamak-1);
  line-height: var(--kutu-1);
  font-weight: 600;
  letter-spacing: -0.005em;
}

.bicem-govde {
  font-size: var(--basamak-0);
  line-height: var(--kutu-0);
  font-weight: 400;
  letter-spacing: 0;
}
```

Satır yüksekliğinin birimsiz çarpan yerine piksel olarak yazılması bilinçli bir karardır.
Birimsiz çarpan kalıtılır ve alt ögelerde kendi boyutuyla yeniden çarpılır; piksel değer
kalıtıldığında sabit kalır. Izgara kısıtı sabit kutu ister, dolayısıyla kutu piksel olarak
bildirilir. Bunun bedeli, bir biçem sınıfının içinde farklı boyutta bir metin geçtiğinde
kutunun ona uymamasıdır; sistem bunu bir kural olarak yasaklar — biçem sınıfları iç içe
geçmez.

Bileşen katmanının burada nasıl kurulacağı da bir karardır. `kayit-baslik-boyutu` gibi bir
bileşen belirteci yalnız kayıt kartının yoğun ve seyrek sürümleri farklı basamak kullanıyorsa
açılır. Açılmadığı yerde bileşen doğrudan biçem sınıfını kullanır ve ayar noktası üretilmez.

#### Özet

- Bir metin düzeyi bileşik belirteçtir: boyut, ağırlık, satır kutusu ve harf aralığı tek bir
  ad altında tanımlanır, çünkü geçerlilik koşulları alanlar arasında kurulur.
- Satır kutusu aralık ölçeğinin taban adımının katı olmak zorundadır; çarpan hedeflenir, kutu
  yuvarlanır ve gerçek çarpan yuvarlamadan çıkar.
- Yuvarlama hedef çarpanı dağıtır (1.5 hedefiyle gerçekleşen çarpanlar 1.44–1.60 arasına
  yayılır) ve ızgara seçimi bu dağılımla toplamlar arasındaki ödünleşimdir: ince ızgara
  çarpanı korur ama bileşen toplamlarını kaydırır, kaba ızgara toplamları düzenler ama
  çarpan aralığını 0.27'ye açar.
- Harf aralığı basamaktan em cinsinden türetilir; em kullanımı boyut değiştiğinde yeniden
  hesap gerektirmez.
- Ayrım denetimi bölge içinde yapılır; yalnız ağırlık kanalında ayrılan çiftler, o ağırlığın
  yazı tipinde gerçekten bulunmasına bağımlıdır.
- Izgaraya oturtulmuş kutularla kartın toplam yüksekliği 148 piksel ve ızgaraya oturuyor;
  oturtulmamış hâlde 145.5 piksel çıkıyor ve artık, kart tekrarlandıkça birikiyor.

#### Sonraki Adım

Satır kutuları aralık ızgarasına oturtuldu, ama ızgaranın kendisi hâlâ piksel cinsinden
tanımlı. Kullanıcının kök yazı boyutunu büyütmesi ya da tarayıcının ölçeği değiştirmesi
durumunda piksel değerler sabit kalır ve metin büyürken boşluklar büyümez. Sonraki ders ölçü
sistemini sayısallaştırır: aralık ve boyut belirteçlerini göreli birimlere çevirir, farklı kök
boyutlarında tam sayı piksel üretip üretmediklerini denetler ve bileşen yüksekliklerini
dokunma hedefi ölçütüyle sınar.

### Aralık ve Boyut Belirteçleri

Source: https://academia.sh/tr/kurslar/tasarim-sistemleri/aralik-ve-boyut-belirtecleri

Aralık ölçeğinin göreli birime çevrilmesi, kök boyutu değiştiğinde metin–boşluk oranının korunması, bileşen yüksekliklerinin hedef boyutu eşikleriyle sınanması ve iç içe köşe yarıçapının türetilmesi.

Önceki ders satır kutularını aralık ölçeğinin 4 piksellik ızgarasına oturttu ve kayıt
kartının toplam yüksekliğinin ızgarada kapandığını gösterdi. Bu hesabın tamamı piksel
cinsindendi.

Piksel bir sorun taşır. Kullanıcı tarayıcısının kök yazı boyutunu büyüttüğünde metin büyür;
piksel cinsinden yazılmış boşluklar büyümez. Arayüz büyümüş metinle sabit boşluğun karışımına
dönüşür ve Yoğunluk Kararları dersinde kurulan denge bozulur. Bu ders ölçü sistemini
sayısallaştırır: birim seçimini bir hesaba bağlar, bileşen yüksekliklerini ölçek adımlarından
türetir ve türetmenin erişilebilirlik eşiklerini karşılayıp karşılamadığını denetler.

#### Birim Bir Sunum Ayrıntısı Değildir

Aralık ölçeği bir sayı dizisidir; hangi birimde yazıldığı ölçeğin parçası değil, ölçeğin
davranışının parçasıdır. İki aday vardır.

**Piksel** sabittir. Kök boyutu ne olursa olsun 16 piksel 16 pikseldir. Bu, tasarım
zamanındaki hesapları kolay kılar: ızgara uyumu her koşulda korunur.

**Göreli birim** kök yazı boyutunun katıdır. Kök 16 pikselken 1 birim 16 piksele karşılık
gelir; kök 20 piksele çıktığında 20 piksele. Ölçek, kullanıcının yazı boyutu tercihini
kendiliğinden devralır.

Seçim bir tercih değil, bir hesabın sonucudur. Aşağıdaki program iki rejimi aynı kart
üzerinde karşılaştırır, ardından bileşen ölçülerini türetip eşiklerle denetler.

```js
// olcu.mjs — aralik ve boyut belirteclerinin sayisallastirilmasi, birim secimi ve turetilmis olculer

const TABAN_KOK = 16; // varsayilan kok yazi boyutu (px)
const OLCEK = [4, 8, 12, 16, 20, 24, 32, 48, 64, 96];

console.log("adim  px  rem      kok 16 px  kok 18 px  kok 20 px  tam sayi (18)  tam sayi (20)");
for (let i = 0; i < OLCEK.length; i++) {
  const px = OLCEK[i];
  const rem = px / TABAN_KOK;
  const k18 = rem * 18;
  const k20 = rem * 20;
  console.log(
    `${String(i + 1).padStart(4)} ${String(px).padStart(3)} ${rem.toFixed(4).padStart(7)} ` +
      `${px.toFixed(2).padStart(10)} ${k18.toFixed(2).padStart(10)} ${k20.toFixed(2).padStart(10)} ` +
      `${(Number.isInteger(k18) ? "evet" : "hayir").padStart(14)} ${(Number.isInteger(k20) ? "evet" : "hayir").padStart(14)}`
  );
}

// Birim secimi: kok boyutu buyudugunde metin ile bosluk birlikte buyuyor mu?
// Kart: ust bosluk + baslik kutusu + grup ici + iki satir govde + grup ici + ustveri + alt bosluk
const KART = { ustBosluk: 16, baslikKutu: 32, grupIci: 8, govdeKutu: 24, govdeSatir: 2, ustveriKutu: 20, altBosluk: 16 };
function kart(kok, aralikRem) {
  const o = kok / TABAN_KOK;              // metin her zaman kok ile olceklenir
  const b = aralikRem ? o : 1;            // bosluklar rem ise olceklenir, px ise sabit kalir
  const metin = (KART.baslikKutu + KART.govdeKutu * KART.govdeSatir + KART.ustveriKutu) * o;
  const bosluk = (KART.ustBosluk + KART.grupIci * 2 + KART.altBosluk) * b;
  return { metin, bosluk, toplam: metin + bosluk, oran: metin / bosluk };
}

console.log("\nbirim  kok  metin (px)  bosluk (px)  toplam (px)  metin/bosluk orani");
for (const [ad, remMi] of [["px ", false], ["rem", true]]) {
  for (const kok of [16, 18, 20, 24]) {
    const k = kart(kok, remMi);
    console.log(
      `${ad}  ${String(kok).padStart(3)} ${k.metin.toFixed(1).padStart(11)} ${k.bosluk.toFixed(1).padStart(12)} ` +
        `${k.toplam.toFixed(1).padStart(12)} ${k.oran.toFixed(3).padStart(19)}`
    );
  }
}

// Bilesen yuksekligi turetilir: satir kutusu + 2 x dikey ic bosluk + 2 x kenar kalinligi.
// Esikler: WCAG 2.5.8 en az 24 px, WCAG 2.5.5 en az 44 px.
const ESIK_TEMEL = 24;
const ESIK_GELISMIS = 44;
const yukseklik = (kutu, ic, kenar) => kutu + 2 * ic + 2 * kenar;

const BILESENLER = [
  { ad: "dugme", kutu: 24, ic: 8, kenar: 1 },
  { ad: "dugme-buyuk", kutu: 24, ic: 12, kenar: 1 },
  { ad: "metin-alani", kutu: 24, ic: 8, kenar: 1 },
  { ad: "simge-dugmesi", kutu: 16, ic: 8, kenar: 1 },
  { ad: "etiket", kutu: 20, ic: 4, kenar: 0 },
  { ad: "liste-satiri", kutu: 24, ic: 12, kenar: 0 },
];
console.log("\nbilesen        kutu  ic bosluk  kenar  yukseklik  2.5.8 (24)  2.5.5 (44)");
for (const b of BILESENLER) {
  const y = yukseklik(b.kutu, b.ic, b.kenar);
  console.log(
    `${b.ad.padEnd(14)} ${String(b.kutu).padStart(4)} ${String(b.ic).padStart(10)} ${String(b.kenar).padStart(6)} ` +
      `${String(y).padStart(10)} ${(y >= ESIK_TEMEL ? "gecti" : "KALDI").padStart(11)} ${(y >= ESIK_GELISMIS ? "gecti" : "KALDI").padStart(11)}`
  );
}

// 44 px esigini olcek adimlariyla tam olarak tutturmak mumkun mu?
console.log("\nkutu 24, kenar 1 -> ic bosluk adimi  yukseklik  2.5.5");
for (const ic of OLCEK) {
  const y = yukseklik(24, ic, 1);
  if (y > 60) break;
  console.log(`${String(ic).padStart(35)} ${String(y).padStart(10)} ${(y >= ESIK_GELISMIS ? "gecer" : "kalir").padStart(7)}`);
}
console.log("kenar 0 ile ayni hesap:");
for (const ic of OLCEK) {
  const y = yukseklik(24, ic, 0);
  if (y > 60) break;
  console.log(`${String(ic).padStart(35)} ${String(y).padStart(10)} ${(y >= ESIK_GELISMIS ? "gecer" : "kalir").padStart(7)}`);
}

// Ic ice kose yaricapi: ic yaricap = dis yaricap - aradaki ic bosluk.
// Tam yuvarlak (999) bir istisnadir: ic ogeye tasinmaz, ic oge kendi yaricapini alir.
const YARICAP_OLCEGI = [0, 4, 8, 12, 999];
console.log("\nic ice cift                 dis yaricap  ic bosluk  hesaplanan  kullanilan  olcekte mi");
for (const [ad, disY, ic] of [
  ["kart / govde", 8, 16],
  ["kart / kapak gorseli", 8, 4],
  ["acilir katman / baslik  ", 12, 8],
  ["acilir katman / dugme", 12, 4],
  ["etiket / sayac rozeti", 999, 4],
  ["dugme / simge cercevesi", 4, 2],
]) {
  const hesap = disY === 999 ? 999 : disY - ic;
  const kullanilan = disY === 999 ? 999 : Math.max(0, hesap);
  console.log(
    `${ad.padEnd(26)} ${String(disY).padStart(11)} ${String(ic).padStart(10)} ${String(hesap).padStart(11)} ` +
      `${String(kullanilan).padStart(11)}  ${YARICAP_OLCEGI.includes(kullanilan) ? "evet" : "HAYIR"}`
  );
}

// Simge olcegi tipografi basamaklarindan degil, satir kutularindan turetilir.
console.log("\nsatir kutusu  hizalanan simge boyutu  olcekte mi");
const SIMGE = [12, 16, 20, 24, 32];
for (const kutu of [20, 24, 32, 36, 48]) {
  // Simge, satir kutusunun icine ortalanir; kutunun 2/3'unden buyuk simge satiri tasirir.
  const hedef = Math.floor(((2 / 3) * kutu) / 4) * 4;
  console.log(
    `${String(kutu).padStart(12)} ${String(hedef).padStart(22)} ${(SIMGE.includes(hedef) ? "evet" : "HAYIR").padStart(11)}`
  );
}
```

```
adim  px  rem      kok 16 px  kok 18 px  kok 20 px  tam sayi (18)  tam sayi (20)
   1   4  0.2500       4.00       4.50       5.00          hayir           evet
   2   8  0.5000       8.00       9.00      10.00           evet           evet
   3  12  0.7500      12.00      13.50      15.00          hayir           evet
   4  16  1.0000      16.00      18.00      20.00           evet           evet
   5  20  1.2500      20.00      22.50      25.00          hayir           evet
   6  24  1.5000      24.00      27.00      30.00           evet           evet
   7  32  2.0000      32.00      36.00      40.00           evet           evet
   8  48  3.0000      48.00      54.00      60.00           evet           evet
   9  64  4.0000      64.00      72.00      80.00           evet           evet
  10  96  6.0000      96.00     108.00     120.00           evet           evet

birim  kok  metin (px)  bosluk (px)  toplam (px)  metin/bosluk orani
px    16       100.0         48.0        148.0               2.083
px    18       112.5         48.0        160.5               2.344
px    20       125.0         48.0        173.0               2.604
px    24       150.0         48.0        198.0               3.125
rem   16       100.0         48.0        148.0               2.083
rem   18       112.5         54.0        166.5               2.083
rem   20       125.0         60.0        185.0               2.083
rem   24       150.0         72.0        222.0               2.083

bilesen        kutu  ic bosluk  kenar  yukseklik  2.5.8 (24)  2.5.5 (44)
dugme            24          8      1         42       gecti       KALDI
dugme-buyuk      24         12      1         50       gecti       gecti
metin-alani      24          8      1         42       gecti       KALDI
simge-dugmesi    16          8      1         34       gecti       KALDI
etiket           20          4      0         28       gecti       KALDI
liste-satiri     24         12      0         48       gecti       gecti

kutu 24, kenar 1 -> ic bosluk adimi  yukseklik  2.5.5
                                  4         34   kalir
                                  8         42   kalir
                                 12         50   gecer
                                 16         58   gecer
kenar 0 ile ayni hesap:
                                  4         32   kalir
                                  8         40   kalir
                                 12         48   gecer
                                 16         56   gecer

ic ice cift                 dis yaricap  ic bosluk  hesaplanan  kullanilan  olcekte mi
kart / govde                         8         16          -8           0  evet
kart / kapak gorseli                 8          4           4           4  evet
acilir katman / baslik              12          8           4           4  evet
acilir katman / dugme               12          4           8           8  evet
etiket / sayac rozeti              999          4         999         999  evet
dugme / simge cercevesi              4          2           2           2  HAYIR

satir kutusu  hizalanan simge boyutu  olcekte mi
          20                     12        evet
          24                     16        evet
          32                     20        evet
          36                     24        evet
          48                     32        evet
```

#### Göreli Birim Tam Sayı Piksel Vermez

Birinci tablo göreli birime geçmenin bedelini gösteriyor. Ölçek adımları kök 16 pikselde tam sayı;
kök 20 pikselde de tam sayı. Kök 18 pikselde ise üç adım yarım piksel üretiyor: 4.50, 13.50 ve
22.50.

Bu bir kusur değil, göreli birimin tanımının sonucudur. Ölçek 16'nın katı olmayan bir kök
boyutunda 16'ya bölünmüş kesirler üretir. Sonuç şu iki cümlede özetlenir: **ızgara uyumu
tasarım zamanının değişmezidir, çalışma zamanının değil.** Tasarımcı 4 piksellik ızgarada
çalışır; kullanıcı kendi kök boyutunda o ızgaranın ölçeklenmiş hâlini görür ve ızgara oranları
korunduğu için görsel sonuç bozulmaz.

Yarım piksel değerlerin kendisi bir sorun üretmez; çizim aşaması bunları kendi çözünürlüğünde
yuvarlar. Sorun ancak bir hizanın iki ayrı yoldan hesaplandığı yerde çıkar — bir kenar piksel
cinsinden, komşusu göreli birimle yazıldığında yuvarlamalar farklı yönlere gidebilir. Kural
bundan çıkar: **bir eksende karışık birim kullanılmaz.**

#### Sabit Boşluk Yoğunluğu Bozuyor

İkinci tablo asıl kararı veriyor. Aynı kart iki rejimde, dört farklı kök boyutunda ölçülüyor.

Piksel rejiminde metin yüksekliği kökle birlikte büyüyor — 100'den 150 piksele — ama boşluk
48 pikselde sabit kalıyor. Metin–boşluk oranı 2.083'ten 3.125'e çıkıyor: yüzde 50 artış.
Kullanıcı yazıyı büyüttüğünde arayüz orantılı olarak büyümüyor, **sıkışıyor**.

Göreli birim rejiminde oran dört kök boyutunun hepsinde 2.083. Kart büyüyor, oranlar
korunuyor. Yakınlık ve Gruplama dersindeki gruplama oranı ölçütü de bu sayede korunur: grup
içi boşluk ile grup sınırı arasındaki oran kök boyutundan bağımsız kalır.

Bu, birim seçimini bir tercih olmaktan çıkarır. Aralık belirteçleri göreli birimde yazılır,
çünkü aralığın işi mutlak bir mesafe belirtmek değil, **bir oran kurmaktır**. Oranı koruyan
birim göreli birimdir.

İstisnalar kayda geçirilir. Kenar kalınlığı, odak halkası kalınlığı ve saç teli çizgiler
piksel cinsinden kalır: bunların işi oran kurmak değil, görünür bir sınır çizmektir ve kök
boyutuyla büyümeleri gerekmez.

#### Bileşen Yüksekliği Seçilmez, Türetilir

Üçüncü tablo boyut belirteçlerinin nereden geldiğini gösteriyor. Bir bileşenin yüksekliği bir
belirteç olarak tanımlanmaz; üç belirteçten hesaplanır.

$$
\text{yükseklik} = \text{satır kutusu} + 2 \cdot \text{dikey iç boşluk} + 2 \cdot \text{kenar kalınlığı}
$$

Bunun gerekçesi Izgara Sistemleri dersindeki sütun genişliği bağıntısının aynısıdır: bağımlı
bir büyüklüğü bağımsız bir karar gibi yazmak, ilk değişiklikte tutarsızlık üretir. Düğmenin
yüksekliği 42 piksel olarak yazılırsa, satır kutusu değiştiğinde 42 yanlış hâle gelir ama
kimse fark etmez.

Denetim iki eşikle yapılıyor. Altı bileşenin hepsi WCAG 2.5.8 ölçütünün istediği 24 pikseli
geçiyor; yalnız ikisi 2.5.5 ölçütünün istediği 44 pikseli geçiyor. Standart düğme 42 pikselde
kalıyor — eşiğin iki piksel altında.

#### İki Piksel Ölçek Adımıyla Kapanmıyor

Dördüncü blok bu iki pikselin neden kapatılamadığını gösteriyor. Satır kutusu 24, kenar 1
sabitken iç boşluk ölçek adımlarından seçiliyor: 8 adımı 42 üretiyor, bir sonraki adım olan 12
doğrudan 50'ye çıkıyor. Aradaki 44 değeri ölçekte karşılıksız.

Kenar kalınlığını sıfıra indirmek de çözmüyor: 8 adımı 40, 12 adımı 48 veriyor.

Üç seçenek kalıyor ve üçü de bir bedel taşıyor. Birincisi, iç boşluğu 12'ye çıkarıp düğmeyi
50 piksele büyütmek; bu, Yoğunluk Kararları dersindeki ekran başına kayıt sayısını düşürür.
İkincisi, ölçeğe 10 piksellik bir adım eklemek; Aralık Ölçeği dersinde gösterildiği gibi 8 ile
10 arasındaki fark ayırt edilebilirlik eşiğinin altındadır, yani ölçeğe karşılıksız bir adım
girer. Üçüncüsü, görsel ölçüyü 42'de bırakıp etkin alanı 44'e genişletmek; Ekran Boyutu ve
Girdi Türü dersinde bunun ancak düğmeler arası boşluk da büyütülerek yapılabileceği
hesaplanmıştı.

Sistem düzeyinde doğru karar üçüncüsüdür ve kayda geçirilir: standart düğme 42 piksel görünür,
etkin alanı 44 pikseldir ve düğmeler arasındaki boşluk en az 20 piksel adımından seçilir. Bu,
bir belirtecin tek başına değil, komşusuyla birlikte geçerli olduğu ikinci örnektir; birincisi
renk eşleşmeleriydi.

#### Yarıçap ve Simge Ölçeği de Türetilir

Beşinci tablo iç içe geçmiş köşe yarıçaplarını hesaplıyor. İç yarıçap, dış yarıçaptan
aradaki iç boşluğun çıkarılmasıyla bulunur; negatif çıkan değer sıfıra düşer. Bu, iki eğrinin
merkezdaş kalmasını sağlar. Kartın gövdesi 16 piksel içeride durduğu için yarıçapı sıfır;
kapak görseli 4 piksel içeride olduğu için 4.

Bir satır ölçek dışı çıkıyor: düğme içindeki simge çerçevesi 2 piksel yarıçap istiyor ve
yarıçap ölçeğinde 2 yok. Karar iki yönlüdür — ya çerçevenin iç boşluğu 4'e çıkarılıp yarıçap
sıfıra indirilir, ya da ölçeğe 2 eklenir. Yinelenme ve Tutarlılık dersinde köşe yarıçapındaki
küçük değerlerin ayırt edilemediği ölçülmüştü; bu bulgu birinci seçeneği gösteriyor.

Tam yuvarlak yarıçap bir istisna olarak tanımlanıyor ve türetmeye girmiyor. İçindeki rozet de
tam yuvarlak olur; çıkarma işlemi burada anlamsızdır çünkü tam yuvarlak bir yarıçap değil, bir
kuraldır.

Son tablo simge ölçeğinin tipografik basamaklardan değil satır kutularından türetildiğini
gösteriyor. Simge, satır kutusunun içine ortalanır ve kutunun üçte ikisini aşmamalıdır; aşarsa
satırın üstüne ve altına taşar. Beş satır kutusu için hesaplanan beş simge boyutunun hepsi
ölçekte karşılık buluyor, çünkü hem satır kutuları hem simge ölçeği 4 piksellik ızgaraya
oturtulmuş durumda.

#### Yazılı Biçim

Aralık ve boyut belirteçleri iki katmanda yazılır; bileşen yükseklikleri hiç yazılmaz, hesapla
elde edilir.

```css
:root {
  /* ilkel: olcek adimlari, goreli birimde */
  --adim-1: 0.25rem;
  --adim-2: 0.5rem;
  --adim-3: 0.75rem;
  --adim-4: 1rem;
  --adim-6: 1.5rem;

  /* ilkel: piksel kalan olculer */
  --kenar-ince: 1px;
  --kenar-kalin: 2px;
  --yaricap-1: 4px;
  --yaricap-2: 8px;

  /* anlamsal */
  --aralik-grup-ici: var(--adim-2);
  --aralik-blok-arasi: var(--adim-4);
  --aralik-bolum-arasi: var(--adim-6);
}

.dugme {
  --dugme-ic-bosluk-y: var(--aralik-grup-ici);
  --dugme-ic-bosluk-x: var(--aralik-blok-arasi);

  padding: var(--dugme-ic-bosluk-y) var(--dugme-ic-bosluk-x);
  border: var(--kenar-ince) solid currentColor;
  border-radius: var(--yaricap-1);
  line-height: 24px;
}
```

Düğmenin yüksekliği bu bildirimde hiçbir yerde yazmıyor. Yükseklik, satır kutusu ile iç
boşlukların toplamından çıkar ve bu toplamın 44 piksele ulaşıp ulaşmadığı denetimin
sorumluluğudur. Sabit bir yükseklik bildirmek, metin sarıldığında ya da kök boyutu
büyüdüğünde içeriğin taşmasına yol açar.

#### Özet

- Birim seçimi bir sunum ayrıntısı değil, ölçeğin davranışını belirleyen bir karardır: göreli
  birim kullanıcının kök yazı boyutu tercihini devralır.
- Göreli birim her kök boyutunda tam sayı piksel vermez; ızgara uyumu tasarım zamanının
  değişmezidir, çalışma zamanının değil. Bir eksende karışık birim kullanılmaz.
- Piksel aralıkla yazılmış bir kartta kök 16'dan 24'e çıktığında metin–boşluk oranı 2.083'ten
  3.125'e yükselir; göreli birimde oran dört kök boyutunda da 2.083 kalır.
- Kenar kalınlığı, odak halkası ve saç teli çizgiler piksel kalır, çünkü işleri oran kurmak
  değil sınır çizmektir.
- Bileşen yüksekliği belirteç olarak yazılmaz; satır kutusu, iç boşluk ve kenar
  kalınlığından türetilir ve hedef boyutu eşikleriyle denetlenir. Standart düğme 42
  pikselde kalır ve 44 eşiği ölçek adımlarıyla tam tutturulamaz; karar görsel ölçüyü
  koruyup etkin alanı ve komşu boşluğu genişletmektir.
- Köşe yarıçapı iç içe geçmiş ögelerde çıkarmayla türetilir; tam yuvarlak bir değer değil bir
  kuraldır ve türetmeye girmez. Simge ölçeği tipografik basamaktan değil satır kutusundan
  türetilir.

#### Sonraki Adım

Ölçek adımları, tipografi ve boyut belirteçleri tek bir görüntü alanı varsayımıyla kuruldu.
Arayüz genişlik değiştirdiğinde sütun sayısı, kenar boşluğu ve oluk da değişir; bu değişimin
nerede olacağı şimdiye kadar tek tek verilen kararlardı. Sonraki ders kesme noktalarını bir
belirteç kümesine çevirir, her kesme noktasındaki sütun aritmetiğini denetler, aralıkların
tutarlı olup olmadığını sınar ve hiçbir düzen kararını değiştirmeyen kesme noktalarını
raporlar.

### Kesme Noktaları ve Izgara

Source: https://academia.sh/tr/kurslar/tasarim-sistemleri/kesme-noktalari-ve-izgara

Kesme noktalarının düzen yapılandırması taşıyan belirteçlere dönüştürülmesi, sütun aritmetiğinin her kesme noktasında denetlenmesi, boşta kalan kesme noktasının saptanması ve sınırların içerik ölçütünden türetilmesi.

Belirteçlerin şimdiye kadarki tamamı tek bir görüntü alanı varsayımıyla kuruldu. Renk rolleri
genişlikten bağımsızdır ve tipografi basamakları da öyle. Aralık ve boyut belirteçleri ise öyle
değil: kenar boşluğu dar bir ekranda 16, geniş bir ekranda 24 piksel olur ve sütun sayısı tümüyle
değişir.

**Kesme noktası (breakpoint)** bu değişimin nerede olacağını söyler. Duyarlı Tasarım İlkesi
dersinde kesme noktası bir ortam sorgusu eşiği olarak tanıtılmıştı; sistem düzeyinde ondan
fazlasıdır. Bir kesme noktası tek bir sayı değil, o eşikten sonra geçerli olan bütün düzen
kararlarını taşıyan bir kayıttır: sütun sayısı, kenar boşluğu, oluk ve kapsayıcının üst sınırı.
Bu ders o kaydı belirteç hâline getirir ve kayıtlar arasındaki aritmetiği denetler.

#### Kesme Noktası Bir Yapılandırma Taşır

Izgara Sistemleri dersinde sütun genişliğinin bağımlı bir büyüklük olduğu gösterilmişti:

$$
\text{sütun} = \frac{\text{kapsayıcı} - 2 \cdot \text{kenar boşluğu} - (n - 1) \cdot \text{oluk}}{n}
$$

Kesme noktası kümesi bu bağıntının girdilerini genişlik aralıklarına dağıtır. Denetimin işi
her aralıkta bağıntının anlamlı bir sonuç verdiğini ve aralıkların birbirine göre tutarlı
olduğunu göstermektir.

Denetim beş soruya yanıt arar. Sütun genişliği tam sayı mı çıkıyor? Kenar boşluğu ve oluk
değerleri aralık ölçeğinden mi geliyor? Bir kesme noktası bir öncekinden farklı bir
yapılandırma taşıyor mu? İçerik ölçütleri hangi genişlikten itibaren karşılanıyor? Kapsayıcının
üst sınırı neye göre seçilmiş?

```js
// kesme.mjs — kesme noktasi kumesinin sutun aritmetigiyle denetlenmesi

const ARALIK_OLCEGI = [4, 8, 12, 16, 20, 24, 32, 48, 64, 96];

// Her kesme noktasi bir duzen yapilandirmasi tasir. ustSinir null ise kapsayici akiskandir.
const KESME = [
  { ad: "dar", alt: 320, sutun: 4, kenar: 16, oluk: 16, ustSinir: null },
  { ad: "orta", alt: 640, sutun: 8, kenar: 24, oluk: 24, ustSinir: null },
  { ad: "genis", alt: 1024, sutun: 12, kenar: 24, oluk: 24, ustSinir: null },
  { ad: "cok-genis", alt: 1440, sutun: 12, kenar: 24, oluk: 24, ustSinir: 1200 },
  { ad: "ultra", alt: 1920, sutun: 12, kenar: 24, oluk: 24, ustSinir: 1200 },
];

const kapsayici = (k, gorusAlani) => (k.ustSinir === null ? gorusAlani : Math.min(gorusAlani, k.ustSinir));
const sutunGenisligi = (kapsayici, k) => (kapsayici - 2 * k.kenar - (k.sutun - 1) * k.oluk) / k.sutun;
const acilim = (n, sutun, oluk) => n * sutun + (n - 1) * oluk;

console.log("kesme      alt sinir  sutun  kenar  oluk  ust sinir  kapsayici  sutun genisligi  tam sayi");
for (const k of KESME) {
  const kap = kapsayici(k, k.alt);
  const s = sutunGenisligi(kap, k);
  console.log(
    `${k.ad.padEnd(10)} ${String(k.alt).padStart(9)} ${String(k.sutun).padStart(6)} ${String(k.kenar).padStart(6)} ` +
      `${String(k.oluk).padStart(5)} ${String(k.ustSinir ?? "-").padStart(10)} ${String(kap).padStart(10)} ` +
      `${s.toFixed(3).padStart(16)} ${(Number.isInteger(s) ? "evet" : "hayir").padStart(9)}`
  );
}

// Aralik degerleri olcekten mi geliyor?
console.log("\nkesme      kenar olcekte  oluk olcekte");
for (const k of KESME) {
  console.log(
    `${k.ad.padEnd(10)} ${(ARALIK_OLCEGI.includes(k.kenar) ? "evet" : "HAYIR").padStart(13)} ` +
      `${(ARALIK_OLCEGI.includes(k.oluk) ? "evet" : "HAYIR").padStart(13)}`
  );
}

// Bosta kalan kesme noktasi: bir onceki ile ayni duzen yapilandirmasini tasiyor.
const imza = (k) => `${k.sutun}/${k.kenar}/${k.oluk}/${k.ustSinir}`;
console.log("\nkesme      imza               onceki ile ayni mi");
for (let i = 0; i < KESME.length; i++) {
  const ayni = i > 0 && imza(KESME[i]) === imza(KESME[i - 1]);
  console.log(`${KESME[i].ad.padEnd(10)} ${imza(KESME[i]).padEnd(18)} ${ayni ? "EVET (bosta)" : "hayir"}`);
}

// Akiskan aralikta sutun genisligi kac gorus alani genisliginde tam sayi cikiyor?
console.log("\nkesme      aralik            ornek genislik  tam sayi ciktigi genislik sayisi");
for (const k of KESME) {
  const ust = KESME[KESME.indexOf(k) + 1]?.alt ?? 2560;
  let tam = 0;
  let ornek = 0;
  for (let w = k.alt; w < ust; w++) {
    ornek++;
    if (Number.isInteger(sutunGenisligi(kapsayici(k, w), k))) tam++;
  }
  console.log(
    `${k.ad.padEnd(10)} ${(k.alt + " - " + (ust - 1)).padEnd(17)} ${String(ornek).padStart(14)} ` +
      `${(tam + "  (" + ((100 * tam) / ornek).toFixed(1) + "%)").padStart(31)}`
  );
}

// Icerik olcutu: suzgec paneli en az 270 px, sonuc listesi en az 480 px istiyor
// (Izgara Sistemleri dersinde hesaplanan degerler).
const SUZGEC_EN_AZ = 270;
const LISTE_EN_AZ = 480;
console.log("\nkesme      bolunme   suzgec (px)  liste (px)  suzgec >=270  liste >=480  karar");
for (const k of KESME) {
  const kap = kapsayici(k, k.alt);
  const s = sutunGenisligi(kap, k);
  let secilen = null;
  for (let a = 1; a < k.sutun; a++) {
    const suzgec = acilim(a, s, k.oluk);
    const liste = acilim(k.sutun - a, s, k.oluk);
    if (suzgec >= SUZGEC_EN_AZ && liste >= LISTE_EN_AZ) {
      secilen = { a, suzgec, liste };
      break;
    }
  }
  if (secilen) {
    console.log(
      `${k.ad.padEnd(10)} ${(secilen.a + "+" + (k.sutun - secilen.a)).padEnd(9)} ${secilen.suzgec.toFixed(1).padStart(11)} ` +
        `${secilen.liste.toFixed(1).padStart(11)} ${"evet".padStart(13)} ${"evet".padStart(12)}  iki sutun`
    );
  } else {
    console.log(
      `${k.ad.padEnd(10)} ${"-".padEnd(9)} ${"-".padStart(11)} ${"-".padStart(11)} ${"-".padStart(13)} ${"-".padStart(12)}  tek sutun`
    );
  }
}

// Iki sutunlu duzenin acildigi en kucuk gorus alani genisligi
const genis = KESME.find((k) => k.ad === "genis");
let esik = null;
for (let w = 320; w <= 1600; w++) {
  const s = sutunGenisligi(kapsayici(genis, w), genis);
  const uygun = [...Array(genis.sutun - 1).keys()]
    .map((i) => i + 1)
    .some((a) => acilim(a, s, genis.oluk) >= SUZGEC_EN_AZ && acilim(genis.sutun - a, s, genis.oluk) >= LISTE_EN_AZ);
  if (uygun) {
    esik = w;
    break;
  }
}
console.log(`\n12 sutunlu yapilandirmayla iki sutunlu duzenin acildigi en kucuk genislik: ${esik} px`);
console.log(`genis kesme noktasinin alt siniri: ${genis.alt} px  ->  ${genis.alt >= esik ? "icerik olcutunu karsiliyor" : "ERKEN"}`);

// Ust sinirin gerekcesi: satir uzunlugu. 16 px metinde ortalama karakter genisligi 7.005 px
// (Satir Uzunlugu ve Araligi dersinde olculdu); 75 karakter ust siniri 525 px eder.
const KARAKTER_PX = 7.005;
console.log("\nkapsayici  9 sutun acilimi  karakter  45-75 araliginda mi");
for (const kap of [1024, 1200, 1440, 1920]) {
  const k = { sutun: 12, kenar: 24, oluk: 24 };
  const s = sutunGenisligi(kap, k);
  const en = acilim(9, s, k.oluk);
  const karakter = en / KARAKTER_PX;
  console.log(
    `${String(kap).padStart(9)} ${en.toFixed(1).padStart(16)} ${karakter.toFixed(1).padStart(9)} ` +
      `${(karakter <= 75 ? "evet" : "HAYIR").padStart(20)}`
  );
}
```

```
kesme      alt sinir  sutun  kenar  oluk  ust sinir  kapsayici  sutun genisligi  tam sayi
dar              320      4     16    16          -        320           60.000      evet
orta             640      8     24    24          -        640           53.000      evet
genis           1024     12     24    24          -       1024           59.333     hayir
cok-genis       1440     12     24    24       1200       1200           74.000      evet
ultra           1920     12     24    24       1200       1200           74.000      evet

kesme      kenar olcekte  oluk olcekte
dar                 evet          evet
orta                evet          evet
genis               evet          evet
cok-genis           evet          evet
ultra               evet          evet

kesme      imza               onceki ile ayni mi
dar        4/16/16/null       hayir
orta       8/24/24/null       hayir
genis      12/24/24/null      hayir
cok-genis  12/24/24/1200      hayir
ultra      12/24/24/1200      EVET (bosta)

kesme      aralik            ornek genislik  tam sayi ciktigi genislik sayisi
dar        320 - 639                    320                     80  (25.0%)
orta       640 - 1023                   384                     48  (12.5%)
genis      1024 - 1439                  416                      34  (8.2%)
cok-genis  1440 - 1919                  480                   480  (100.0%)
ultra      1920 - 2559                  640                   640  (100.0%)

kesme      bolunme   suzgec (px)  liste (px)  suzgec >=270  liste >=480  karar
dar        -                   -           -             -            -  tek sutun
orta       -                   -           -             -            -  tek sutun
genis      4+8             309.3       642.7          evet         evet  iki sutun
cok-genis  3+9             270.0       858.0          evet         evet  iki sutun
ultra      3+9             270.0       858.0          evet         evet  iki sutun

12 sutunlu yapilandirmayla iki sutunlu duzenin acildigi en kucuk genislik: 888 px
genis kesme noktasinin alt siniri: 1024 px  ->  icerik olcutunu karsiliyor

kapsayici  9 sutun acilimi  karakter  45-75 araliginda mi
     1024            726.0     103.6                HAYIR
     1200            858.0     122.5                HAYIR
     1440           1038.0     148.2                HAYIR
     1920           1398.0     199.6                HAYIR
```

#### Boşta Kalan Kesme Noktası

Üçüncü tablo en doğrudan bulguyu veriyor. `ultra` kesme noktası, `cok-genis` ile aynı
yapılandırmayı taşıyor: 12 sütun, 24 kenar boşluğu, 24 oluk, 1200 piksel üst sınır. İkisi
arasında hiçbir düzen kararı değişmiyor.

Böyle bir kesme noktası bir kural üretmez, yalnız bir ad üretir. Bedeli üç yerde ödenir:
biçem dosyası bir ortam sorgusu daha taşır, sınama listesi bir genişlik daha içerir ve yeni
gelen biri bu genişlikte ne değiştiğini bulmaya çalışır. Boşta kalan kesme noktası, önceki
derslerdeki boşta kalan belirtecin düzen tarafındaki karşılığıdır ve aynı biçimde
kaldırılır.

Kesme noktalarının sayısı bir hedef değildir. Her kesme noktası bir düzen kararı
değiştirmelidir; değiştirmiyorsa kesme noktası değil, bir sayıdır.

#### Tam Sayı Sütun Bir Kural Olamaz

Dördüncü tablo düzen aritmetiğinin en çok yanlış anlaşılan yanını ölçüyor. Akışkan
aralıklarda sütun genişliği çok nadiren tam sayı çıkıyor: dar aralıkta genişliklerin yüzde
25'inde, orta aralıkta yüzde 12.5'inde, geniş aralıkta yalnız yüzde 8.2'sinde.

Kapsayıcının üst sınırla sabitlendiği iki aralıkta oran yüzde 100. Nedeni açıktır: kapsayıcı
sabitlendiğinde sütun genişliği de sabitlenir ve tek bir değer hesaplanır.

Buradan çıkan sonuç, tam sayı sütun genişliğinin bir tasarım hedefi olamayacağıdır. Akışkan
bir ızgarada sütun genişliği sürekli değişir; onu tam sayıya zorlamak, kapsayıcıyı tam sayı
katlarına yuvarlamak demektir ve bu, kenarlarda düzensiz boşluklar bırakır. Doğru yaklaşım
sütun genişliğini hiç yazmamaktır: ızgara pay birimleriyle tanımlanır, sütun genişliği çizim
aşamasında hesaplanır ve hizalar ızgaranın kendisinden gelir.

Hesabın işlevi de burada netleşir. Sütun genişliği bir bildirim için değil, **içerik
ölçütlerini sınamak** için hesaplanır: bir bloğun kaç piksel yer bulacağı ve bunun yeterli
olup olmadığı.

#### Kesme Noktası İçerikten Türetilir, Aygıttan Değil

Beşinci ve altıncı bloklar kesme noktası değerlerinin nereden geleceğini yanıtlıyor. Süzgeç
paneli en az 270, sonuç listesi en az 480 piksel istiyor; bu değerler Izgara Sistemleri
dersinde içerik ölçütlerinden hesaplanmıştı.

Dar ve orta kesme noktalarında hiçbir bölünme iki ölçütü birden karşılamıyor; düzen tek
sütunlu kalıyor. Geniş kesme noktasında 4+8 bölünmesi çalışıyor, çok geniş kesme noktasında
3+9.

Altıncı blok bir uyumsuzluk gösteriyor. 12 sütunlu yapılandırmayla iki sütunlu düzenin
açılabildiği en küçük genişlik 888 piksel; oysa `genis` kesme noktasının alt sınırı 1024.
Aradaki 136 piksellik bant boyunca düzen tek sütunlu kalıyor, ama içerik iki sütuna sığıyor.

Bu, kesme noktası değerlerinin nereden seçildiğine dair bir bulgudur. 1024 bir içerik ölçütü
değil, bir aygıt genişliğidir; hesaptan değil alışkanlıktan gelir. İçerikten türetilen değer
888'dir ve aralık ölçeğine yakın bir yuvarlama ile 896 alınabilir. Bir kararın beş yıl sonra da
doğru kalıp kalmayacağını soran ölçütün düzen tarafındaki karşılığı budur: aygıt genişliklerine
bağlanan bir kesme noktası, aygıtlar değiştiğinde yanlış hâle gelir; içerik ölçütüne bağlanan
kesme noktası, içerik değişmediği sürece doğru kalır.

#### Üst Sınır da Bir İçerik Kararıdır

Son tablo kapsayıcının üst sınırının gerekçesini arıyor ve beklenmedik bir sonuç veriyor. Dokuz
sütunluk sonuç listesi alanı 1024 piksellik kapsayıcıda 726, 1200'de 858, 1440'ta 1038, 1920'de
1398 piksel genişliğinde. Satır Uzunluğu ve Aralığı dersinde ölçülen ortalama karakter
genişliğiyle bu, sırasıyla 103.6, 122.5, 148.2 ve 199.6 karakter eder. Dördü de 45–75 aralığının
dışında.

Bu, üst sınırın satır uzunluğunu tek başına çözmediğini gösteriyor. 1200 piksellik üst sınır
sonuç listesi alanını 858 piksele indiriyor ama 858 piksel hâlâ 122 karakterlik bir satır
demektir.

Doğru okuma şudur: kapsayıcı üst sınırı **düzenin** üst sınırıdır, **metnin** değil. Metnin
üst sınırı ayrı bir belirteçtir ve sütun sayısına değil karakter sayısına bağlıdır. Kayıt
kartındaki özet paragrafı 9 sütunun tamamını değil, kendi ölçü sınırını kullanır. İki sınır
karıştırıldığında ya düzen gereksiz yere daraltılır ya da metin okunamayacak kadar uzar.

#### Yazılı Biçim

Kesme noktaları belirteç olarak tanımlanır, ama ortam sorgusu koşullarında doğrudan
kullanılamazlar: bir ortam sorgusu koşulu özel özellik okuyamaz. Bu, belirteç sisteminin
biçemle olan sınırının ikinci örneğidir; birincisi kontrast sözleşmesiydi.

```css
:root {
  --kesme-orta: 640px;
  --kesme-genis: 896px;
  --kesme-cok-genis: 1440px;

  --izgara-sutun: 4;
  --izgara-kenar: var(--adim-4);
  --izgara-oluk: var(--adim-4);
  --izgara-ust-sinir: none;
}

@media (min-width: 640px) {
  :root {
    --izgara-sutun: 8;
    --izgara-kenar: var(--adim-6);
    --izgara-oluk: var(--adim-6);
  }
}

@media (min-width: 896px) {
  :root { --izgara-sutun: 12; }
}

@media (min-width: 1440px) {
  :root { --izgara-ust-sinir: 1200px; }
}

.izgara {
  display: grid;
  grid-template-columns: repeat(var(--izgara-sutun), 1fr);
  gap: var(--izgara-oluk);
  padding-inline: var(--izgara-kenar);
  max-width: var(--izgara-ust-sinir);
  margin-inline: auto;
}
```

Sorgu koşullarındaki sayılar elle yazıldığı için belirteç değerleriyle çakışma riski taşır.
Çözüm, sorgu koşullarını belirteç dosyasından üreten bir derleme adımıdır: kesme noktası
değerleri tek bir kaynakta durur, biçem dosyası oradan üretilir. Ön ve Son İşlemciler dersinde
ayrılan iş bölümü burada uygulanır — sorgu koşulu derleme zamanına, sütun sayısı çalışma
zamanına aittir.

Sütun genişliğinin hiçbir yerde yazmadığına dikkat edilmelidir. Izgara pay birimleriyle
tanımlandığı için genişlik çizim aşamasında hesaplanır; bu dersteki hesap bildirim üretmek
için değil, içerik ölçütlerini sınamak için yapıldı.

#### Özet

- Kesme noktası bir sayı değil, o eşikten sonra geçerli olan bütün düzen kararlarını taşıyan
  bir yapılandırma kaydıdır: sütun sayısı, kenar boşluğu, oluk ve kapsayıcı üst sınırı.
- Bir öncekiyle aynı yapılandırmayı taşıyan kesme noktası boştadır; kural üretmez, yalnız
  bakım yükü üretir ve kaldırılır.
- Akışkan aralıklarda sütun genişliği nadiren tam sayı çıkar — geniş aralıkta genişliklerin
  yalnız yüzde 8.2'sinde. Tam sayı sütun bir tasarım hedefi olamaz; ızgara pay birimleriyle
  tanımlanır. Hesabın amacı bildirim üretmek değil, içerik ölçütlerini sınamaktır.
- Kesme noktası değeri aygıt genişliğinden değil içerik ölçütünden türetilir; ölçülen kümede
  iki sütunlu düzen 888 pikselde açılabiliyorken kesme noktası 1024'te duruyor.
- Kapsayıcı üst sınırı düzenin sınırıdır, metnin değil; metin ölçüsü ayrı bir belirteçtir ve
  karakter sayısına bağlıdır.
- Ortam sorgusu koşulları özel özellik okuyamaz; kesme noktası değerleri tek kaynakta tutulup
  sorgu koşulları derleme zamanında üretilir.

#### Sonraki Adım

Temel katman burada tamamlanıyor: renk, tipografi, aralık, boyut ve düzen kararlarının hepsi
adlandırılmış ve türetilebilir hâlde. Geriye tek bir eksik kalıyor. Renk Belirteçleri dersinde
yirmi rolün yalnız sekizinin koyu tema karşılığı vardı; on iki hücre boştu. Bir sistemin
birden çok temayı taşıması, her tema için ayrı bir değer listesi yazmak değil, temaları tek bir
kuraldan türetmektir. Sonraki ders bu türetmeyi kurar: açık temadan koyu temayı ve bir marka
temasını üretir, eksik belirteç raporunu kapatır ve belirteç kaynağından biçem çıktısını
üretir.

### Tema Yönetimi

Source: https://academia.sh/tr/kurslar/tasarim-sistemleri/tema-yonetimi

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.

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

```js
// 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.04045 ? 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
konusunda 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?

## Katalog ve Yönetişim

Bileşen kataloğunun kapsamı ve olgunluk düzeyleri, tasarımla kod arasında ortak adlandırma, belgelendirme kapsamı, sürümleme ve kırıcı değişiklik yönetimi, katkı süreci, yönetişim modelleri, benimseme ölçümü ve araç zinciri eşitlemesi.

### Bileşen Kataloğu

Source: https://academia.sh/tr/kurslar/tasarim-sistemleri/bilesen-katalogu

Bir bileşenin katalogda durmayı hak edip etmediğini belirleyen kapsam ölçütü, olgunluk düzeylerinin çıkış koşulları ve kataloğun sağlığını kullanım payına bağlayan denetim.

Belirteç katmanı tamamlandığında sistemin değerleri adlandırılmış ve türetilebilir hâle
gelmiş oluyor: bir rengin adı var, bir aralığın adımı var, bir temanın değerleri ilkel
katmandan hesaplanıyor. Ancak bu değerleri kullanan şey bileşenlerdir ve bileşen katmanı
belirteç katmanı gibi hesapla türetilemez. Kataloğa neyin gireceği, girenin ne kadar
hazır olduğu ve kimin sahiplendiği ayrı kararlardır.

Bu ders üç soruyu ayırır. Bir bileşen katalogda durmayı hangi ölçütle hak eder? Katalogdaki
bir bileşenin "hazır" olması ne demektir? Ve bir kataloğun sağlıklı olup olmadığı hangi
sayıyla anlaşılır?

#### Katalog Nedir, Ne Değildir

Bileşen kataloğu, sistemin sunduğu bileşenlerin listesi değildir; her bileşen için **kimin
sahiplendiğini, hangi olgunluk düzeyinde olduğunu ve hangi sözleşmeyi verdiğini** kayıt
altına alan yapıdır. Bir depoda yirmi bileşen bulunması katalog olduğu anlamına gelmez.
Katalog, listeye giriş ve çıkış kurallarıyla tanımlanır.

Bu ayrım pratik bir sonuç doğurur. Katalogsuz bir bileşen kütüphanesinde her bileşen
eşit güvenilirlikte görünür: kullanan kişi, bir bileşenin altı ay sonra da aynı biçimde
duracağını mı yoksa yarın adının değişeceğini mi bilemez. Katalog, bu belirsizliği
bileşen başına bir düzey etiketine indirger.

#### Kapsam Ölçütü

Katalog büyümesinin doğal eğilimi, arayüzde görünen her şeyi içeri almaktır. Bu eğilim iki
maliyet üretir: her bileşenin belgelenmesi, sürümlenmesi ve göç ettirilmesi gerekir; ve
katalog büyüdükçe içinde arama yapmak zorlaşır.

Kapsama alma kararı üç soruya bağlanabilir:

- **Yinelenme.** Bileşen kaç ayrı yerde kullanılıyor? Tek yerde kullanılan bir şey
  kataloğa değil, kullanıldığı ürüne aittir.
- **Sahiplik yayılımı.** Kaç ayrı ekip kullanıyor? Tek ekibin kullandığı bir bileşen, o
  ekibin deposunda daha hızlı gelişir; kataloğa alındığında yalnızca inceleme yükü ekler.
- **Karar taşıyıcılığı.** Bileşen bir tasarım kararını taşıyor mu? "Ödünç al" düğmesi
  birincil eylem rengi kararını taşır; bir kayıt satırının sütun sırası taşımaz, o ürünün
  bilgi mimarisi kararıdır.

Bu üç sorunun ikisi sayıya çevrilebilir. Aşağıdaki program katalog arayüzünün sistemine
ait yirmi iki bileşeni alır, olgunluk dağılımını çıkarır, her bileşenin bulunduğu düzeyin
ölçütünü karşılayıp karşılamadığını denetler ve kapsam ölçütünün dışında kalanları
listeler.

```js
// katalog.mjs — bilesen katalogunun kapsam ve olgunluk denetimi

// Her kayit bir bilesenin katalogdaki durumunu tasir: olgunluk duzeyi, o duzeyin
// istedigi kanit alanlari, surum, kac yerde ve kac ekipte kullanildigi.
const KATALOG = [
  { ad: "dugme",              duzey: "kararli",  belge: true,  ornek: true,  karsiOrnek: true,  test: true,  erisim: true,  gocNotu: false, surum: "2.3.0", kullanim: 412, ekip: 6 },
  { ad: "baglanti",           duzey: "kararli",  belge: true,  ornek: true,  karsiOrnek: true,  test: true,  erisim: true,  gocNotu: false, surum: "1.4.2", kullanim: 268, ekip: 6 },
  { ad: "metin-alani",        duzey: "kararli",  belge: true,  ornek: true,  karsiOrnek: true,  test: true,  erisim: true,  gocNotu: false, surum: "2.0.1", kullanim: 143, ekip: 5 },
  { ad: "onay-kutusu",        duzey: "kararli",  belge: true,  ornek: true,  karsiOrnek: false, test: true,  erisim: true,  gocNotu: false, surum: "1.2.0", kullanim:  96, ekip: 5 },
  { ad: "radyo-grubu",        duzey: "kararli",  belge: true,  ornek: true,  karsiOrnek: true,  test: true,  erisim: false, gocNotu: false, surum: "1.1.0", kullanim:  34, ekip: 4 },
  { ad: "arama-alani",        duzey: "kararli",  belge: true,  ornek: true,  karsiOrnek: true,  test: true,  erisim: true,  gocNotu: false, surum: "1.6.0", kullanim:  71, ekip: 4 },
  { ad: "rozet",              duzey: "kararli",  belge: true,  ornek: true,  karsiOrnek: true,  test: true,  erisim: true,  gocNotu: false, surum: "1.0.3", kullanim: 188, ekip: 6 },
  { ad: "kart",               duzey: "kararli",  belge: true,  ornek: true,  karsiOrnek: true,  test: true,  erisim: true,  gocNotu: false, surum: "1.3.1", kullanim: 157, ekip: 6 },
  { ad: "kirinti-yolu",       duzey: "kararli",  belge: true,  ornek: true,  karsiOrnek: true,  test: true,  erisim: true,  gocNotu: false, surum: "1.0.0", kullanim:  29, ekip: 3 },
  { ad: "sayfalama",          duzey: "kararli",  belge: true,  ornek: true,  karsiOrnek: true,  test: true,  erisim: true,  gocNotu: false, surum: "1.2.4", kullanim:  41, ekip: 4 },
  { ad: "bildirim-seridi",    duzey: "kararli",  belge: true,  ornek: false, karsiOrnek: false, test: true,  erisim: true,  gocNotu: false, surum: "0.9.0", kullanim:  63, ekip: 5 },
  { ad: "bos-durum",          duzey: "kararli",  belge: true,  ornek: true,  karsiOrnek: true,  test: true,  erisim: true,  gocNotu: false, surum: "1.1.2", kullanim:  52, ekip: 5 },
  { ad: "iskelet",            duzey: "aday",     belge: true,  ornek: true,  karsiOrnek: false, test: true,  erisim: false, gocNotu: false, surum: "0.7.0", kullanim:  38, ekip: 3 },
  { ad: "sekmeler",           duzey: "aday",     belge: true,  ornek: true,  karsiOrnek: false, test: false, erisim: false, gocNotu: false, surum: "0.5.1", kullanim:  24, ekip: 3 },
  { ad: "acilir-liste",       duzey: "aday",     belge: false, ornek: true,  karsiOrnek: false, test: false, erisim: false, gocNotu: false, surum: "0.4.0", kullanim:  57, ekip: 4 },
  { ad: "akordeon",           duzey: "aday",     belge: true,  ornek: true,  karsiOrnek: false, test: false, erisim: false, gocNotu: false, surum: "0.3.2", kullanim:  17, ekip: 2 },
  { ad: "kip-pencere",        duzey: "aday",     belge: true,  ornek: false, karsiOrnek: false, test: true,  erisim: false, gocNotu: false, surum: "0.6.0", kullanim:  45, ekip: 5 },
  { ad: "arac-ipucu",         duzey: "deneysel", belge: false, ornek: false, karsiOrnek: false, test: false, erisim: false, gocNotu: false, surum: "0.1.0", kullanim:  12, ekip: 2 },
  { ad: "tablo",              duzey: "deneysel", belge: false, ornek: true,  karsiOrnek: false, test: false, erisim: false, gocNotu: false, surum: "0.2.1", kullanim:   9, ekip: 2 },
  { ad: "kayit-satiri",       duzey: "deneysel", belge: false, ornek: false, karsiOrnek: false, test: false, erisim: false, gocNotu: false, surum: "0.1.0", kullanim:   6, ekip: 1 },
  { ad: "suzgec-paneli",      duzey: "aday",     belge: true,  ornek: true,  karsiOrnek: false, test: false, erisim: false, gocNotu: false, surum: "0.3.0", kullanim:   4, ekip: 1 },
  { ad: "yukleme-gostergesi", duzey: "kaldirilan", belge: true, ornek: true, karsiOrnek: true,  test: true,  erisim: true,  gocNotu: true,  surum: "1.0.0", kullanim:  31, ekip: 4 },
];

// Her duzeyin cikis olcutu: o duzeyde durabilmek icin dolu olmasi gereken alanlar.
const OLCUT = {
  deneysel:   [],
  aday:       ["belge", "ornek"],
  kararli:    ["belge", "ornek", "karsiOrnek", "test", "erisim"],
  kaldirilan: ["belge", "gocNotu"],
};

// Kararli duzey ayrica 1.0.0 ve ustu bir surum ister.
const anaSurum = (s) => Number(s.split(".")[0]);

const DUZEYLER = ["deneysel", "aday", "kararli", "kaldirilan"];
const ETIKET = { deneysel: "deneysel", aday: "aday", kararli: "kararlı", kaldirilan: "kaldırılan" };

const toplamKullanim = KATALOG.reduce((t, b) => t + b.kullanim, 0);

console.log("olgunluk düzeyi   bileşen   kullanım   kullanım payı");
for (const d of DUZEYLER) {
  const grup = KATALOG.filter((b) => b.duzey === d);
  const k = grup.reduce((t, b) => t + b.kullanim, 0);
  console.log(
    `${ETIKET[d].padEnd(17)} ${String(grup.length).padStart(7)} ${String(k).padStart(10)} ` +
    `${((k / toplamKullanim) * 100).toFixed(1).padStart(13)}%`
  );
}
console.log(`${"toplam".padEnd(17)} ${String(KATALOG.length).padStart(7)} ${String(toplamKullanim).padStart(10)} ${"100.0".padStart(13)}%`);

// Duzeyinin olcutunu karsilamayan bilesenler.
console.log("\nbileşen             düzey       eksik ölçüt");
let ihlal = 0;
for (const b of KATALOG) {
  const eksik = OLCUT[b.duzey].filter((alan) => !b[alan]);
  if (b.duzey === "kararli" && anaSurum(b.surum) < 1) eksik.push(`surum<1.0.0 (${b.surum})`);
  if (eksik.length === 0) continue;
  ihlal++;
  console.log(`${b.ad.padEnd(19)} ${ETIKET[b.duzey].padEnd(11)} ${eksik.join(", ")}`);
}
console.log(`ölçütünü karşılamayan bileşen sayısı: ${ihlal}/${KATALOG.length}`);

// Kapsam olcutu: katalogda durmayi hak eden bilesen en az iki ekipte ve en az sekiz
// yerde kullanilir. Altinda kalan bilesen urune ozgudur, katalogda degil urunde durur.
const EKIP_ESIGI = 2;
const KULLANIM_ESIGI = 8;
console.log("\nkapsam ölçütü dışında kalanlar (ekip < 2 veya kullanım < 8)");
console.log("bileşen             ekip   kullanım");
for (const b of KATALOG) {
  if (b.ekip >= EKIP_ESIGI && b.kullanim >= KULLANIM_ESIGI) continue;
  console.log(`${b.ad.padEnd(19)} ${String(b.ekip).padStart(4)} ${String(b.kullanim).padStart(10)}`);
}

// Tek satirlik saglik gostergesi: kullanimin ne kadari kararli bilesenlere dusuyor.
const kararliKullanim = KATALOG.filter((b) => b.duzey === "kararli").reduce((t, b) => t + b.kullanim, 0);
const olcutuGecen = KATALOG.filter((b) => {
  const eksik = OLCUT[b.duzey].filter((alan) => !b[alan]);
  return eksik.length === 0 && !(b.duzey === "kararli" && anaSurum(b.surum) < 1);
});
const gecenKullanim = olcutuGecen.reduce((t, b) => t + b.kullanim, 0);
console.log(`\nkararlı düzeydeki kullanım payı        : ${((kararliKullanim / toplamKullanim) * 100).toFixed(1)}%`);
console.log(`ölçütünü karşılayan bileşenlerin payı  : ${((gecenKullanim / toplamKullanim) * 100).toFixed(1)}%`);
```

```
olgunluk düzeyi   bileşen   kullanım   kullanım payı
deneysel                3         27           1.5%
aday                    6        185          10.3%
kararlı                12       1554          86.5%
kaldırılan              1         31           1.7%
toplam                 22       1797         100.0%

bileşen             düzey       eksik ölçüt
onay-kutusu         kararlı     karsiOrnek
radyo-grubu         kararlı     erisim
bildirim-seridi     kararlı     ornek, karsiOrnek, surum<1.0.0 (0.9.0)
acilir-liste        aday        belge
kip-pencere         aday        ornek
ölçütünü karşılamayan bileşen sayısı: 5/22

kapsam ölçütü dışında kalanlar (ekip < 2 veya kullanım < 8)
bileşen             ekip   kullanım
kayit-satiri           1          6
suzgec-paneli          1          4

kararlı düzeydeki kullanım payı        : 86.5%
ölçütünü karşılayan bileşenlerin payı  : 83.6%
```

#### Olgunluk Düzeyleri

Çıktının ilk bloğu dört düzeyi gösteriyor ve düzeylerin anlamı, birbirinden ne kadar
farklı söz verdiklerinden gelir:

- **Deneysel.** Sistem bu bileşenin biçiminin ve adının değişebileceğini söyler. Kullanan
  ekip, değişikliği kendi üstlenir. Belge zorunlu değildir; zorunlu olan tek şey bileşenin
  düzeyinin görünür olmasıdır.
- **Aday.** Biçim oturmuştur ama sözleşme henüz kilitlenmemiştir. Belge ve en az bir örnek
  gerekir; kırıcı değişiklik hâlâ olabilir, ama duyurulur.
- **Kararlı.** Sözleşme kilitlenmiştir. Kırıcı bir değişiklik ancak ana sürüm artışıyla ve
  göç yoluyla gelir. Bu söz, çıktıdaki beş kanıt alanına bağlanmıştır: belge, örnek, karşı
  örnek, test ve erişilebilirlik incelemesi.
- **Kullanımdan kaldırılan.** Bileşen artık kullanılmamalıdır ama hâlâ çalışır. Bu düzeyin
  ölçütü göç notudur: yerine ne kullanılacağı yazılı olmalıdır.

Düzeyler etiket olarak değil, **çıkış ölçütü** olarak tanımlanır. "Bu bileşen kararlı"
demek, listedeki beş alanın dolu olduğunu iddia etmektir. Çıktının ikinci bloğu bu iddianın
üç yerde tutmadığını gösteriyor. `onay-kutusu` karşı örneksiz, `radyo-grubu`
erişilebilirlik incelemesi geçmemiş, `bildirim-seridi` ise üç ayrı eksikle kararlı
sayılmış. Bu satırlar bir hata listesi değil, **düzey düşürme veya eksik tamamlama** iş
listesidir; ölçüt olmadan bu liste hiç üretilemezdi.

Düzey geçişleri tek yönlü değildir. Bir bileşen kararlıyken erişilebilirlik incelemesinde
kalırsa aday düzeye düşürülebilir. Düşürme, kırıcı değişiklikten farklı bir sinyaldir:
bileşen hâlâ çalışır, ama sistemin verdiği kararlılık sözü geri çekilmiştir.

#### Kapsamın Dışında Kalanlar

Çıktının üçüncü bloğu kapsam ölçütünü işletiyor. `kayit-satiri` ve `suzgec-paneli` tek
ekipte ve sekizden az yerde kullanılıyor. İkisi de katalog arayüzünün kendi parçalarıdır;
sistemin bileşeni değil, ürünün bileşenidir.

Bu ayrım, sistemin ilk kurulduğu yerin ürün olmasından doğan bir karışıklığı çözer.
Sistem, katalog arayüzünden doğduğu için ürünün her parçası doğal aday gibi görünür. Ancak
`kayit-satiri` bir bibliyografik kaydın hangi alanlarını hangi sırayla gösterdiğine karar
verir; bu karar başka bir üründe yeniden kullanılamaz. Buna karşılık o satırın içindeki
rozet, düğme ve bağlantı yeniden kullanılabilir.

Eşiklerin kendisi bir karardır ve ölçeğe bağlıdır. İki ekipli bir kuruluşta "en az iki
ekip" ölçütü hiçbir şeyi elemez; on ekipli bir kuruluşta üçe çekilebilir. Ölçütün değeri
sayının kendisinde değil, kararın gerekçesinin yazılı olmasındadır.

#### Kataloğu Bileşen Sayısıyla Ölçmemek

Son iki satır, kataloğun sağlığını sayan iki göstergeyi veriyor. Kullanımın yüzde 86,5'i kararlı
bileşenlere, yüzde 83,6'sı ise bulunduğu düzeyin ölçütünü gerçekten karşılayan bileşenlere
düşüyor. İki sayı farklı kümeleri ölçtüğü için aradaki 2,9 puan doğrudan okunamaz: ikincisi bütün
düzeyleri kapsıyor. "Kararlı" etiketinin karşılıksız kaldığı pay ayrı hesaplanır ve daha büyüktür
— kararlı kullanımın 193'ü, yani toplamın yüzde 10,7'si beş kanıt alanından en az birini
taşımıyor.

Bu iki sayı, bileşen sayısından farklı bir şey söyler. Katalogdaki yirmi iki bileşenin
üçü deneysel, ama deneysel bileşenlerin kullanımdaki payı yalnızca yüzde 1,5. Yani
kataloğun olgunlaşmamış bölümü, arayüzün küçük bir bölümünü etkiliyor. Bileşen sayısıyla
ölçen bir rapor "bileşenlerin yüzde 13,6'sı deneysel" derdi ve aynı katalog daha riskli
görünürdü.

Ters durum da olabilir. `acilir-liste` belgesiz bir aday olmasına karşın 57 yerde
kullanılıyor; tek başına deneysel bileşenlerin tümünden fazla. Kullanımla ağırlıklandırılan
gösterge bu bileşeni öne çıkarır, sayı temelli gösterge ise onu diğer beş adayla eşitler.
Kataloğun iş listesini sıralayan şey, kullanım ağırlığıdır.

#### Özet

- Bileşen kataloğu bir bileşen listesi değil, her bileşenin sahibini, olgunluk düzeyini ve
  sözleşmesini kayıt altına alan yapıdır.
- Kapsam kararı yinelenme, sahiplik yayılımı ve karar taşıyıcılığı sorularına bağlanır;
  ilk ikisi eşiklerle sayıya çevrilebilir ve ürüne özgü bileşenleri eler.
- Olgunluk düzeyleri etiket değil çıkış ölçütüdür: her düzey, dolu olması gereken kanıt
  alanlarıyla tanımlanır ve düzey iki yönde de değişebilir.
- Kataloğun sağlığı bileşen sayısıyla değil kullanım payıyla ölçülür; ağırlıksız sayım aynı
  kataloğu olduğundan daha riskli veya daha güvenli gösterir.
- Ölçütünü karşılamayan bileşenlerin listesi, kullanım ağırlığına göre sıralandığında
  doğrudan iş listesine dönüşür.

#### Sonraki Adım

Kapsam ve olgunluk kararları bir varsayıma dayanıyordu: bir bileşenden söz edildiğinde
tasarımcının ve geliştiricinin aynı şeyi kastettiği varsayımına. Çıktıdaki `acilir-liste`
satırının tasarım dosyasındaki karşılığının ne olduğu, `arac-ipucu` ile bir üründeki
"ipucu balonu"nun aynı bileşen olup olmadığı bu tablodan okunamaz. Sonraki ders adlandırma
sözleşmesini kurar: tasarım adlarıyla kod adları arasındaki eşlemeyi bir kurala bağlar,
kuralı denetleyen bir betik yazar ve eşleşmeyen adları — hem tasarımda karşılığı olmayan
bileşenleri hem de kodda karşılığı olmayan tasarım parçalarını — sayar.

### Adlandırma Sözleşmeleri

Source: https://academia.sh/tr/kurslar/tasarim-sistemleri/adlandirma-sozlesmeleri

Tasarım adlarıyla kod adları arasındaki türetim kuralı, adın dilbilgisi denetimi, eş anlamlı ad çakışmalarının saptanması ve değişke eksenleriyle girdi özellikleri arasındaki farkın raporlanması.

Önceki dersin katalog tablosu bir varsayıma dayanıyordu: bir bileşenden söz edildiğinde
tasarımcının ve geliştiricinin aynı şeyi kastettiği varsayımına. Tablodaki `arac-ipucu`
satırının tasarım dosyasındaki karşılığının ne olduğu, ya da tasarımda duran bir parçanın
kodda hiç bulunmadığı o tablodan okunamaz.

Adlandırma sözleşmesi bu boşluğu kapatır. Amacı estetik değildir: iki ayrı kayıt kümesi
arasında **makineyle denetlenebilir** bir eşleme kurmaktır. Eşleme kurulduğunda "tasarımda
var, kodda yok" ve "kodda var, tasarımda yok" listeleri hesaplanabilir hâle gelir; bu iki
liste, sistemin en sık ve en sessiz kusurunu görünür kılar.

#### İki Ad Kümesi, Tek Kavram

Bileşen iki yerde adlandırılır. Tasarım kitaplığında hiyerarşik bir ad taşır; bu ad
insanlara gösterilir ve boşluk, büyük harf, Türkçe karakter içerebilir. Kodda ise bir
modül adı taşır; bu ad dosya sisteminde ve içe aktarımlarda geçer, ASCII olmak zorundadır.

İki ad kümesinin **bağımsız** yönetilmesi, sistemin en yaygın çürüme biçimidir. Tasarımcı bir
parçanın adını değiştirir ve kod adı eskisi kalır; ya da geliştirici yeni bir bileşen ekler ve
tasarım kitaplığında karşılığı hiç oluşmaz. Hiçbiri hata vermez, çünkü iki küme arasında kimse bir
denklik iddia etmemiştir.

Sözleşme tam olarak bu denklik iddiasıdır ve üç parçası vardır:

- **Dilbilgisi.** Tasarım adı hangi biçimde yazılır: kaç bölüm, hangi harf düzeni.
- **Türetim.** Tasarım adından kod adı nasıl hesaplanır. Bu kural bir işlev olmalıdır;
  iki adın "birbirine benzemesi" denetlenemez, birinden diğerinin hesaplanması denetlenir.
- **Tekillik.** Bir kavram için tek ad. Aynı şeyi anlatan iki ad, iki bileşen üretir.

#### Türetim Kuralının Yazılması

Türetim kuralı çevriyazımla başlar ve sırası önemlidir. Türkçe harfler önce ASCII
karşılıklarına çevrilmeli, sonra küçültme yapılmalıdır. Ters sırada, büyük noktalı `İ`
harfinin küçültülmesi noktası ayrı duran bir birleşik karakter üretir ve kod adı gözle
görülmeden bozulur.

Aşağıdaki program tasarım kitaplığındaki yirmi bir adı ve kod kataloğundaki yirmi bir adı
alır; türetimi uygular, dilbilgisini denetler, eş anlamlı ad sözlüğünü işletir ve son
olarak değişke eksenleriyle girdi özelliklerini karşılaştırır.

```js
// adlandirma.mjs — tasarim adlari ile kod adlari arasindaki eslemenin denetimi

// Tasarim kitapligindaki adlar: "Kategori/Bilesen" ve degiske eksenleri.
const TASARIM = [
  { ad: "Eylem/Düğme",                   eksen: ["tur", "boyut", "durum"] },
  { ad: "Eylem/Bağlantı",                eksen: ["tur", "durum"] },
  { ad: "Form/Metin Alanı",              eksen: ["boyut", "durum"] },
  { ad: "Form/Onay Kutusu",              eksen: ["durum", "secili"] },
  { ad: "Form/Radyo Grubu",              eksen: ["yon", "durum"] },
  { ad: "Form/Açılır Liste",             eksen: ["boyut", "durum"] },
  { ad: "Form/Arama Alanı",              eksen: ["boyut", "durum"] },
  { ad: "Gösterim/Rozet",                eksen: ["ton", "boyut"] },
  { ad: "Kapsayıcı/Kart",                eksen: ["yukselti", "tiklanabilir"] },
  { ad: "Gezinme/Kırıntı Yolu",          eksen: ["kisaltma"] },
  { ad: "Gezinme/Sayfalama",             eksen: ["boyut"] },
  { ad: "Gezinme/Sekmeler",              eksen: ["yon", "boyut"] },
  { ad: "Geri Bildirim/Bildirim Şeridi", eksen: ["ton", "kapatilabilir"] },
  { ad: "Geri Bildirim/Boş Durum",       eksen: ["boyut"] },
  { ad: "Geri Bildirim/İskelet",         eksen: ["satir"] },
  { ad: "Katman/Kip Pencere",            eksen: ["boyut"] },
  { ad: "Katman/İpucu Balonu",           eksen: ["yon"] },
  { ad: "Kapsayıcı/Akordeon",            eksen: ["boyut"] },
  { ad: "Gösterim/Tablo",                eksen: ["yogunluk"] },
  { ad: "Kart Bileşeni",                 eksen: ["yukselti"] },
  { ad: "Form/Girdi/Sayı Alanı",         eksen: ["boyut"] },
];

// Kod katalogundaki adlar ve her bilesenin girdi ozellikleri.
const KOD = [
  { ad: "dugme",           ozellik: ["tur", "boyut", "durum"] },
  { ad: "baglanti",        ozellik: ["tur", "durum"] },
  { ad: "metin-alani",     ozellik: ["boyut", "durum"] },
  { ad: "onay-kutusu",     ozellik: ["durum", "secili"] },
  { ad: "radyo-grubu",     ozellik: ["yon", "durum"] },
  { ad: "acilir-liste",    ozellik: ["boyut", "durum", "aramali"] },
  { ad: "arama-alani",     ozellik: ["boyut", "durum"] },
  { ad: "rozet",           ozellik: ["ton", "boyut"] },
  { ad: "kart",            ozellik: ["yukselti", "tiklanabilir"] },
  { ad: "kirinti-yolu",    ozellik: ["kisaltma"] },
  { ad: "sayfalama",       ozellik: ["boyut"] },
  { ad: "sekmeler",        ozellik: ["yon", "boyut"] },
  { ad: "bildirim-seridi", ozellik: ["ton", "kapatilabilir"] },
  { ad: "bos-durum",       ozellik: ["boyut"] },
  { ad: "iskelet",         ozellik: ["satir"] },
  { ad: "kip-pencere",     ozellik: ["boyut", "kapatilabilir"] },
  { ad: "arac-ipucu",      ozellik: ["yon"] },
  { ad: "akordeon",        ozellik: ["boyut"] },
  { ad: "tablo",           ozellik: ["yogunluk"] },
  { ad: "sayi-alani",      ozellik: ["boyut"] },
  { ad: "yardim-metni",    ozellik: [] },
];

// Turkce harfleri once cevir, sonra kucult. Ters sira yapilirsa "I" harfi noktali
// "i" uretir ve kod adi sessizce bozulur.
const CEVRIM = { "ı": "i", "İ": "i", "ş": "s", "Ş": "s", "ç": "c", "Ç": "c", "ğ": "g", "Ğ": "g", "ü": "u", "Ü": "u", "ö": "o", "Ö": "o", "â": "a", "î": "i", "û": "u" };
const cevir = (s) => [...s].map((h) => CEVRIM[h] ?? h).join("");

// Turetim kurali: son bolum -> cevriyazim -> kucult -> bosluk yerine tire.
const koddanBeklenen = (tasarimAdi) => {
  const son = tasarimAdi.split("/").at(-1);
  return cevir(son).toLowerCase().replace(/\s+/g, "-");
};

// Dilbilgisi kurallari: ad iki bolumden olusur, her bolum Baslik Bicimi'ndedir.
function dilbilgisi(tasarimAdi) {
  const kusur = [];
  const bolum = tasarimAdi.split("/");
  if (bolum.length !== 2) kusur.push(`bölüm sayısı ${bolum.length}, 2 olmalı`);
  for (const b of bolum) {
    for (const sozcuk of b.split(/\s+/)) {
      if (sozcuk !== sozcuk.charAt(0).toLocaleUpperCase("tr") + sozcuk.slice(1)) {
        kusur.push(`"${sozcuk}" başlık biçiminde değil`);
      }
    }
  }
  return kusur;
}

// Es anlamli ad sozlugu: bir kavram icin kabul edilen tek ad ve reddedilen adlar.
const ESANLAM = [
  { kabul: "arac-ipucu", red: ["ipucu-balonu", "aciklama-balonu"] },
  { kabul: "kip-pencere", red: ["diyalog", "acilir-pencere"] },
  { kabul: "bildirim-seridi", red: ["uyari-kutusu"] },
];

const kodAdlari = new Set(KOD.map((k) => k.ad));

console.log("tasarım adı                     beklenen kod adı    durum");
let eslesen = 0;
const eslesmeyenTasarim = [];
for (const t of TASARIM) {
  const beklenen = koddanBeklenen(t.ad);
  const bulundu = kodAdlari.has(beklenen);
  if (bulundu) eslesen++; else eslesmeyenTasarim.push(beklenen);
  console.log(`${t.ad.padEnd(31)} ${beklenen.padEnd(19)} ${bulundu ? "eşleşti" : "KOD YOK"}`);
}

const beklenenler = new Set(TASARIM.map((t) => koddanBeklenen(t.ad)));
const eslesmeyenKod = KOD.filter((k) => !beklenenler.has(k.ad)).map((k) => k.ad);
console.log(`\ntasarımda karşılığı olmayan kod adları: ${JSON.stringify(eslesmeyenKod)}`);

console.log("\ndilbilgisi kusurları");
for (const t of TASARIM) {
  const kusur = dilbilgisi(t.ad);
  if (kusur.length) console.log(`${t.ad.padEnd(31)} ${kusur.join("; ")}`);
}

console.log("\neş anlamlı ad çakışmaları");
const tumAdlar = [...kodAdlari, ...TASARIM.map((t) => koddanBeklenen(t.ad))];
for (const e of ESANLAM) {
  const gecen = e.red.filter((r) => tumAdlar.includes(r));
  if (gecen.length) console.log(`${e.kabul.padEnd(19)} yerine kullanılan: ${JSON.stringify(gecen)}`);
}

console.log("\ndeğişke ekseni ile girdi özelliği farkları");
const kodHarita = new Map(KOD.map((k) => [k.ad, k.ozellik]));
for (const t of TASARIM) {
  const ozellik = kodHarita.get(koddanBeklenen(t.ad));
  if (!ozellik) continue;
  const yalnizTasarim = t.eksen.filter((e) => !ozellik.includes(e));
  const yalnizKod = ozellik.filter((o) => !t.eksen.includes(o));
  if (yalnizTasarim.length === 0 && yalnizKod.length === 0) continue;
  console.log(`${t.ad.padEnd(31)} yalnız tasarımda: ${JSON.stringify(yalnizTasarim)}  yalnız kodda: ${JSON.stringify(yalnizKod)}`);
}

const oran = (eslesen / TASARIM.length) * 100;
console.log(`\neşleşen ad sayısı            : ${eslesen}/${TASARIM.length} (%${oran.toFixed(1)})`);
console.log(`tasarımda karşılığı olmayan  : ${eslesmeyenKod.length}`);
console.log(`kodda karşılığı olmayan      : ${eslesmeyenTasarim.length}`);
```

```
tasarım adı                     beklenen kod adı    durum
Eylem/Düğme                     dugme               eşleşti
Eylem/Bağlantı                  baglanti            eşleşti
Form/Metin Alanı                metin-alani         eşleşti
Form/Onay Kutusu                onay-kutusu         eşleşti
Form/Radyo Grubu                radyo-grubu         eşleşti
Form/Açılır Liste               acilir-liste        eşleşti
Form/Arama Alanı                arama-alani         eşleşti
Gösterim/Rozet                  rozet               eşleşti
Kapsayıcı/Kart                  kart                eşleşti
Gezinme/Kırıntı Yolu            kirinti-yolu        eşleşti
Gezinme/Sayfalama               sayfalama           eşleşti
Gezinme/Sekmeler                sekmeler            eşleşti
Geri Bildirim/Bildirim Şeridi   bildirim-seridi     eşleşti
Geri Bildirim/Boş Durum         bos-durum           eşleşti
Geri Bildirim/İskelet           iskelet             eşleşti
Katman/Kip Pencere              kip-pencere         eşleşti
Katman/İpucu Balonu             ipucu-balonu        KOD YOK
Kapsayıcı/Akordeon              akordeon            eşleşti
Gösterim/Tablo                  tablo               eşleşti
Kart Bileşeni                   kart-bileseni       KOD YOK
Form/Girdi/Sayı Alanı           sayi-alani          eşleşti

tasarımda karşılığı olmayan kod adları: ["arac-ipucu","yardim-metni"]

dilbilgisi kusurları
Kart Bileşeni                   bölüm sayısı 1, 2 olmalı
Form/Girdi/Sayı Alanı           bölüm sayısı 3, 2 olmalı

eş anlamlı ad çakışmaları
arac-ipucu          yerine kullanılan: ["ipucu-balonu"]

değişke ekseni ile girdi özelliği farkları
Form/Açılır Liste               yalnız tasarımda: []  yalnız kodda: ["aramali"]
Katman/Kip Pencere              yalnız tasarımda: []  yalnız kodda: ["kapatilabilir"]

eşleşen ad sayısı            : 19/21 (%90.5)
tasarımda karşılığı olmayan  : 2
kodda karşılığı olmayan      : 2
```

#### Raporun Okunması

Yirmi bir adın on dokuzu eşleşiyor. Kalan dört satır — iki eşleşmeyen tasarım adı ve iki
eşleşmeyen kod adı — dört farklı kusur türünü temsil ediyor ve her biri farklı bir işle
kapanır.

`Katman/İpucu Balonu` ile `arac-ipucu` **aynı bileşendir, iki adı vardır**. Bu, listenin
en zararlı satırıdır; çünkü ne tasarımda ne kodda bir eksik görünür. Her iki tarafta da
bileşen mevcuttur, yalnızca birbirine bağlanamaz. Eş anlamlı ad sözlüğü bu durumu
yakalayan tek denetimdir: kabul edilen adın yanına reddedilen adlar yazılmadıkça, bir
denetim programı iki adın aynı kavramı gösterdiğini kendiliğinden bilemez.

`Kart Bileşeni` **iki kusur birden taşır**. Kategorisiz yazılmıştır ve zaten var olan
`Kapsayıcı/Kart` kaydının kopyasıdır. "Bileşeni" sözcüğü de sözleşme dışıdır: kataloğun
her kaydı zaten bir bileşendir, adın bunu tekrar söylemesi hiçbir ayrım üretmez.

`yardim-metni` **kodda var, tasarımda yok**. Bu satır her zaman kusur anlamına gelmez.
Görsel karşılığı olmayan bir yardımcının kod tarafında bulunması olağandır. Sözleşmenin
istediği şey, bu satırın **açıkça işaretlenmiş** olmasıdır: kataloğa "tasarım karşılığı
yok" notu düşülmezse, denetim her çalıştığında aynı satırı kusur gibi raporlar ve rapor
okunmaz hâle gelir.

`Form/Girdi/Sayı Alanı` ilginç bir durumu gösteriyor: dilbilgisi denetiminden kalıyor ama
ad eşleşmesinden geçiyor. Türetim son bölümü aldığı için üç bölümlü ad da doğru kod adını
üretiyor. Bu, iki denetimin **ayrı** tutulmasının nedenidir. Türetim kuralı gevşek
olabilir; dilbilgisi kuralı gevşek olursa kategoriler dağılır ve kitaplıkta arama yapmak
zorlaşır.

#### Değişke Ekseni ile Girdi Özelliği

Adlandırma yalnızca bileşen adını kapsamaz. Tasarım kitaplığında bir bileşenin
görünümlerini üreten eksenler vardır; kodda bunların karşılığı Bileşen Tabanlı Arayüz
Geliştirme kursunda tanımlanan girdi özellikleridir. İkisinin adları da eşleşmelidir,
çünkü tasarımcı ile geliştirici arasındaki konuşma bu eksenlerin adları üzerinden yürür.

Çıktının son bloğu iki sapma gösteriyor ve ikisi de aynı yönde: kodda olan bir eksen
tasarımda yok. `acilir-liste` bileşeninin `aramali` özelliği ve `kip-pencere` bileşeninin
`kapatilabilir` özelliği kod tarafında eklenmiş, tasarım kitaplığına yansımamış.

Bu yön, sapmanın nedeni hakkında bilgi verir. Kodda beliren yeni eksen genellikle bir ürün
ihtiyacından doğar: bir ekibin uzun listeye ihtiyacı olmuş, arama özelliği eklenmiş.
Tasarımda beliren ama kodda olmayan eksen ise gerçekleştirilmemiş bir tasarım kararıdır.
İki durumun kapatma yolu farklıdır; ilki tasarım kitaplığının güncellenmesini, ikincisi
katkı sürecine bir iş girmesini gerektirir.

Eksen **değerlerinin** de sözleşmeye girmesi gerekir. `tur` ekseninin tasarımda "birincil"
ve "ikincil", kodda `birincil` ve `ikincil` değerlerini alması denetlenebilir; tasarımda
"ana eylem" yazılıp kodda `birincil` kullanılması, ad eşleşse bile konuşmayı bozar.

#### Sözleşmenin Sürdürülmesi

Denetim programı tek seferlik bir temizlik aracı değildir. Ad kümeleri sürekli değişir ve
sapma her zaman yeniden birikir. İki uygulama kararı bunu belirler.

Birincisi, denetimin **kırıcı olup olmadığıdır**. Rapor üreten ama hiçbir şeyi durdurmayan
bir denetim, birkaç ay içinde okunmayan bir çıktıya dönüşür. Eşleşme oranı için bir alt
sınır belirlenip bu sınırın altına düşüldüğünde sürecin durdurulması, raporu karara
bağlar.

İkincisi, **istisnaların kayıt altına alınmasıdır**. `yardim-metni` gibi meşru
eşleşmezlikler bir istisna listesine yazılır ve listeye her giriş bir gerekçe taşır.
İstisna listesi büyüdükçe sözleşmenin kendisinin gözden geçirilmesi gerektiği anlaşılır;
listenin uzunluğu da bir göstergedir.

#### Özet

- Bileşen iki ayrı kümede adlandırılır; iki küme bağımsız yönetildiğinde aralarındaki
  sapma hiçbir yerde hata üretmeden birikir.
- Sözleşmenin üç parçası vardır: adın dilbilgisi, tasarım adından kod adını hesaplayan
  türetim kuralı ve bir kavrama tek ad veren tekillik kuralı.
- Türetimde Türkçe harfler küçültmeden **önce** çevrilir; ters sıra noktalı `İ` harfinde
  bozuk kod adı üretir.
- Eş anlamlı ad çakışması iki tarafta da eksik göstermediği için yalnızca bir eş anlam
  sözlüğüyle yakalanır; en zararlı kusur türü budur.
- Değişke eksenleri ile girdi özellikleri de eşleşmelidir; sapmanın yönü, işin tasarım
  kitaplığında mı yoksa katkı sürecinde mi olduğunu söyler.
- Denetim bir alt sınıra bağlanmadıkça ve istisnalar gerekçeleriyle kayıt altına
  alınmadıkça rapor okunmaz hâle gelir.

#### Sonraki Adım

Adlar eşleştiğinde bir bileşenden söz etmek mümkün olur, ama onu **kullanmak** için
yeterli değildir. `acilir-liste` bileşeninin hangi durumda `arama-alani` yerine
seçileceği, `kip-pencere` yerine ne zaman ayrı bir sayfa kullanılacağı ve `rozet` ile
`bildirim-seridi` arasındaki ayrım adlardan okunamaz. Sonraki ders bileşen belgesinin
zorunlu bölümlerini tanımlar, karşı örneğin neden örnek kadar gerekli olduğunu gösterir ve
belgelendirme kapsamını kullanımla ağırlıklandırarak hesaplar.

### Belgelendirme

Source: https://academia.sh/tr/kurslar/tasarim-sistemleri/belgelendirme

Bileşen belgesinin zorunlu bölümleri, karşı örneğin taşıdığı bilgi, belgelendirme kapsamının bölüm ve bileşen düzeyinde hesabı ve özellik tablosunun arayüzle karşılaştırılması.

Adlar eşleştiğinde bir bileşenden söz etmek mümkün olur, ama onu kullanmak için yeterli
değildir. `acilir-liste` bileşeninin hangi durumda `arama-alani` yerine seçileceği,
`kip-pencere` yerine ne zaman ayrı bir sayfa kullanılacağı ve `rozet` ile
`bildirim-seridi` arasındaki ayrım adlardan okunamaz.

Belgelendirme bu kararları taşır. Bileşen belgesi bir kullanım kılavuzu değil,
**bileşenin sözleşmesinin okunabilir hâlidir**: ne yapar, ne yapmaz, hangi girdileri alır,
hangi durumda seçilmez. Bu ders belgenin zorunlu bölümlerini tanımlar ve kapsamın nasıl
ölçüleceğini gösterir.

#### Belgenin Zorunlu Bölümleri

Bileşen belgesinin bölümleri, kullanan kişinin sırayla sorduğu sorulara karşılık gelir:

- **Amaç.** Bileşen hangi işi görür? Tek cümle. Amacı iki cümleyle anlatılan bir bileşen
  genellikle iki bileşendir.
- **Ne zaman kullanılır.** Hangi bağlamda doğru seçimdir. Bu bölüm bileşeni tarif etmez,
  **seçim ölçütü** verir.
- **Ne zaman kullanılmaz.** Karşı örnek. Yerine hangi bileşenin kullanılacağını söyler.
- **Özellik tablosu.** Girdi özelliklerinin adları, aldıkları değerler, varsayılanları ve
  zorunlu olup olmadıkları.
- **Örnek.** Kopyalanıp çalıştırılabilir kullanım. Görsel değil, kaynak metin.
- **Erişilebilirlik notu.** Klavye davranışı, odak sırası ve bileşenin kendiliğinden
  sağladığı ile kullananın sağlaması gerekenin ayrımı.
- **İlgili bileşenler.** Komşu bileşenlere bağlantı; kullanan kişi yanlış yerden başlamış
  olabilir.

Bu yedi bölümün altısı tanıdıktır. Sistemlerin çoğunda en sık eksik kalan iki bölüm, karşı
örnek ile ilgili bileşenlerdir; ikisi de aynı işi yapar: kullanıcıyı **bu bileşenden
uzaklaştırma** işini.

#### Karşı Örneğin Taşıdığı Bilgi

Örnek, bileşenin doğru kullanımını gösterir. Karşı örnek ise başka bir şey gösterir:
bileşenin **sınırını**. İkisi birbirinin yerine geçmez.

Katalog arayüzünden somut bir örnekle: `rozet` bileşeni bir kaydın durumunu gösterir —
"rafta", "ödünçte", "ayırtılmış". `bildirim-seridi` ise kullanıcıya bir olay bildirir —
"ödünç alma işlemi tamamlandı". İkisi de küçük, renkli, kısa metinli kutulardır ve ikisi
de bir durumu anlatır. Ayrımı yalnızca karşı örnek yazılı hâle getirir: rozet bir
**nesnenin** özelliğidir ve nesneyle birlikte durur; bildirim şeridi bir **eylemin**
sonucudur ve eylemden sonra belirir. Bu ayrım özellik tablosundan okunamaz, çünkü iki
bileşenin özellikleri neredeyse aynıdır.

Karşı örneğin ikinci işi, kaçış deliğinin kapatılmasıdır. Bileşen Tabanlı Arayüz
Geliştirme kursunda gösterildiği gibi bir bileşen çağırana kaçış deliği verir; belge, bu
deliğin nerede biteceğini söylemezse her ekip kendi sınırını çizer. "Rozet içine düğme
koymayın, tıklanabilir durum için `dugme` bileşenini kullanın" cümlesi bir kuraldır ve
denetlenebilir.

#### Kapsamın Hesaplanması

Belgelendirmenin "yapıldı" ya da "yapılmadı" olarak izlenmesi işe yaramaz; belgeler kısmen
yazılır ve kısmen eskir. Aşağıdaki program kataloğun on dokuz belgelenmiş bileşenini alır,
bölüm bazında ve bileşen bazında kapsamı hesaplar, eksikleri kullanım etkisine göre
sıralar ve son olarak özellik tablosunu gerçek arayüzle karşılaştırır.

```js
// belge.mjs — belgelendirme kapsaminin bilesen ve bolum duzeyinde hesabi

// Bir bilesen belgesinin zorunlu bolumleri.
const BOLUM = ["amac", "ne-zaman", "ne-zaman-degil", "ozellik-tablosu", "ornek", "erisim-notu", "ilgili"];

// Her bilesen icin var olan bolumler ve kullanim sayisi (01. dersteki katalogla ayni).
const BELGE = [
  { ad: "dugme",           kullanim: 412, var: ["amac", "ne-zaman", "ne-zaman-degil", "ozellik-tablosu", "ornek", "erisim-notu", "ilgili"] },
  { ad: "baglanti",        kullanim: 268, var: ["amac", "ne-zaman", "ne-zaman-degil", "ozellik-tablosu", "ornek", "erisim-notu", "ilgili"] },
  { ad: "rozet",           kullanim: 188, var: ["amac", "ne-zaman", "ne-zaman-degil", "ozellik-tablosu", "ornek", "erisim-notu"] },
  { ad: "kart",            kullanim: 157, var: ["amac", "ne-zaman", "ne-zaman-degil", "ozellik-tablosu", "ornek", "erisim-notu", "ilgili"] },
  { ad: "metin-alani",     kullanim: 143, var: ["amac", "ne-zaman", "ne-zaman-degil", "ozellik-tablosu", "ornek", "erisim-notu", "ilgili"] },
  { ad: "onay-kutusu",     kullanim:  96, var: ["amac", "ne-zaman", "ozellik-tablosu", "ornek", "erisim-notu", "ilgili"] },
  { ad: "arama-alani",     kullanim:  71, var: ["amac", "ne-zaman", "ne-zaman-degil", "ozellik-tablosu", "ornek", "erisim-notu", "ilgili"] },
  { ad: "bildirim-seridi", kullanim:  63, var: ["amac", "ne-zaman", "ozellik-tablosu", "erisim-notu"] },
  { ad: "acilir-liste",    kullanim:  57, var: ["ozellik-tablosu", "ornek"] },
  { ad: "bos-durum",       kullanim:  52, var: ["amac", "ne-zaman", "ne-zaman-degil", "ozellik-tablosu", "ornek", "erisim-notu", "ilgili"] },
  { ad: "kip-pencere",     kullanim:  45, var: ["amac", "ne-zaman", "ozellik-tablosu", "erisim-notu"] },
  { ad: "sayfalama",       kullanim:  41, var: ["amac", "ne-zaman", "ne-zaman-degil", "ozellik-tablosu", "ornek", "erisim-notu", "ilgili"] },
  { ad: "iskelet",         kullanim:  38, var: ["amac", "ne-zaman", "ozellik-tablosu", "ornek"] },
  { ad: "radyo-grubu",     kullanim:  34, var: ["amac", "ne-zaman", "ne-zaman-degil", "ozellik-tablosu", "ornek", "ilgili"] },
  { ad: "kirinti-yolu",    kullanim:  29, var: ["amac", "ne-zaman", "ne-zaman-degil", "ozellik-tablosu", "ornek", "erisim-notu", "ilgili"] },
  { ad: "sekmeler",        kullanim:  24, var: ["amac", "ne-zaman", "ozellik-tablosu", "ornek"] },
  { ad: "akordeon",        kullanim:  17, var: ["amac", "ozellik-tablosu", "ornek"] },
  { ad: "arac-ipucu",      kullanim:  12, var: ["ozellik-tablosu"] },
  { ad: "tablo",           kullanim:   9, var: ["ozellik-tablosu", "ornek"] },
];

// Bolum bazli kapsam: hangi bolum en cok eksik?
console.log("bölüm             belgesi olan   bileşen payı   kullanım payı");
const toplamKullanim = BELGE.reduce((t, b) => t + b.kullanim, 0);
for (const bol of BOLUM) {
  const olan = BELGE.filter((b) => b.var.includes(bol));
  const k = olan.reduce((t, b) => t + b.kullanim, 0);
  console.log(
    `${bol.padEnd(17)} ${String(olan.length).padStart(9)}/${BELGE.length} ` +
    `${((olan.length / BELGE.length) * 100).toFixed(1).padStart(12)}% ` +
    `${((k / toplamKullanim) * 100).toFixed(1).padStart(13)}%`
  );
}

// Bilesen bazli kapsam ve eksik etkisi.
const satirlar = BELGE.map((b) => {
  const eksik = BOLUM.filter((bol) => !b.var.includes(bol));
  return { ...b, eksik, kapsam: (BOLUM.length - eksik.length) / BOLUM.length, etki: eksik.length * b.kullanim };
}).sort((a, b) => b.etki - a.etki);

console.log("\nbileşen           kapsam   kullanım   eksik etkisi   eksik bölümler");
for (const s of satirlar) {
  if (s.eksik.length === 0) continue;
  console.log(
    `${s.ad.padEnd(17)} ${(s.kapsam * 100).toFixed(0).padStart(5)}% ${String(s.kullanim).padStart(10)} ` +
    `${String(s.etki).padStart(14)}   ${s.eksik.join(", ")}`
  );
}

const hamKapsam = satirlar.reduce((t, s) => t + s.kapsam, 0) / satirlar.length;
const agirlikliKapsam = satirlar.reduce((t, s) => t + s.kapsam * s.kullanim, 0) / toplamKullanim;
console.log(`\nham kapsam (bileşen ortalaması) : %${(hamKapsam * 100).toFixed(1)}`);
console.log(`kullanımla ağırlıklı kapsam     : %${(agirlikliKapsam * 100).toFixed(1)}`);

// Ozellik tablosunun gercek arayuzle karsilastirilmasi.
const ARAYUZ = {
  dugme:        ["tur", "boyut", "durum", "simge", "genislik"],
  "acilir-liste": ["boyut", "durum", "aramali", "coklu"],
  rozet:        ["ton", "boyut"],
  "kip-pencere": ["boyut", "kapatilabilir", "baslik"],
};
const BELGELENEN = {
  dugme:        ["tur", "boyut", "durum", "simge"],
  "acilir-liste": ["boyut", "durum"],
  rozet:        ["ton", "boyut", "ikonlu"],
  "kip-pencere": ["boyut", "baslik"],
};

console.log("\nözellik tablosu sapması");
console.log("bileşen           belgelenmemiş        belgede olup arayüzde olmayan");
for (const ad of Object.keys(ARAYUZ)) {
  const eksik = ARAYUZ[ad].filter((o) => !BELGELENEN[ad].includes(o));
  const fazla = BELGELENEN[ad].filter((o) => !ARAYUZ[ad].includes(o));
  console.log(`${ad.padEnd(17)} ${JSON.stringify(eksik).padEnd(20)} ${JSON.stringify(fazla)}`);
}
const toplamOzellik = Object.values(ARAYUZ).reduce((t, a) => t + a.length, 0);
const belgelenen = Object.keys(ARAYUZ).reduce((t, ad) => t + ARAYUZ[ad].filter((o) => BELGELENEN[ad].includes(o)).length, 0);
console.log(`özellik tablosu kapsamı: ${belgelenen}/${toplamOzellik} (%${((belgelenen / toplamOzellik) * 100).toFixed(1)})`);
```

```
bölüm             belgesi olan   bileşen payı   kullanım payı
amac                     16/19         84.2%          95.6%
ne-zaman                 15/19         78.9%          94.6%
ne-zaman-degil           10/19         52.6%          79.4%
ozellik-tablosu          19/19        100.0%         100.0%
ornek                    16/19         84.2%          93.2%
erisim-notu              12/19         63.2%          89.1%
ilgili                   10/19         52.6%          74.2%

bileşen           kapsam   kullanım   eksik etkisi   eksik bölümler
acilir-liste         29%         57            285   amac, ne-zaman, ne-zaman-degil, erisim-notu, ilgili
bildirim-seridi      57%         63            189   ne-zaman-degil, ornek, ilgili
rozet                86%        188            188   ilgili
kip-pencere          57%         45            135   ne-zaman-degil, ornek, ilgili
iskelet              57%         38            114   ne-zaman-degil, erisim-notu, ilgili
onay-kutusu          86%         96             96   ne-zaman-degil
sekmeler             57%         24             72   ne-zaman-degil, erisim-notu, ilgili
arac-ipucu           14%         12             72   amac, ne-zaman, ne-zaman-degil, ornek, erisim-notu, ilgili
akordeon             43%         17             68   ne-zaman, ne-zaman-degil, erisim-notu, ilgili
tablo                29%          9             45   amac, ne-zaman, ne-zaman-degil, erisim-notu, ilgili
radyo-grubu          86%         34             34   erisim-notu

ham kapsam (bileşen ortalaması) : %73.7
kullanımla ağırlıklı kapsam     : %89.4

özellik tablosu sapması
bileşen           belgelenmemiş        belgede olup arayüzde olmayan
dugme             ["genislik"]         []
acilir-liste      ["aramali","coklu"]  []
rozet             []                   ["ikonlu"]
kip-pencere       ["kapatilabilir"]    []
özellik tablosu kapsamı: 10/14 (%71.4)
```

#### Bölüm Bazlı Kapsamın Söylediği

İlk tablo, hangi bölümün sistem genelinde eksik kaldığını gösteriyor. Özellik tablosu
yüzde yüz doludur; çünkü arayüzden üretilebilir ve genellikle üretilir. Karşı örnek ile
ilgili bileşenler ise yüzde 52,6'da kalıyor: her iki bileşenden yalnızca biri bu
bölümlere sahip.

Bu dağılım rastgele değildir. Otomatik üretilebilen bölüm dolu, **karar yazmayı gerektiren
bölüm boş**. Belgelendirme eksiği çoğu zaman zaman eksikliği değil, karar eksikliğidir:
karşı örnek yazmak için bileşenin sınırının kararlaştırılmış olması gerekir ve o karar
verilmemiştir.

İki sütun arasındaki fark da anlamlıdır. Karşı örnek bileşenlerin yüzde 52,6'sında var ama
kullanımın yüzde 79,4'ünü kapsıyor; yani eksik olanlar çoğunlukla az kullanılan
bileşenler. İlgili bileşenler bölümünde ise fark daha dar: yüzde 52,6'ya karşı yüzde 74,2.
Bu, eksikliğin çok kullanılan bileşenlere de sıçradığını gösterir ve ikinci tablo bunu
doğruluyor.

#### Eksiğin Sıralanması

İkinci tablo, kapsam yüzdesiyle iş önceliğinin aynı şey olmadığını gösteriyor. `rozet`
yüzde 86 kapsamla listedeki en iyi durumdaki bileşenlerden biri, ama eksik etkisi 188 ile
üçüncü sırada; çünkü 188 yerde kullanılıyor ve tek eksiği herkesi etkiliyor. `tablo` ise
yüzde 29 kapsamla çok daha kötü durumda ama etkisi 45; dokuz yerde kullanılıyor.

Eksik etkisi çarpımı — eksik bölüm sayısı ile kullanım sayısının çarpımı — bu iki bilgiyi tek
sıralamada birleştirir. Sıralamanın başındaki `acilir-liste`, ilk derste de görülmüştü: belgesiz
bir aday olmasına karşın 57 yerde kullanılıyor. İki ayrı denetim aynı bileşeni işaret ettiğinde, o
bileşen sıradaki iştir.

Ham kapsam ile ağırlıklı kapsam arasındaki 15,7 puanlık fark da okunmalıdır. İki sayı
farklı sorulara yanıt verir: ham kapsam "kataloğun ne kadarı belgelendi", ağırlıklı kapsam
"kullanımın ne kadarı belgeli bir bileşene düşüyor" sorusuna. Yalnız ağırlıklı sayıyı
raporlamak, az kullanılan bileşenlerin sürekli belgesiz kalmasını gizler; yalnız ham sayıyı
raporlamak ise `rozet` gibi tek eksikli ama yaygın bileşenleri gözden kaçırır. İkisi
birlikte raporlanır.

#### Özellik Tablosunun Eskimesi

Son blok belgenin en sinsi çürüme biçimini gösteriyor: belge var, ama yanlış. `dugme`
bileşeninin `genislik` özelliği ve `acilir-liste` bileşeninin iki özelliği arayüze eklenmiş
ama tabloya yazılmamış. Bunlar **eksik** bilgidir; kullanan kişi özelliğin varlığını
bilmez, kendi çözümünü yazar ve sapma üretir.

`rozet` satırı ise farklı bir kusur taşıyor: `ikonlu` özelliği belgede duruyor, arayüzde yok. Bu
**yanlış** bilgidir ve eksikten daha pahalıdır; kullanan kişi belgeye güvenip özelliği verir,
hiçbir şey olmaz ve hatanın nedenini kendi kodunda arar.

Bu blok aynı zamanda çözümün yönünü gösterir. Özellik tablosu elle yazıldığı sürece
eskimesi kaçınılmazdır; arayüz tanımından üretildiğinde ise sapma yapısal olarak imkânsız
hâle gelir. Elle yazılması gereken bölümler — amaç, seçim ölçütü, karşı örnek — zaten
üretilemeyen bölümlerdir. Belgelendirme aracının seçimi bu ayrıma göre yapılır: üretilebilir
olanı üreten, üretilemeyeni yazmaya zorlayan bir düzen.

#### Özet

- Bileşen belgesi yedi bölümden oluşur ve her bölüm kullanan kişinin sorduğu belirli bir
  soruya karşılık gelir.
- Karşı örnek, özellik tablosundan okunamayan sınır bilgisini taşır; benzer görünen iki
  bileşen arasındaki ayrımı yazılı hâle getiren tek bölüm odur.
- Otomatik üretilebilen bölümler dolu, karar yazmayı gerektiren bölümler boş kalır;
  belgelendirme eksiği çoğu zaman karar eksikliğidir.
- Kapsam ham ve kullanımla ağırlıklı olarak birlikte raporlanır; biri az kullanılan
  bileşenlerin ihmalini, diğeri yaygın bileşenlerdeki tek eksiği gizler.
- Eksik bölüm sayısı ile kullanımın çarpımı, kapsam yüzdesinden farklı bir iş sıralaması
  üretir.
- Belgede olup arayüzde olmayan bir özellik, arayüzde olup belgede olmayandan pahalıdır;
  özellik tablosu arayüz tanımından üretildiğinde bu sapma yapısal olarak ortadan kalkar.

#### Sonraki Adım

Belgedeki özellik tablosunun arayüzle karşılaştırılması bir soruyu açıkta bıraktı:
`dugme` bileşenine `genislik` özelliğinin eklenmesi ile `rozet` bileşeninden `ikonlu`
özelliğinin kaldırılması aynı ağırlıkta değildir. Birincisi hiçbir kullanımı bozmaz,
ikincisi o özelliği veren her yeri bozar. Sonraki ders bu ayrımı sürüm numarasına bağlar:
iki sürümün bileşen arayüzlerini karşılaştıran, değişiklikleri sınıflandıran ve sonraki
sürüm numarasını hesaplayan bir betik yazar, buradan göç kılavuzunu üretir.

### Sürümleme ve Yayın

Source: https://academia.sh/tr/kurslar/tasarim-sistemleri/surumleme-ve-yayin

Bileşen arayüzünde neyin kırıcı sayıldığı, iki anlık görüntünün karşılaştırılmasından sürüm numarasının türetilmesi, göç kılavuzunun üretilmesi ve göç maliyetinin çağrı yeriyle ölçülmesi.

Belgedeki özellik tablosunun arayüzle karşılaştırılması bir soruyu açıkta bırakmıştı:
`dugme` bileşenine `genislik` özelliğinin eklenmesi ile `rozet` bileşeninden `ikonlu`
özelliğinin kaldırılması aynı ağırlıkta değildir. Birincisi hiçbir kullanımı bozmaz,
ikincisi o özelliği veren her yeri bozar.

Sürümleme bu ağırlık farkını bir sayıya çevirir. Modüller, Araçlar ve Ekosistem kursunda
tanıtılan anlamlı sürümleme, bir paketin sürüm numarasını değişikliğin sınıfına bağlar.
Bileşen kataloğunda uygulanabilmesi için tek bir şeye ihtiyaç vardır: bileşen arayüzünde
neyin kırıcı sayıldığının kesin listesi.

#### Bileşen Arayüzünde Kırıcı Olan

Bir bileşenin sözleşmesi girdi özelliklerinden ibaret değildir, ama ölçülebilir bölümü
odur. Aşağıdaki değişiklikler kırıcıdır:

- **Bileşenin kaldırılması.** En görünür olanı; içe aktarım hemen kırılır.
- **Özelliğin kaldırılması.** O özelliği veren çağrılar sessizce etkisiz kalır.
- **Özelliğin adının değişmesi.** Kaldırma ile eklemenin birleşimi gibi görünür ama göç
  adımı farklıdır.
- **İsteğe bağlı özelliğin zorunlu hâle gelmesi.** Özelliği vermeyen her çağrı bozulur.
- **Varsayılan değerin değişmesi.** Kaynak metnini hiç değiştirmemiş her çağrı farklı
  davranış görür.
- **Değer kümesinin daralması.** Kalkan değeri veren çağrılar tanımsız duruma düşer.

Bunların dışında kalan iki sınıf uyumludur. **Ekleme**: yeni bileşen, isteğe bağlı yeni
özellik, değer kümesinin genişlemesi. **Yama**: arayüzü değiştirmeyen davranış düzeltmesi.

Listede olmayan ama önemli bir kalem var: **belge ağacının yapısı ve sınıf adları**. Bir
bileşenin ürettiği düğüm ağacına dışarıdan CSS yazılabiliyorsa, o ağaç da sözleşmenin
parçasıdır ve değişmesi kırıcıdır. Katalog bu yüzden ya ağacı sözleşmeye dahil eder ya da
dahil olmadığını açıkça yazar; ikisinden birini yapmayan sistem, her yayında adı konmamış
kırıcı değişiklikler üretir.

#### Farkın Hesaplanması

Aşağıdaki program kataloğun iki anlık görüntüsünü alır, yukarıdaki kuralları uygular,
değişiklikleri sınıflandırır, sürüm numarasını türetir ve göç kılavuzunu üretir. Göç
maliyeti, her değişikliğin etkilediği çağrı yeri sayısıyla ölçülür.

```js
// surum.mjs — iki katalog anlik goruntusunu karsilastirip surum numarasi turetme

// Bir anlik goruntu: bilesen -> ozellik adi -> { deger kumesi, varsayilan, zorunlu }
const ESKI = {
  surum: "3.4.2",
  bilesen: {
    dugme:        { tur: { deger: ["birincil", "ikincil", "sessiz"], varsayilan: "ikincil", zorunlu: false },
                    boyut: { deger: ["kucuk", "orta"], varsayilan: "orta", zorunlu: false },
                    simge: { deger: null, varsayilan: null, zorunlu: false } },
    rozet:        { ton: { deger: ["notr", "olumlu", "uyari", "olumsuz"], varsayilan: "notr", zorunlu: false },
                    boyut: { deger: ["kucuk", "orta"], varsayilan: "orta", zorunlu: false },
                    ikonlu: { deger: null, varsayilan: false, zorunlu: false } },
    "acilir-liste": { boyut: { deger: ["kucuk", "orta"], varsayilan: "orta", zorunlu: false },
                    durum: { deger: null, varsayilan: null, zorunlu: false } },
    "kip-pencere": { boyut: { deger: ["orta", "genis"], varsayilan: "orta", zorunlu: false },
                    baslik: { deger: null, varsayilan: null, zorunlu: false } },
    "bildirim-seridi": { ton: { deger: ["bilgi", "olumlu", "uyari", "olumsuz"], varsayilan: "bilgi", zorunlu: false },
                    kapatilabilir: { deger: null, varsayilan: true, zorunlu: false } },
    "yukleme-gostergesi": { boyut: { deger: ["kucuk", "orta"], varsayilan: "orta", zorunlu: false } },
    iskelet:      { satir: { deger: null, varsayilan: 3, zorunlu: false } },
  },
};

const YENI = {
  bilesen: {
    dugme:        { tur: { deger: ["birincil", "ikincil", "sessiz", "tehlikeli"], varsayilan: "ikincil", zorunlu: false },
                    boyut: { deger: ["kucuk", "orta"], varsayilan: "orta", zorunlu: false },
                    "simge-adi": { deger: null, varsayilan: null, zorunlu: false },
                    genislik: { deger: ["icerik", "tam"], varsayilan: "icerik", zorunlu: false } },
    rozet:        { ton: { deger: ["notr", "olumlu", "uyari", "olumsuz"], varsayilan: "notr", zorunlu: false },
                    boyut: { deger: ["kucuk", "orta"], varsayilan: "kucuk", zorunlu: false } },
    "acilir-liste": { boyut: { deger: ["kucuk", "orta"], varsayilan: "orta", zorunlu: false },
                    durum: { deger: null, varsayilan: null, zorunlu: false },
                    aramali: { deger: null, varsayilan: false, zorunlu: false },
                    coklu: { deger: null, varsayilan: false, zorunlu: false } },
    "kip-pencere": { boyut: { deger: ["orta", "genis"], varsayilan: "orta", zorunlu: false },
                    baslik: { deger: null, varsayilan: null, zorunlu: true },
                    kapatilabilir: { deger: null, varsayilan: true, zorunlu: false } },
    "bildirim-seridi": { ton: { deger: ["olumlu", "uyari", "olumsuz"], varsayilan: "uyari", zorunlu: false },
                    kapatilabilir: { deger: null, varsayilan: true, zorunlu: false } },
    iskelet:      { satir: { deger: null, varsayilan: 3, zorunlu: false } },
    "sayi-alani": { boyut: { deger: ["kucuk", "orta"], varsayilan: "orta", zorunlu: false } },
  },
};

// Ad degisikligi elle bildirilir; yoksa kaldirma + ekleme olarak gorunur.
const YENIDEN_ADLANDIRMA = [{ bilesen: "dugme", eski: "simge", yeni: "simge-adi" }];

// Cagri yeri sayilari: goc maliyetini bu sayilar belirler.
const KULLANIM = { dugme: 412, rozet: 188, "acilir-liste": 57, "kip-pencere": 45, "bildirim-seridi": 63, "yukleme-gostergesi": 31, iskelet: 38 };
// Ozellik basina cagri yeri sayisi (o ozelligi acikca veren yerler).
const OZELLIK_KULLANIMI = { "dugme.simge": 96, "rozet.ikonlu": 24, "rozet.boyut": 61, "bildirim-seridi.ton": 63, "kip-pencere.baslik": 40 };

const esitKume = (a, b) => JSON.stringify(a) === JSON.stringify(b);
const adlandirmaBul = (bilesen, ad, yon) =>
  YENIDEN_ADLANDIRMA.find((r) => r.bilesen === bilesen && r[yon] === ad);

const degisiklikler = [];
const ekle = (sinif, bilesen, aciklama, gocAdimi, etki) =>
  degisiklikler.push({ sinif, bilesen, aciklama, gocAdimi, etki });

for (const [ad, eskiOzellikler] of Object.entries(ESKI.bilesen)) {
  const yeniOzellikler = YENI.bilesen[ad];
  if (!yeniOzellikler) {
    ekle("kirici", ad, "bileşen kaldırıldı", "yerine iskelet bileşeni kullanılır", KULLANIM[ad] ?? 0);
    continue;
  }
  for (const [ozAd, eskiOz] of Object.entries(eskiOzellikler)) {
    const adDegisimi = adlandirmaBul(ad, ozAd, "eski");
    if (adDegisimi) {
      ekle("kirici", ad, `özellik adı değişti: ${ozAd} → ${adDegisimi.yeni}`,
        `${ozAd} yazan her çağrı ${adDegisimi.yeni} olur`, OZELLIK_KULLANIMI[`${ad}.${ozAd}`] ?? 0);
      continue;
    }
    const yeniOz = yeniOzellikler[ozAd];
    if (!yeniOz) {
      ekle("kirici", ad, `özellik kaldırıldı: ${ozAd}`, `${ozAd} veren çağrılardan kaldırılır`,
        OZELLIK_KULLANIMI[`${ad}.${ozAd}`] ?? 0);
      continue;
    }
    if (!eskiOz.zorunlu && yeniOz.zorunlu) {
      ekle("kirici", ad, `özellik zorunlu hâle geldi: ${ozAd}`, `${ozAd} vermeyen çağrılara değer eklenir`,
        (KULLANIM[ad] ?? 0) - (OZELLIK_KULLANIMI[`${ad}.${ozAd}`] ?? 0));
    }
    if (eskiOz.varsayilan !== yeniOz.varsayilan) {
      ekle("kirici", ad, `varsayılan değişti: ${ozAd} ${JSON.stringify(eskiOz.varsayilan)} → ${JSON.stringify(yeniOz.varsayilan)}`,
        `${ozAd} vermeyen çağrılara eski değer açıkça yazılır`,
        (KULLANIM[ad] ?? 0) - (OZELLIK_KULLANIMI[`${ad}.${ozAd}`] ?? 0));
    }
    if (eskiOz.deger && yeniOz.deger && !esitKume(eskiOz.deger, yeniOz.deger)) {
      const kalkan = eskiOz.deger.filter((d) => !yeniOz.deger.includes(d));
      const gelen = yeniOz.deger.filter((d) => !eskiOz.deger.includes(d));
      if (kalkan.length) {
        ekle("kirici", ad, `değer kümesi daraldı: ${ozAd} ${JSON.stringify(kalkan)} kalktı`,
          `${JSON.stringify(kalkan)} veren çağrılar başka değere taşınır`, OZELLIK_KULLANIMI[`${ad}.${ozAd}`] ?? 0);
      }
      if (gelen.length) ekle("ekleme", ad, `değer kümesi genişledi: ${ozAd} ${JSON.stringify(gelen)} eklendi`, "-", 0);
    }
  }
  for (const ozAd of Object.keys(yeniOzellikler)) {
    if (eskiOzellikler[ozAd]) continue;
    if (adlandirmaBul(ad, ozAd, "yeni")) continue;
    const sinif = yeniOzellikler[ozAd].zorunlu ? "kirici" : "ekleme";
    ekle(sinif, ad, `${sinif === "kirici" ? "zorunlu " : ""}özellik eklendi: ${ozAd}`,
      sinif === "kirici" ? `${ozAd} her çağrıya eklenir` : "-", sinif === "kirici" ? (KULLANIM[ad] ?? 0) : 0);
  }
}
for (const ad of Object.keys(YENI.bilesen)) {
  if (!ESKI.bilesen[ad]) ekle("ekleme", ad, "bileşen eklendi", "-", 0);
}

const SIRA = { kirici: 0, ekleme: 1, yama: 2 };
degisiklikler.sort((a, b) => SIRA[a.sinif] - SIRA[b.sinif] || b.etki - a.etki);

console.log("sınıf     bileşen             değişiklik");
for (const d of degisiklikler) console.log(`${d.sinif.padEnd(9)} ${d.bilesen.padEnd(19)} ${d.aciklama}`);

const sayim = { kirici: 0, ekleme: 0, yama: 0 };
for (const d of degisiklikler) sayim[d.sinif]++;
console.log(`\nkırıcı: ${sayim.kirici}   ekleme: ${sayim.ekleme}   yama: ${sayim.yama}`);

// Surum turetimi: en agir sinif hangi bilesenin artacagini belirler.
function sonrakiSurum(mevcut, sayim) {
  const [ana, ikincil, yama] = mevcut.split(".").map(Number);
  if (sayim.kirici > 0) return `${ana + 1}.0.0`;
  if (sayim.ekleme > 0) return `${ana}.${ikincil + 1}.0`;
  return `${ana}.${ikincil}.${yama + 1}`;
}
console.log(`mevcut sürüm: ${ESKI.surum}   türetilen sürüm: ${sonrakiSurum(ESKI.surum, sayim)}`);
console.log(`kırıcılar ayıklansaydı: ${sonrakiSurum(ESKI.surum, { kirici: 0, ekleme: sayim.ekleme, yama: sayim.yama })}`);

// Goc kilavuzu: yalniz kirici degisiklikler, etkiledigi cagri yeri sayisiyla.
console.log("\n— GÖÇ KILAVUZU —");
let toplamEtki = 0;
for (const d of degisiklikler.filter((x) => x.sinif === "kirici")) {
  toplamEtki += d.etki;
  console.log(`* ${d.bilesen}: ${d.aciklama}\n    yapılacak: ${d.gocAdimi}\n    etkilenen çağrı yeri: ${d.etki}`);
}
console.log(`\ntoplam göç maliyeti: ${toplamEtki} çağrı yeri`);
console.log(`kırıcı değişiklik başına ortalama: ${(toplamEtki / sayim.kirici).toFixed(1)} çağrı yeri`);
```

```
sınıf     bileşen             değişiklik
kirici    rozet               varsayılan değişti: boyut "orta" → "kucuk"
kirici    dugme               özellik adı değişti: simge → simge-adi
kirici    bildirim-seridi     değer kümesi daraldı: ton ["bilgi"] kalktı
kirici    yukleme-gostergesi  bileşen kaldırıldı
kirici    rozet               özellik kaldırıldı: ikonlu
kirici    kip-pencere         özellik zorunlu hâle geldi: baslik
kirici    bildirim-seridi     varsayılan değişti: ton "bilgi" → "uyari"
ekleme    dugme               değer kümesi genişledi: tur ["tehlikeli"] eklendi
ekleme    dugme               özellik eklendi: genislik
ekleme    acilir-liste        özellik eklendi: aramali
ekleme    acilir-liste        özellik eklendi: coklu
ekleme    kip-pencere         özellik eklendi: kapatilabilir
ekleme    sayi-alani          bileşen eklendi

kırıcı: 7   ekleme: 6   yama: 0
mevcut sürüm: 3.4.2   türetilen sürüm: 4.0.0
kırıcılar ayıklansaydı: 3.5.0

— GÖÇ KILAVUZU —
* rozet: varsayılan değişti: boyut "orta" → "kucuk"
    yapılacak: boyut vermeyen çağrılara eski değer açıkça yazılır
    etkilenen çağrı yeri: 127
* dugme: özellik adı değişti: simge → simge-adi
    yapılacak: simge yazan her çağrı simge-adi olur
    etkilenen çağrı yeri: 96
* bildirim-seridi: değer kümesi daraldı: ton ["bilgi"] kalktı
    yapılacak: ["bilgi"] veren çağrılar başka değere taşınır
    etkilenen çağrı yeri: 63
* yukleme-gostergesi: bileşen kaldırıldı
    yapılacak: yerine iskelet bileşeni kullanılır
    etkilenen çağrı yeri: 31
* rozet: özellik kaldırıldı: ikonlu
    yapılacak: ikonlu veren çağrılardan kaldırılır
    etkilenen çağrı yeri: 24
* kip-pencere: özellik zorunlu hâle geldi: baslik
    yapılacak: baslik vermeyen çağrılara değer eklenir
    etkilenen çağrı yeri: 5
* bildirim-seridi: varsayılan değişti: ton "bilgi" → "uyari"
    yapılacak: ton vermeyen çağrılara eski değer açıkça yazılır
    etkilenen çağrı yeri: 0

toplam göç maliyeti: 346 çağrı yeri
kırıcı değişiklik başına ortalama: 49.4 çağrı yeri
```

#### Sürüm Numarasının Türetilmesi

Sürüm numarası bir karar değil, bir **sonuçtur**. Program yedi kırıcı değişiklik bulduğu
için 3.4.2 sürümü 4.0.0'a çıkıyor. Kırıcılar ayıklanıp yalnızca eklemeler yayımlansaydı
sürüm 3.5.0 olurdu; bu ikinci satır, sürüm numarasını tartışılabilir bir konu olmaktan
çıkarıp yayın kapsamının sonucu hâline getirir.

Bu ayrım pratik bir seçenek üretir. Yedi kırıcı değişiklikten hiçbiri yayına zorunlu
değilse, ekleme yayını hemen çıkabilir ve kırıcılar bir sonraki ana sürüme biriktirilebilir.
Kırıcı değişiklikleri **biriktirmek**, her birini ayrı ana sürümle yayımlamaktan ucuzdur:
kullanan ekipler bir kez göç eder, yedi kez değil.

Ad değişikliği satırı türetimin sınırını gösteriyor. `simge` özelliğinin `simge-adi`
olması, program tarafından ancak elle bildirildiği için ad değişikliği olarak
tanınabiliyor. Bildirilmeseydi iki ayrı satır üretilirdi: "özellik kaldırıldı: simge" ve
"özellik eklendi: simge-adi". Sürüm numarası yine 4.0.0 çıkardı, ama göç kılavuzu yanlış
olurdu; kullanan kişi kaldırma ile eklemenin aynı özelliğin iki hâli olduğunu bilemezdi.
Fark alma otomatik, **niyet bildirimi elle** yapılır.

#### Göç Maliyetinin Ölçülmesi

Göç kılavuzu, kırıcı değişikliklerin çağrı yeri sayısına göre sıralanmış hâlidir ve bu
sıralama iki kararı besler.

Birincisi, hangi kırıcı değişikliğin gerçekten değdiğidir. `rozet` bileşeninin `boyut`
varsayılanının değişmesi 127 çağrı yerini etkiliyor; `ikonlu` özelliğinin kaldırılması
yalnızca 24. Aynı yayında duran iki değişikliğin maliyeti beş kattan fazla farklı.
Varsayılan değişikliğinin gerekçesi bu 127 sayısına değmiyorsa, değişiklik geri çekilir ve
kırıcı listesinden düşer.

İkincisi, göçün nasıl yapılacağıdır. Listedeki adımların bir bölümü **mekaniktir**: ad
değişikliği, bir dizgi değiştirmedir ve kod dönüştürücüyle otomatikleştirilebilir. Bir
bölümü ise **karar ister**: `bildirim-seridi` bileşeninin `bilgi` tonunu veren 63 çağrının
hangisinin `olumlu`, hangisinin `uyari` olacağına bakmadan karar verilemez. Kılavuzun bu
iki sınıfı ayırması, göç süresinin tahminini gerçekçi kılar.

Son satır ilginç bir durum gösteriyor: `bildirim-seridi` bileşeninin varsayılan
değişikliğinin etkisi sıfır. Nedeni, o bileşeni kullanan 63 çağrının hepsinin `ton`
değerini zaten açıkça vermesi. Değişiklik sözleşme gereği kırıcıdır — sürüm numarasını
yükseltir — ama göç maliyeti yoktur. Sözleşme ile maliyet ayrı şeylerdir; sürüm numarası
sözleşmeye, yayın planı maliyete bakar.

#### Yayın Biçimi

Kataloğun tek paket olarak mı yoksa bileşen başına ayrı paketler olarak mı yayımlanacağı,
yukarıdaki sayıların dağılımını değiştirir.

**Tek paket** yayımlandığında bir bileşendeki kırıcı değişiklik tüm paketin ana sürümünü
yükseltir. Yukarıdaki örnekte `rozet` yüzünden `iskelet` bileşenini kullanan ekip de 4.0.0
sürümüne geçmek zorunda kalır; kendisi için hiçbir şey değişmediği hâlde bir göç kararı vermesi
gerekir. Buna karşılık bağımlılık yönetimi tektir ve sürümler arası uyum sorunu çıkmaz.

**Bileşen başına paket** yayımlandığında her bileşen kendi hızında ilerler ve göç kararları
ayrışır. Bedeli, bağımlılık ağacında aynı sistemin farklı sürümlerinin yan yana
bulunabilmesidir; iki bileşen aynı belirteç katmanının farklı sürümlerine bağlanırsa
tutarlılık, sistemin var oluş nedeni, kaybolur.

Seçim, iki sayının karşılaştırılmasıyla yapılır: gereksiz göçe zorlanan çağrı yeri sayısı
ile yan yana duran sürümlerden doğan tutarsızlık sayısı. Katalog küçük ve bileşenler
birbirine sıkı bağlıyken tek paket ucuzdur; katalog büyüdükçe ve bileşenler arası bağ
gevşedikçe ayrı paketler ucuzlar.

#### Kullanımdan Kaldırma Penceresi

Kırıcı değişiklik doğrudan yapılmaz; önce kullanımdan kaldırma işareti konur, sonra
kaldırma yapılır. Aradaki süre bir **pencere**dir ve takvimle değil yayınla ölçülür:
"bir ana sürüm boyunca" ifadesi, "üç ay" ifadesinden daha kullanışlıdır, çünkü yayın
kadansı değişse bile anlamını korur.

Pencerenin işlevi, göçün yayın anına yığılmasını önlemektir. `simge` özelliği bir ana
sürüm önce kullanımdan kaldırılmış olsaydı, 96 çağrı yerinin bir bölümü zaten taşınmış
olurdu ve 4.0.0 yayınındaki göç maliyeti daha düşük çıkardı. Kullanımdan kaldırma işareti
konurken uyarı metninin **göç adımını** içermesi de bu yüzden gereklidir; uyarı yalnızca
"bu özellik kaldırılacak" diyorsa hiçbir göçü hızlandırmaz.

#### Özet

- Bileşen arayüzünde kırıcı olanlar sayılabilir bir listedir: bileşenin kaldırılması,
  özelliğin kaldırılması veya adının değişmesi, zorunlu hâle gelme, varsayılanın değişmesi
  ve değer kümesinin daralması.
- Sürüm numarası bir karar değil, yayına giren en ağır değişiklik sınıfının sonucudur;
  kırıcıları biriktirip tek ana sürümde yayımlamak, her birini ayrı yayımlamaktan ucuzdur.
- Fark alma otomatikleştirilebilir ama ad değişikliği gibi niyet bildirimleri elle
  yapılmalıdır; bildirilmezse kılavuz yanlış olur.
- Göç maliyeti etkilenen çağrı yeri sayısıyla ölçülür ve mekanik adımlarla karar
  gerektiren adımların ayrılması gerekir.
- Sözleşme ile maliyet ayrı şeylerdir: göç maliyeti sıfır olan bir değişiklik de sürüm
  numarasını yükseltir.
- Tek paket ile bileşen başına paket arasındaki seçim, gereksiz göçe zorlanan çağrı yeri
  sayısı ile yan yana duran sürümlerden doğan tutarsızlık sayısı karşılaştırılarak yapılır.

#### Sonraki Adım

Bu dersin ürettiği sürüm ve göç kılavuzu, değişikliklerin nereden geldiğini sormadı;
arayüz tanımı iki kez okundu ve arada bir şey değişmişti. Değişikliği kimin önerdiği,
kimin incelediği ve hangi ölçütle kabul edildiği açık kaldı. `dugme` bileşenine `genislik`
özelliğinin eklenmesi kimin kararıydı ve bu karar kaç günde verildi? Sonraki ders katkı
sürecini bir durum makinesi olarak kurar, geçersiz geçişleri saptar ve öneriden yayına
kadar geçen süreyi aşamalara ayırarak ölçer.

### Katkı Süreci

Source: https://academia.sh/tr/kurslar/tasarim-sistemleri/katki-sureci

Öneriden yayına uzanan akışın durum makinesi olarak tanımlanması, kabul kapılarının onaylara bağlanması, geçersiz geçişlerin saptanması ve aşama sürelerinin ölçülerek darboğazın bulunması.

Önceki dersin ürettiği sürüm ve göç kılavuzu, değişikliklerin nereden geldiğini sormadı;
arayüz tanımı iki kez okundu ve arada bir şey değişmişti. `dugme` bileşenine `genislik`
özelliğinin eklenmesi kimin kararıydı, kim inceledi ve bu karar kaç günde verildi?

Katkı süreci bu soruların yanıtını verir. Bir tasarım sistemi, kullanan ekiplerin
değiştiremediği bir şeye dönüştüğünde iki sonuçtan biri doğar: ya sistem ihtiyaçların
gerisinde kalır ya da ekipler sistemi atlayıp kendi kopyalarını yazar. Katkı süreci bu
ikisini önlemek için vardır, ama kendisi de bir maliyet taşır ve maliyeti ölçülmelidir.

#### Akışın Durumları

Bir katkı, öneri anından yayına kadar birkaç ayrı durumdan geçer ve her durumun bir sorusu
vardır:

- **Öneri.** Ne isteniyor? Bu aşamada çözüm değil, ihtiyaç yazılır: hangi ekranda, hangi
  sorun, mevcut bileşenlerle neden çözülemiyor.
- **Keşif.** Bu gerçekten sistemin işi mi? İlk derste tanımlanan kapsam ölçütü burada
  işletilir; ihtiyaç tek ekibe özgüyse katkı ürüne yönlendirilir.
- **Taslak.** Nasıl çözülecek? Arayüz tasarlanır, belge bölümleri yazılır, göç etkisi
  varsa hesaplanır.
- **İnceleme.** Çözüm sistemin ölçütlerini karşılıyor mu? Üç ayrı inceleme yürür: tasarım,
  kod ve erişilebilirlik.
- **Kabul.** Karar verildi. Yayın notunun yazılması bekleniyor.
- **Yayın.** Katkı bir sürümle çıktı.

Bunlara üç son durum eklenir: **reddedildi**, **ertelendi** ve incelemeden taslağa dönüş.
Reddedilme bir başarısızlık değil, kapsam ölçütünün çalıştığının kanıtıdır; hiç
reddedilmeyen bir katkı süreci, ölçütün işletilmediğini gösterir.

#### Durum Makinesinin Kurulması

Akışı yazılı bir belge olarak tanımlamak yetmez; hangi geçişin geçerli olduğu ve bir
geçişin hangi onayları gerektirdiği makineyle denetlenebilir olmalıdır. Aşağıdaki program
yedi katkının geçmişini alır, geçişleri ve kapıları denetler, aşama sürelerini hesaplar ve
darboğazı bulur.

```js
// katki.mjs — katki akisinin durum makinesi, kapi denetimi ve asama sureleri

// Gecerli gecisler. Listede olmayan her gecis gecersizdir.
const GECIS = {
  oneri:      ["kesif", "reddedildi", "ertelendi"],
  kesif:      ["taslak", "reddedildi", "ertelendi"],
  taslak:     ["inceleme", "ertelendi"],
  inceleme:   ["kabul", "taslak", "reddedildi"],
  kabul:      ["yayin"],
  yayin:      [],
  reddedildi: [],
  ertelendi:  ["kesif"],
};

// Kapilar: bir gecisin yapilabilmesi icin toplanmis olmasi gereken onaylar.
const KAPI = {
  "inceleme->kabul": ["tasarim", "kod", "erisim"],
  "kabul->yayin":    ["surum-notu"],
};

// Katki gecmisleri: [gun, durum] cifti ve gecis anina kadar toplanmis onaylar.
const KATKILAR = [
  { ad: "dugme.genislik", onay: ["tasarim", "kod", "erisim", "surum-notu"],
    gecmis: [[0, "oneri"], [2, "kesif"], [5, "taslak"], [9, "inceleme"], [12, "kabul"], [14, "yayin"]] },
  { ad: "acilir-liste.aramali", onay: ["tasarim", "kod", "erisim", "surum-notu"],
    gecmis: [[0, "oneri"], [6, "kesif"], [11, "taslak"], [18, "inceleme"], [47, "kabul"], [52, "yayin"]] },
  { ad: "sayi-alani", onay: ["tasarim", "kod", "erisim", "surum-notu"],
    gecmis: [[0, "oneri"], [3, "kesif"], [10, "taslak"], [21, "inceleme"], [29, "taslak"], [36, "inceleme"], [44, "kabul"], [46, "yayin"]] },
  { ad: "arac-ipucu.belge", onay: ["tasarim", "kod", "erisim", "surum-notu"],
    gecmis: [[0, "oneri"], [4, "ertelendi"], [61, "kesif"], [66, "taslak"], [70, "inceleme"], [78, "kabul"], [80, "yayin"]] },
  { ad: "kart.tiklanabilir", onay: ["tasarim", "kod", "erisim", "surum-notu"],
    gecmis: [[0, "oneri"], [3, "inceleme"], [8, "kabul"], [10, "yayin"]] },
  { ad: "tablo.yogunluk", onay: ["tasarim", "kod", "surum-notu"],
    gecmis: [[0, "oneri"], [5, "kesif"], [9, "taslak"], [16, "inceleme"], [23, "kabul"], [25, "yayin"]] },
  { ad: "rozet.ikonlu-kaldirma", onay: ["tasarim"],
    gecmis: [[0, "oneri"], [7, "kesif"], [19, "reddedildi"]] },
];

// 1. Gecis ve kapi denetimi.
console.log("geçersiz geçişler ve kapı ihlalleri");
let kusurlu = 0;
for (const k of KATKILAR) {
  const kusur = [];
  for (let i = 1; i < k.gecmis.length; i++) {
    const onceki = k.gecmis[i - 1][1];
    const simdi = k.gecmis[i][1];
    if (!GECIS[onceki].includes(simdi)) kusur.push(`geçersiz geçiş: ${onceki} → ${simdi}`);
    const kapi = KAPI[`${onceki}->${simdi}`];
    if (kapi) {
      const eksik = kapi.filter((o) => !k.onay.includes(o));
      if (eksik.length) kusur.push(`${onceki} → ${simdi} için eksik onay: ${eksik.join(", ")}`);
    }
  }
  if (kusur.length) {
    kusurlu++;
    console.log(`${k.ad.padEnd(23)} ${kusur.join(" | ")}`);
  }
}
console.log(`kusurlu katkı: ${kusurlu}/${KATKILAR.length}`);

// 2. Asamada gecirilen sureler.
const sureler = {};
for (const k of KATKILAR) {
  for (let i = 1; i < k.gecmis.length; i++) {
    const durum = k.gecmis[i - 1][1];
    const sure = k.gecmis[i][0] - k.gecmis[i - 1][0];
    (sureler[durum] ??= []).push(sure);
  }
}
const ortanca = (dizi) => {
  const s = [...dizi].sort((a, b) => a - b);
  const o = Math.floor(s.length / 2);
  return s.length % 2 ? s[o] : (s[o - 1] + s[o]) / 2;
};

console.log("\naşama       geçiş sayısı   ortanca gün   en uzun gün   toplam gün");
for (const durum of ["oneri", "kesif", "taslak", "inceleme", "kabul", "ertelendi"]) {
  const d = sureler[durum] ?? [];
  if (d.length === 0) continue;
  console.log(
    `${durum.padEnd(11)} ${String(d.length).padStart(12)} ${String(ortanca(d)).padStart(13)} ` +
    `${String(Math.max(...d)).padStart(13)} ${String(d.reduce((t, x) => t + x, 0)).padStart(12)}`
  );
}

// 3. Uctan uca sure: yayina ulasan katkilarda oneri -> yayin.
const yayinlanan = KATKILAR.filter((k) => k.gecmis.at(-1)[1] === "yayin");
const uctanUca = yayinlanan.map((k) => k.gecmis.at(-1)[0] - k.gecmis[0][0]);
console.log("\nkatkı                   uçtan uca gün");
for (const k of yayinlanan) console.log(`${k.ad.padEnd(23)} ${String(k.gecmis.at(-1)[0]).padStart(14)}`);
console.log(`ortanca uçtan uca süre: ${ortanca(uctanUca)} gün`);
console.log(`en uzun: ${Math.max(...uctanUca)} gün   en kısa: ${Math.min(...uctanUca)} gün`);

// 4. Darbogaz: uctan uca surenin hangi asamaya dustugunu pay olarak ver.
const toplamSure = Object.values(sureler).flat().reduce((t, x) => t + x, 0);
console.log("\naşamanın toplam süredeki payı");
const paylar = Object.entries(sureler)
  .map(([durum, d]) => [durum, d.reduce((t, x) => t + x, 0)])
  .sort((a, b) => b[1] - a[1]);
for (const [durum, t] of paylar) {
  console.log(`${durum.padEnd(11)} ${String(t).padStart(5)} gün  %${((t / toplamSure) * 100).toFixed(1)}`);
}
```

```
geçersiz geçişler ve kapı ihlalleri
kart.tiklanabilir       geçersiz geçiş: oneri → inceleme
tablo.yogunluk          inceleme → kabul için eksik onay: erisim
kusurlu katkı: 2/7

aşama       geçiş sayısı   ortanca gün   en uzun gün   toplam gün
oneri                  7             4             7           30
kesif                  6             5            12           36
taslak                 6             7            11           40
inceleme               7             8            29           68
kabul                  6             2             5           15
ertelendi              1            57            57           57

katkı                   uçtan uca gün
dugme.genislik                      14
acilir-liste.aramali                52
sayi-alani                          46
arac-ipucu.belge                    80
kart.tiklanabilir                   10
tablo.yogunluk                      25
ortanca uçtan uca süre: 35.5 gün
en uzun: 80 gün   en kısa: 10 gün

aşamanın toplam süredeki payı
inceleme       68 gün  %27.6
ertelendi      57 gün  %23.2
taslak         40 gün  %16.3
kesif          36 gün  %14.6
oneri          30 gün  %12.2
kabul          15 gün  %6.1
```

#### İki Kusurun Okunması

Yedi katkının ikisi kusurlu. İki kusur birbirinden farklıdır ve farklı şeyler bozar.

`kart.tiklanabilir` öneriden doğrudan incelemeye geçmiş; keşif ve taslak atlanmış. Sonucu,
listedeki en kısa uçtan uca süredir: on gün. Atlanan iki aşamanın işi de yapılmamıştır: kapsam
ölçütü işletilmediği için bu değişikliğin sistemin işi olup olmadığı sorulmamış, taslak
yazılmadığı için de göç etkisi hesaplanmamıştır. Hızlı görünen bu yol, maliyeti sonraya taşır.

`tablo.yogunluk` ise geçişleri doğru yapmış ama erişilebilirlik onayı olmadan kabul
edilmiş. Bu satır ilk dersteki katalog tablosunun bir satırıyla örtüşüyor: `tablo`
bileşeninin erişilebilirlik incelemesi geçmemiş görünüyordu. İki denetim aynı boşluğu iki
farklı yerden gösteriyor — biri kataloğun durumundan, diğeri katkının geçmişinden.

Kapıların değeri buradadır. Bir inceleme türünün "yapılması beklenen" bir şey olarak
yazılması yeterli değildir; geçişin koşulu hâline getirilmedikçe, işler sıkıştığında ilk
atlanan o olur.

#### Sürenin Nereye Gittiği

İkinci tablo aşamaların sürelerini veriyor. İnceleme, hem ortanca (8 gün) hem toplam süre
payı (yüzde 27,6) bakımından en pahalı aşama. Ancak toplam süre payı tek başına yanıltıcı
olabilir: `ertelendi` durumu yüzde 23,2 pay alıyor, oysa bu payın tamamı tek bir katkıdan
geliyor — `arac-ipucu.belge` katkısı 57 gün ertelenmiş durumda beklemiş.

Bu ayrım, ortanca ile toplamın neden birlikte okunduğunu gösterir. Toplam, kaynakların
nereye gittiğini söyler; ortanca, tipik bir katkının ne kadar beklediğini. `ertelendi`
durumunun ortancası da 57'dir, ama tek bir gözlem üzerinden hesaplandığı için bir eğilim
değil, tek bir olaydır.

İnceleme aşamasında da benzer bir yayılım var: ortanca 8 gün, en uzun 29 gün. Yirmi dokuz
günlük bekleme `acilir-liste.aramali` katkısına ait ve bu, üçüncü tablodaki 52 günlük
uçtan uca sürenin çoğunu tek başına açıklıyor. İnceleme aşamasının ortalaması değil,
**kuyruğu** sorundur; sürecin iyileştirilmesi ortancayı değil bu uzun kuyruğu hedef almalıdır.

`sayi-alani` katkısı incelemeden taslağa dönmüş, sonra yeniden incelemeye girmiş. Bu geri
dönüş geçerli bir geçiştir ve akışın çalıştığının işaretidir; incelemenin hiçbir katkıyı
geri döndürmemesi, incelemenin biçimsel hâle geldiğini gösterirdi.

#### Sürecin Maliyeti ve Katkının Sınıflandırılması

Ortanca uçtan uca süre 35,5 gün. Bu sayı tek başına iyi ya da kötü değildir; bir eşikle
karşılaştırıldığında anlam kazanır. Eşik şudur: **bir katkının sistemden geçme süresi,
kendi kopyasını yazma süresini aştığında ekipler kendi kopyalarını yazar.** İsteğe bağlı
bir özellik eklemek bir ekibin kendi deposunda yarım gün sürüyorsa, otuz beş günlük bir
akış o özelliği sisteme hiç getirmez; sistem, gerçek ihtiyaçları görmez hâle gelir.

Çözüm, kapıları kaldırmak değil, katkıları sınıflandırmaktır. Önceki derste kurulan
sınıflandırma burada doğrudan işe yarar:

- **Ekleme sınıfı katkılar** — isteğe bağlı yeni özellik, değer kümesinin genişlemesi —
  hiçbir çağrıyı bozmaz. Bunlar için keşif aşaması kısaltılabilir ve tasarım incelemesi
  ile kod incelemesi birleştirilebilir.
- **Kırıcı sınıfı katkılar** tam akışı izler; göç maliyeti hesaplanmadan taslak
  tamamlanmış sayılmaz.
- **Yeni bileşen önerileri** en uzun akışı izler, çünkü kapsam ölçütü asıl burada
  işletilir.

Bu üç yolun süreleri ayrı ayrı ölçülürse, "sürecimiz yavaş" cümlesi ölçülebilir bir
iddiaya dönüşür: hangi sınıfta, hangi aşamada, kaç gün.

#### Katkı Verenin Gördüğü

Sürecin bir de dışarıdan görünen yüzü vardır. Katkı veren kişi, katkısının hangi durumda
olduğunu ve sıradaki adımın kimde beklediğini görebilmelidir. Durum makinesinin en somut
faydası budur: her katkının tek bir durumu vardır ve o durumun sahibi bellidir.

İkinci gereklilik, **tamamlanmışlık tanımının** yazılı olmasıdır. Taslağın hangi koşullarda
incelemeye geçebileceği — belge bölümlerinin yazılmış, göç etkisinin hesaplanmış, örneğin
eklenmiş olması — katkı verene önceden söylenmezse, inceleme aşaması bir eksik listesi
üretme yerine döner ve kuyruk uzar. İnceleme aşamasının 29 günlük en uzun beklemesi çoğu
zaman incelemenin yavaşlığından değil, incelemeye eksik girmiş bir taslaktan doğar.

#### Özet

- Katkı akışı öneri, keşif, taslak, inceleme, kabul ve yayın durumlarından oluşur; her
  durumun ayrı bir sorusu ve ayrı bir sahibi vardır.
- Geçişlerin geçerliliği ve kabul kapılarının onayları makineyle denetlenebilir;
  denetlenmediğinde en çok atlanan aşama, koşula bağlanmamış incelemedir.
- Aşama atlayan bir katkı hızlı görünür ama atlanan aşamanın işi yapılmamıştır; maliyet
  sonraya taşınır.
- Toplam süre payı kaynakların nereye gittiğini, ortanca tipik bekleme süresini söyler;
  ikisi birlikte okunmadığında tek bir uzun bekleme eğilim gibi görünür.
- Katkının sistemden geçme süresi kendi kopyasını yazma süresini aştığında sistem gerçek
  ihtiyaçları görmez olur; çözüm kapıları kaldırmak değil, ekleme ve kırıcı katkılara ayrı
  yollar tanımlamaktır.
- Tamamlanmışlık tanımı önceden yazılmadığında inceleme aşaması eksik listesi üretme
  yerine döner ve kuyruk uzar.

#### Sonraki Adım

Bu dersin durum makinesi bir şeyi sormadı: incelemeyi kim yapar? Üç onayın da tek bir
merkezî ekipten gelmesiyle, onayların katkı veren ekiplerin kendi içinden gelmesi farklı
sistemler üretir; ikisinin de kuyruk süresi, tutarlılık düzeyi ve ölçekle davranışı
farklıdır. Sonraki ders bu soruyu yönetişim modeli olarak ele alır: merkezî, federatif ve
karma düzenleri talep ile kapasite üzerinden modelleyip her birinin kuyruk süresini ve
ürettiği sapma sayısını iki farklı ölçekte hesaplar.

### Yönetişim Modeli

Source: https://academia.sh/tr/kurslar/tasarim-sistemleri/yonetisim-modeli

Merkezî, federatif ve karma yönetişimin talep ile kapasite üzerinden modellenmesi, kuyruk süresinin yerel kopyaya dönüşmesi, ölçekle değişen kırılma noktası ve merkezde kalması gereken kararların ayrılması.

Önceki dersin durum makinesi bir şeyi sormadı: incelemeyi kim yapar? Üç onayın da tek bir
merkezî ekipten gelmesiyle, onayların katkı veren ekiplerin kendi içinden gelmesi farklı
sistemler üretir.

Yönetişim modeli bu sorunun yanıtıdır ve iki büyüklük arasında bir seçimdir: **tutarlılık**
ile **hız**. Tek bir ekip her şeyi incelerse kararlar tutarlı olur ama kuyruk uzar; her
ekip kendini incelerse kuyruk kısalır ama kararlar dağılır. Bu ders ödünleşimi bir modelle
ölçer ve seçimin ölçeğe bağlı olduğunu gösterir.

#### Üç Model

**Merkezî yönetişim.** Sistemin sahibi tek bir ekiptir. Bileşen tasarımı, inceleme ve
yayın bu ekipten geçer. Ürün ekipleri katkı önerir, ama karar merkezdedir. Kapasite bu
ekibin büyüklüğüyle sınırlıdır ve kuruluş büyüdükçe artmaz.

**Federatif yönetişim.** Sistemin bileşenleri ürün ekipleri arasında paylaştırılır. Her
ekip sahiplendiği bileşenleri kendi içinde inceler ve yayımlar. Kapasite ekip sayısıyla
birlikte artar. Ancak inceleyen kişi yalnızca kendi ürününü görür; başka bir üründe aynı
kararın nasıl verildiğini bilmez.

**Karma yönetişim.** Çekirdek bir ekip belirteç katmanını, adlandırma sözleşmesini ve
kırıcı değişiklik onayını elinde tutar; taslak ve ilk inceleme ürün ekiplerinde yapılır.
Kapasite hem ürün ekiplerinin hazırlama kapasitesine hem de çekirdeğin onay kapasitesine
bağlıdır; hangisi küçükse o sınırlar.

Bu üç modelin farkı, katkıların ne kadarının **sistem genelini gören bir gözle**
incelendiğidir. Aşağıdaki model bu oranı doğrudan bir değişken olarak alır ve sapma
sayısını buradan hesaplar.

#### Modelin Kurulması

Model üç şeyi izler: dönem başına gelen talep, modelin kapasitesi ve kuyrukta biriken iş.
Kuyruk belirli bir süreyi aştığında katkı terk edilir ve o ekip kendi kopyasını yazar; bu
kopya bir sapmadır. İkinci sapma kaynağı, sistem görüşü olmadan yapılan incelemelerdir.

```js
// yonetisim.mjs — uc yonetisim modelinin kuyruk suresi ve sapma sayisiyla karsilastirilmasi

// Varsayimlar (hepsi bu kurulusun olculmus degerleri olarak alinmistir):
const DONEM = 12;              // kac donem boyunca izlenecek
const TALEP_EKIP_BASINA = 5;   // bir urun ekibinin donem basina urettigi katki talebi
const BEKLEME_ESIGI = 2;       // kac donemden uzun beklenen is terk edilir
const TEMEL_SAPMA = 0.35;      // sistem gorusu olmadan incelenen katkinin sapma olasiligi

// Modeller. "kapasite" bir donemde tamamlanabilen katki sayisini verir;
// "gorus" ise katkilarin ne kadarinin sistem genelini goren bir gozle incelendigini.
const MODELLER = [
  {
    ad: "merkezî",
    kapasite: () => 22,               // tek ekip; ekip sayisindan bagimsiz
    gorus: 0.95,
  },
  {
    ad: "federatif",
    kapasite: (ekip) => 6 * ekip,     // her urun ekibi kendi incelemesini yapar
    gorus: 0.15,                      // yalnizca ornekleme denetimi
  },
  {
    ad: "karma",
    kapasite: (ekip) => Math.min(55, 6 * ekip), // urun ekibi hazirlar, cekirdek onaylar
    gorus: 0.70,
  },
];

function calistir(kapasite, gorus, talep) {
  let kuyruk = 0, kabul = 0, yerelKopya = 0, kuyrukToplami = 0;
  for (let d = 0; d < DONEM; d++) {
    kuyruk += talep;
    const islenen = Math.min(kuyruk, kapasite);
    kuyruk -= islenen;
    kabul += islenen;
    const tavan = BEKLEME_ESIGI * kapasite;
    if (kuyruk > tavan) {
      yerelKopya += kuyruk - tavan;
      kuyruk = tavan;
    }
    kuyrukToplami += kuyruk;
  }
  const ortalamaKuyruk = kuyrukToplami / DONEM;
  return {
    kabul,
    yerelKopya,
    bekleme: ortalamaKuyruk / kapasite,
    incelemeSapmasi: kabul * TEMEL_SAPMA * (1 - gorus),
    sonKuyruk: kuyruk,
  };
}

for (const ekip of [4, 12]) {
  const talep = TALEP_EKIP_BASINA * ekip;
  console.log(`\n=== ${ekip} ürün ekibi · dönem başına ${talep} katkı talebi · ${DONEM} dönem · toplam talep ${talep * DONEM} ===`);
  console.log("model       kapasite   yayımlanan   bekleyen   bekleme(dönem)   inceleme sapması   yerel kopya   toplam sapma");
  const satirlar = [];
  for (const m of MODELLER) {
    const kap = m.kapasite(ekip);
    const s = calistir(kap, m.gorus, talep);
    const toplam = s.incelemeSapmasi + s.yerelKopya;
    satirlar.push({ ad: m.ad, toplam });
    console.log(
      `${m.ad.padEnd(11)} ${String(kap).padStart(8)} ${String(s.kabul).padStart(12)} ${String(s.sonKuyruk).padStart(10)} ` +
      `${s.bekleme.toFixed(2).padStart(16)} ${s.incelemeSapmasi.toFixed(1).padStart(18)} ` +
      `${String(s.yerelKopya).padStart(13)} ${toplam.toFixed(1).padStart(14)}`
    );
  }
  satirlar.sort((a, b) => a.toplam - b.toplam);
  console.log(`en az sapma üreten model: ${satirlar[0].ad} (${satirlar[0].toplam.toFixed(1)})`);
}

// Kirilma noktasi: merkezî model kac urun ekibine kadar kuyruk uretmeden yetisir?
console.log("\nekip   talep   merkezî kapasite   kuyruk üretir mi");
for (let ekip = 2; ekip <= 8; ekip++) {
  const talep = TALEP_EKIP_BASINA * ekip;
  const kap = MODELLER[0].kapasite(ekip);
  console.log(`${String(ekip).padStart(4)} ${String(talep).padStart(7)} ${String(kap).padStart(18)} ${(talep > kap ? "evet" : "hayır").padStart(17)}`);
}

// Karma modelde cekirdek onay kapasitesinin sonucu ne kadar degistirdigi.
console.log("\nkarma modelde çekirdek onay kapasitesi (12 ekip, talep 60, toplam talep 720)");
console.log("çekirdek   etkin kapasite   yayımlanan   bekleyen   yerel kopya   toplam sapma");
for (const cekirdek of [35, 45, 55, 65]) {
  const kap = Math.min(cekirdek, 6 * 12);
  const s = calistir(kap, 0.70, 60);
  console.log(
    `${String(cekirdek).padStart(8)} ${String(kap).padStart(16)} ${String(s.kabul).padStart(12)} ` +
    `${String(s.sonKuyruk).padStart(10)} ${String(s.yerelKopya).padStart(13)} ` +
    `${(s.incelemeSapmasi + s.yerelKopya).toFixed(1).padStart(14)}`
  );
}
```

```

=== 4 ürün ekibi · dönem başına 20 katkı talebi · 12 dönem · toplam talep 240 ===
model       kapasite   yayımlanan   bekleyen   bekleme(dönem)   inceleme sapması   yerel kopya   toplam sapma
merkezî           22          240          0             0.00                4.2             0            4.2
federatif         24          240          0             0.00               71.4             0           71.4
karma             24          240          0             0.00               25.2             0           25.2
en az sapma üreten model: merkezî (4.2)

=== 12 ürün ekibi · dönem başına 60 katkı talebi · 12 dönem · toplam talep 720 ===
model       kapasite   yayımlanan   bekleyen   bekleme(dönem)   inceleme sapması   yerel kopya   toplam sapma
merkezî           22          264         44             1.98                4.6           412          416.6
federatif         72          720          0             0.00              214.2             0          214.2
karma             55          660         60             0.59               69.3             0           69.3
en az sapma üreten model: karma (69.3)

ekip   talep   merkezî kapasite   kuyruk üretir mi
   2      10                 22             hayır
   3      15                 22             hayır
   4      20                 22             hayır
   5      25                 22              evet
   6      30                 22              evet
   7      35                 22              evet
   8      40                 22              evet

karma modelde çekirdek onay kapasitesi (12 ekip, talep 60, toplam talep 720)
çekirdek   etkin kapasite   yayımlanan   bekleyen   yerel kopya   toplam sapma
      35               35          420         70           230          274.1
      45               45          540         90            90          146.7
      55               55          660         60             0           69.3
      65               65          720          0             0           75.6
```

#### Sonucun Ölçekle Değişmesi

İki tablo aynı üç modeli iki farklı ölçekte çalıştırıyor ve sıralama tersine dönüyor.

Dört ürün ekibinde merkezî model en iyi sonucu veriyor: 240 katkının tamamı yayımlanıyor,
hiç bekleme yok ve sapma 4,2. Federatif model aynı işi aynı sürede yapıyor ama sapması 71,4
— on yedi kat fazla. Bu ölçekte merkezî ekibin kapasitesi talebi karşılamaya yetiyor;
federatif modelin sağladığı ek kapasitenin hiçbir karşılığı yok, yalnızca inceleme
görüşünün düşmesinin bedeli ödeniyor.

On iki ürün ekibinde merkezî model çöküyor: sapma 416,6. Bu sayının 412'si yerel kopya.
Merkezî ekip 720 talebin yalnızca 264'ünü karşılayabiliyor; kalan iş kuyrukta bekleme
eşiğini aşıyor ve ekipler kendi kopyalarını yazıyor.

Buradaki mekanizma, yönetişim tartışmalarının çoğunda gözden kaçan noktadır. Merkezî
modelin maliyeti **beklemenin kendisi değildir**; bekleme, sistemi terk etmeye dönüştüğü
için maliyettir. Merkezî ekibin ürettiği inceleme kalitesi hiç düşmüyor — sapması hâlâ 4,6
ile en düşük değer. Sistem, incelediği şeyde iyi olmayı sürdürüyor; sorun, işlerin büyük
bölümünü hiç incelemiyor olması.

Üçüncü tablo merkezî modelin kırılma noktasını veriyor: bu kapasiteyle dört ürün ekibine
kadar kuyruk oluşmuyor, beşinci ekipten sonra oluşmaya başlıyor. Bu sayı bir kural değil,
bu kuruluşun kapasitesiyle talebinin oranıdır; merkezî ekip büyütülürse nokta sağa kayar.
Kararı veren şey modelin adı değil, bu orandır.

#### Karma Modelin Ayarı

Son tablo karma modelin tek bir parametreye — çekirdeğin onay kapasitesine — ne kadar
duyarlı olduğunu gösteriyor ve dikkatli okunması gerekir.

Çekirdek kapasitesi 35 iken karma model neredeyse merkezî model kadar kötü: 230 yerel
kopya. Kapasite 45'e çıktığında yerel kopya 90'a, 55'e çıktığında sıfıra iniyor. Yani
karma modelin işe yaraması, çekirdeğin onay işini **ürün ekiplerinin hazırlama hızına
yetişecek** kadar hızlı yapmasına bağlıdır. Çekirdek darboğaz olursa karma model, adı
karma olan bir merkezî modeldir.

Son satır bir tuzak içeriyor. Çekirdek kapasitesi 65 olduğunda toplam sapma 75,6'ya
çıkıyor — 55'teki 69,3 değerinden yüksek. Bu, kapasiteyi artırmanın zararlı olduğu anlamına
gelmez. Kapasite 55 iken 660 katkı yayımlanmış ve 60 katkı hâlâ kuyrukta bekliyor;
kapasite 65 iken 720 katkının tamamı yayımlanmış. Yayımlanan katkı başına sapma iki
durumda da aynıdır: 69,3 bölü 660 ile 75,6 bölü 720, ikisi de 0,105. Toplam sapmanın
düşük görünmesinin nedeni, işin bir bölümünün henüz yapılmamış olmasıdır.

Bu, ölçüm kurarken kaçınılması gereken genel bir kusurdur: **karşılanmamış talep, bir
başarı gibi görünebilir.** Yayımlanan katkı sayısı ve bekleyen kuyruk uzunluğu raporlanmaz
ise, iş yapmayan bir sistem en temiz rakamları üretir.

#### Merkezde Kalması Gereken Kararlar

Model seçimi tek bir düğme değildir; her karar türü ayrı ayrı merkezde tutulabilir ya da
dağıtılabilir. Ayrımın ölçütü, kararın **kaç bileşeni birden etkilediğidir**.

Merkezde kalması gereken kararlar, tek tek bileşenlerin ötesinde etki üretenlerdir:
belirteç katmanının yapısı, adlandırma sözleşmesi, olgunluk düzeylerinin çıkış ölçütleri,
kırıcı değişiklik onayı ve erişilebilirlik ölçütleri. Bunlardan birinin iki ekipte farklı
uygulanması, sistemin bir sistem olmasını sona erdirir.

Dağıtılabilecek kararlar ise tek bileşenin içinde kalanlardır: bir bileşene isteğe bağlı
özellik eklenmesi, bir değer kümesinin genişletilmesi, belge örneklerinin yazılması, ürüne
özgü bileşenlerin tasarımı. Önceki derste kurulan sınıflandırma burada doğrudan bir
yetkilendirme kuralına dönüşür: **ekleme sınıfı katkılar dağıtılır, kırıcı sınıfı katkılar
merkezde kalır.**

Bu ayrımın işlemesi için üçüncü bir kayıt gerekir: **sahiplik**. Her bileşenin adı yazılı
bir sahibi olmalıdır; sahibi olmayan bileşen, hiçbir modelde incelenmez. Sahiplik kaydı
kataloğun parçasıdır ve ilk derste tanımlanan olgunluk düzeyiyle birlikte tutulur.

#### Özet

- Yönetişim modeli tutarlılık ile hız arasında bir seçimdir; üç düzen — merkezî, federatif,
  karma — kapasiteyle inceleme görüşü değişkenleriyle tanımlanır. Merkezî modelin kapasitesi
  kuruluşla birlikte büyümez, federatifin kapasitesi büyür ama inceleme görüşü düşer.
- Merkezî modelin maliyeti beklemenin kendisi değil, beklemenin yerel kopyaya
  dönüşmesidir; sistem incelediği şeyde iyi kalır, ama işlerin çoğunu hiç incelemez.
- Model seçimi ölçeğe bağlıdır: aynı üç model, dört ekipte merkezî lehine, on iki ekipte
  karma lehine sonuç verir. Kırılma noktası kapasite ile talebin oranından hesaplanır.
- Karma modelde çekirdeğin onay kapasitesi ürün ekiplerinin hazırlama hızına yetişmezse,
  model adı karma olan bir merkezî modele dönüşür.
- Karşılanmamış talep raporlanmadığında düşük sapma sayısı yanıltır; yayımlanan katkı ve
  bekleyen kuyruk birlikte raporlanır.
- Kararlar tek tek dağıtılır: birden çok bileşeni etkileyenler merkezde kalır, tek bileşen
  içinde kalanlar dağıtılır; her bileşenin yazılı bir sahibi olmalıdır.

#### Sonraki Adım

Bu dersin modelinde "yerel kopya" bir varsayımdı: kuyrukta bekleyen işin belirli bir
bölümünün sistemden ayrılacağı varsayıldı. Gerçek bir kuruluşta bu sayı varsayılmaz,
ölçülür. Sonraki ders benimseme ölçümünü kurar: depo taramasıyla sistem bileşeni
kullanımının yerel kopyalara oranını hesaplar, kapsam yüzdesini ekip bazında çıkarır,
sapma listesini üretir ve hangi yerel kopyanın kataloğa girmesi gereken bir bileşene
işaret ettiğini gösterir.

### Benimseme Ölçümü

Source: https://academia.sh/tr/kurslar/tasarim-sistemleri/benimseme-olcumu

Depo taramasıyla kapsam yüzdesinin ekip bazında hesaplanması, trafikle ağırlıklı kapsamla farkı, sapma listesinin yerel kopya ve katalog adayı olarak sınıflandırılması ve sezgisel eşlemenin sınırları.

Önceki dersin modelinde "yerel kopya" bir varsayımdı: kuyrukta bekleyen işin belirli bir
bölümünün sistemden ayrılacağı varsayıldı. Gerçek bir kuruluşta bu sayı varsayılmaz,
ölçülür.

Benimseme ölçümünün amacı bir başarı raporu üretmek değildir. Sistemin nerede
kullanılmadığını bulmak, sistemin ne eksiği olduğunu söyler; her yerel kopya, ya
kataloğun bir boşluğuna ya bir bulunabilirlik sorununa ya da bir katkı yolunun
tıkanıklığına işaret eder.

#### Neyin Ölçüleceği

Benimseme için sorulabilecek en zayıf soru "ekipler sistemi kullanıyor mu" sorusudur;
yanıtı her zaman "evet"tir. Ölçülebilir soru şudur: **arayüzdeki bileşen çağrılarının
yüzde kaçı sistemden geliyor?**

Bu oran kapsam yüzdesidir ve iki farklı yolla ölçülebilir:

- **Durağan tarama.** Kaynak metindeki içe aktarımlar ve bileşen çağrıları sayılır. Her
  çağrı yeri bir birim sayılır; nadiren açılan bir yönetim ekranındaki çağrı, ana sayfadaki
  çağrıyla eşit ağırlıktadır.
- **Çalışma zamanı telemetrisi.** Gerçekten oluşturulan bileşen örnekleri sayılır ve
  ekranın görüntülenme sayısıyla ağırlıklanır.

İkisi farklı sorulara yanıt verir ve ikisi de gereklidir. Durağan tarama bakım yükünü
ölçer: kaç yerde kod değiştirileceğini söyler. Trafikle ağırlıklı ölçüm kullanıcı
etkisini ölçer: sistemin görünen arayüzün ne kadarını ürettiğini söyler.

#### Taramanın Yapılması

Aşağıdaki program üç ürün ekibinin yedi dosyasını tarar. Her dosyanın içe aktarımlarını
çözümler, bileşen çağrılarını sayar, her çağrıyı sistem paketinden mi yoksa yerel bir
dosyadan mı geldiğine göre sınıflandırır ve iki kapsam yüzdesini birlikte hesaplar.
Ardından yerel bileşenleri kataloğa göre sınıflandırır.

```js
// benimseme.mjs — depo taramasiyla kapsam yuzdesi, sapma listesi ve katalog adaylari

// Kaynak dosyalar. Yol, ilk bolumuyle ekibi verir. Sayi, o ekranin donem basina
// goruntulenme sayisidir; calisma zamani agirligi buradan gelir. Gorunum tanimlari
// oge(bilesen, ozellikler, ...cocuklar) bicimindedir.
const DEPO = [
  { yol: "urun-katalog/arama-sayfasi.js", goruntulenme: 48200, kaynak: `
import { AramaAlani, Kart, Rozet, Dugme, Sayfalama, BosDurum } from "@sistem/bilesenler";
import { FiltrePaneli } from "../bilesenler/filtre-paneli";
export const AramaSayfasi = () => oge("div", {},
  oge(AramaAlani), oge(FiltrePaneli),
  oge(Kart, {}, oge(Rozet), oge(Dugme)),
  oge(Kart, {}, oge(Rozet), oge(Dugme)),
  oge(BosDurum), oge(Sayfalama));` },
  { yol: "urun-katalog/kayit-sayfasi.js", goruntulenme: 31400, kaynak: `
import { Kart, Rozet, Dugme, KirintiYolu, Sekmeler } from "@sistem/bilesenler";
import { KayitKarti } from "../bilesenler/kayit-karti";
export const KayitSayfasi = () => oge("div", {},
  oge(KirintiYolu), oge(KayitKarti), oge(Sekmeler),
  oge(Kart, {}, oge(Rozet), oge(Dugme), oge(Dugme)));` },
  { yol: "urun-katalog/odunc-sayfasi.js", goruntulenme: 9800, kaynak: `
import { Dugme, BildirimSeridi, Iskelet } from "@sistem/bilesenler";
import { OzelDugme } from "../bilesenler/ozel-dugme";
import { YuklemeCubugu } from "../bilesenler/yukleme-cubugu";
export const OduncSayfasi = () => oge("div", {},
  oge(BildirimSeridi), oge(Iskelet),
  oge(OzelDugme), oge(OzelDugme), oge(Dugme), oge(YuklemeCubugu));` },
  { yol: "urun-uyelik/uye-girisi.js", goruntulenme: 22600, kaynak: `
import { MetinAlani, Dugme, Baglanti, BildirimSeridi } from "@sistem/bilesenler";
export const UyeGirisi = () => oge("form", {},
  oge(MetinAlani), oge(MetinAlani), oge(Dugme), oge(Baglanti), oge(BildirimSeridi));` },
  { yol: "urun-uyelik/uye-profili.js", goruntulenme: 7300, kaynak: `
import { Kart, MetinAlani, OnayKutusu, Dugme } from "@sistem/bilesenler";
import { DurumRozeti } from "../bilesenler/durum-rozeti";
import { OzelDugme } from "../bilesenler/ozel-dugme";
export const UyeProfili = () => oge("div", {},
  oge(Kart, {}, oge(DurumRozeti), oge(MetinAlani), oge(OnayKutusu)),
  oge(OzelDugme), oge(Dugme));` },
  { yol: "urun-yonetim/raf-yonetimi.js", goruntulenme: 1900, kaynak: `
import { Dugme, MetinAlani, KipPencere } from "@sistem/bilesenler";
import { VeriTablosu } from "../bilesenler/veri-tablosu";
import { FiltrePaneli } from "../bilesenler/filtre-paneli";
import { BasitAcilirListe } from "../bilesenler/basit-acilir-liste";
export const RafYonetimi = () => oge("div", {},
  oge(FiltrePaneli), oge(BasitAcilirListe), oge(VeriTablosu),
  oge(KipPencere, {}, oge(MetinAlani), oge(Dugme)));` },
  { yol: "urun-yonetim/kullanici-yonetimi.js", goruntulenme: 1200, kaynak: `
import { Dugme, MetinAlani } from "@sistem/bilesenler";
import { VeriTablosu } from "../bilesenler/veri-tablosu";
import { OzelKipPencere } from "../bilesenler/ozel-kip-pencere";
import { DurumRozeti } from "../bilesenler/durum-rozeti";
export const KullaniciYonetimi = () => oge("div", {},
  oge(VeriTablosu), oge(DurumRozeti), oge(DurumRozeti),
  oge(OzelKipPencere, {}, oge(MetinAlani), oge(Dugme)));` },
];

const SISTEM_PAKETI = "@sistem/bilesenler";
// Katalogdaki bilesenlerin kod tarafindaki adlari; yerel kopya saptamasi bunlara bakar.
const KATALOG = ["Dugme", "Baglanti", "MetinAlani", "OnayKutusu", "Rozet", "Kart", "Sayfalama",
  "AramaAlani", "BosDurum", "KipPencere", "Iskelet", "Sekmeler", "AcilirListe", "BildirimSeridi", "KirintiYolu"];

// Bir dosyadaki ice aktarimlari cozumle: ad -> kaynak.
function iceAktarimlar(kaynak) {
  const harita = new Map();
  for (const m of kaynak.matchAll(/import\s*\{([^}]+)\}\s*from\s*"([^"]+)"/g)) {
    for (const ad of m[1].split(",").map((s) => s.trim()).filter(Boolean)) harita.set(ad, m[2]);
  }
  return harita;
}
// Kullanim sayimi: oge() cagrisinin ilk argumani buyuk harfle basliyorsa bilesendir.
function kullanimlar(kaynak) {
  const sayim = new Map();
  for (const m of kaynak.matchAll(/\boge\(\s*([A-Z][A-Za-z0-9]*)/g)) sayim.set(m[1], (sayim.get(m[1]) ?? 0) + 1);
  return sayim;
}

const ekipler = new Map();
const yerelToplam = new Map();
for (const dosya of DEPO) {
  const ekip = dosya.yol.split("/")[0];
  const ith = iceAktarimlar(dosya.kaynak);
  const kul = kullanimlar(dosya.kaynak);
  const e = ekipler.get(ekip) ?? { sistem: 0, yerel: 0, agirlikliSistem: 0, agirlikliYerel: 0 };
  for (const [ad, adet] of kul) {
    const kaynak = ith.get(ad);
    if (!kaynak) continue;
    const sistemMi = kaynak === SISTEM_PAKETI;
    e[sistemMi ? "sistem" : "yerel"] += adet;
    e[sistemMi ? "agirlikliSistem" : "agirlikliYerel"] += adet * dosya.goruntulenme;
    if (!sistemMi) {
      const y = yerelToplam.get(ad) ?? { adet: 0, ekipler: new Set() };
      y.adet += adet;
      y.ekipler.add(ekip);
      yerelToplam.set(ad, y);
    }
  }
  ekipler.set(ekip, e);
}

console.log("ekip            sistem   yerel   kapsam   trafikle ağırlıklı kapsam");
let toplamSistem = 0, toplamYerel = 0, agSistem = 0, agYerel = 0;
for (const [ekip, e] of ekipler) {
  toplamSistem += e.sistem; toplamYerel += e.yerel;
  agSistem += e.agirlikliSistem; agYerel += e.agirlikliYerel;
  const kapsam = (e.sistem / (e.sistem + e.yerel)) * 100;
  const agirlikli = (e.agirlikliSistem / (e.agirlikliSistem + e.agirlikliYerel)) * 100;
  console.log(
    `${ekip.padEnd(15)} ${String(e.sistem).padStart(6)} ${String(e.yerel).padStart(7)} ` +
    `${(kapsam.toFixed(1) + "%").padStart(8)} ${(agirlikli.toFixed(1) + "%").padStart(26)}`
  );
}
console.log(
  `${"TOPLAM".padEnd(15)} ${String(toplamSistem).padStart(6)} ${String(toplamYerel).padStart(7)} ` +
  `${(((toplamSistem / (toplamSistem + toplamYerel)) * 100).toFixed(1) + "%").padStart(8)} ` +
  `${(((agSistem / (agSistem + agYerel)) * 100).toFixed(1) + "%").padStart(25)}`
);

// Sapma listesi: yerel bilesenin katalogda karsiligi var mi?
const karsilikBul = (yerelAd) => KATALOG.find((k) => yerelAd.includes(k)) ?? null;
console.log("\nsapma listesi");
console.log("yerel bileşen        kullanım   ekip   katalog karşılığı   sınıf");
const adaylar = [];
for (const [ad, y] of [...yerelToplam].sort((a, b) => b[1].adet - a[1].adet)) {
  const karsilik = karsilikBul(ad);
  const sinif = karsilik ? "yerel kopya" : (y.ekipler.size >= 2 ? "katalog adayı" : "ürüne özgü");
  if (sinif === "katalog adayı") adaylar.push(ad);
  console.log(
    `${ad.padEnd(20)} ${String(y.adet).padStart(9)} ${String(y.ekipler.size).padStart(6)}   ` +
    `${(karsilik ?? "-").padEnd(19)} ${sinif}`
  );
}

const kopyaAdedi = [...yerelToplam].filter(([ad]) => karsilikBul(ad)).reduce((t, [, y]) => t + y.adet, 0);
console.log(`\nyerel kopya kullanımı        : ${kopyaAdedi} (kataloğa geçirilebilir)`);
console.log(`katalog adayı yerel bileşenler: ${JSON.stringify(adaylar)}`);
console.log(`kopyalar kataloğa geçseydi kapsam: %${(((toplamSistem + kopyaAdedi) / (toplamSistem + toplamYerel)) * 100).toFixed(1)}`);
```

```
ekip            sistem   yerel   kapsam   trafikle ağırlıklı kapsam
urun-katalog        18       5    78.3%                      85.7%
urun-uyelik          9       2    81.8%                      90.7%
urun-yonetim         5       7    41.7%                      43.5%
TOPLAM              32      14    69.6%                     85.7%

sapma listesi
yerel bileşen        kullanım   ekip   katalog karşılığı   sınıf
OzelDugme                    3      2   Dugme               yerel kopya
DurumRozeti                  3      2   Rozet               yerel kopya
FiltrePaneli                 2      2   -                   katalog adayı
VeriTablosu                  2      1   -                   ürüne özgü
KayitKarti                   1      1   Kart                yerel kopya
YuklemeCubugu                1      1   -                   ürüne özgü
BasitAcilirListe             1      1   AcilirListe         yerel kopya
OzelKipPencere               1      1   KipPencere          yerel kopya

yerel kopya kullanımı        : 9 (kataloğa geçirilebilir)
katalog adayı yerel bileşenler: ["FiltrePaneli"]
kopyalar kataloğa geçseydi kapsam: %89.1
```

#### İki Kapsam Sayısının Ayrımı

Toplam satırında iki sayı arasında 16,1 puanlık fark var: durağan kapsam yüzde 69,6,
trafikle ağırlıklı kapsam yüzde 85,7. Fark tek bir ekipten geliyor. `urun-yonetim` ekibinin
kapsamı yüzde 41,7 ile listedeki en düşük değer, ama ekranlarının görüntülenme sayısı
diğerlerinin yanında küçük kalıyor; ağırlıklı hesapta neredeyse görünmüyor.

İki sayının hangisinin kullanılacağı, sorulan soruya bağlıdır. Kullanıcının gördüğü
arayüzün ne kadarının sistemden geldiği sorulduğunda ağırlıklı sayı doğrudur. Sistemin
bakım yükü ve kırıcı bir değişikliğin kaç yeri etkileyeceği sorulduğunda durağan sayı
doğrudur; sürümleme dersindeki göç maliyeti hesabı da bu ikinci sayıya dayanır.

Yalnızca ağırlıklı sayının raporlanması, düşük trafikli ürünlerin sistemden tamamen
kopmasını gizler. `urun-yonetim` ekibi bu tabloda yüzde 41,7'de duruyor ve durumu
düzelmezse sistem bu üründe hiç kullanılmayacak. Ağırlıklı sayıya bakan bir yönetişim
kararı bu ekibi hiç görmez.

#### Sapma Listesinin Sınıflandırılması

Sapma listesi yerel bileşenleri üç sınıfa ayırıyor ve her sınıfın ayrı bir işi var.

**Yerel kopya** — katalogda karşılığı olan bir bileşen. `OzelDugme` ile `DurumRozeti` iki
ayrı ekipte kullanılıyor; ikisi de sistemde zaten var olan bir bileşenin yerine yazılmış.
Bu satırlar bir soru üretir: kopya neden yazıldı? Yanıt genellikle üç şeyden biridir —
sistem bileşeni istenen bir özelliği vermiyordu, kaçış deliği yetersizdi, ya da katkı yolu
önceki derste görülen kuyruğa takılmıştı. Yanıt bulunmadan kopya kaldırılırsa aynı kopya
başka adla geri döner.

**Katalog adayı** — karşılığı olmayan ama iki ayrı ekipte kullanılan bileşen.
`FiltrePaneli` bu sınıfta görünüyor. İlk dersin kapsam ölçütü tam olarak bu durumu arıyordu:
birden çok ekipte yinelenen bir parça, kataloğa girmeye adaydır.

**Ürüne özgü** — karşılığı olmayan ve tek ekipte kullanılan bileşen. `VeriTablosu` ve
`YuklemeCubugu` bu sınıfta. Bunlar sapma değildir; ürünün kendi parçalarıdır ve kataloğa
alınmaları yalnızca inceleme yükü ekler.

Son satır, sapmanın kapatılmasının kapsama etkisini veriyor: yerel kopyaların dokuzu
sisteme geçirilseydi kapsam yüzde 69,6'dan yüzde 89,1'e çıkardı. Bu sayı bir hedef değil,
bir tavan ölçüsüdür: kopyaları kapatmanın en iyi durumdaki getirisini gösterir ve bu işin
diğer işlerle karşılaştırılmasını sağlar.

#### Sezgisel Eşlemenin Sınırı

Rapor bir programın ürettiği listedir ve programın kullandığı eşleme kuralı sezgiseldir:
yerel adın içinde bir katalog adı geçiyorsa kopya sayılır. Bu kural iki yerde yanılıyor ve
iki yanılma da öğreticidir.

`KayitKarti` satırı `Kart` bileşeninin kopyası olarak işaretlenmiş. Oysa ilk dersteki
`kayit-satiri` gibi kayıt kartı da, bibliyografik bir kaydın hangi alanlarını hangi sırayla
gösterdiğine karar veren, ürüne özgü bir parçadır. Adının içinde "Kart" geçmesi, kart bileşeninin
kopyası olduğunu göstermez; onu **kullanıyor** olması daha olasıdır. Sezgisel eşlemenin ürettiği
liste bir sonuç değil, bir inceleme kuyruğudur.

`FiltrePaneli` satırındaki yanılma daha ilginçtir. Program bunu katalog adayı olarak
işaretliyor, çünkü katalogda "FiltrePaneli" adında bir şey yok. Oysa ilk dersteki katalog
tablosunda `suzgec-paneli` diye bir kayıt var — üstelik tek ekipte ve dört yerde
kullanıldığı için kapsam ölçütünü karşılamıyordu. İki tablo yan yana konduğunda gerçek
sorun görünür hâle gelir: katalogda kimsenin kullanmadığı bir süzgeç paneli duruyor, iki
ekip ise kendi filtre panelini yazmış.

Bu bir kapsam sorunu değil, **bulunabilirlik** sorunudur ve kaynağı adlandırma dersinde
tanımlanan eş anlamlılıktır. Çözümü yeni bir bileşen eklemek değil, var olan bileşenin eş
anlamlı adlarını sözlüğe yazmak ve arama yüzeyini bu sözlükle beslemektir. Ölçüm bu
sonuca ancak iki raporun birlikte okunmasıyla varır; tek başına hiçbir sayı bunu söylemez.

#### Ölçümün Kendi Maliyeti

Benimseme ölçümü sürekli çalışan bir iştir ve iki tuzağı vardır.

Birincisi, kapsam yüzdesinin **hedef hâline getirilmesidir**. Ekiplerin başarısı bu sayıyla
ölçülürse, ürüne özgü bileşenler zorla kataloğa itilir ve katalog ilk dersteki kapsam
ölçütünü kaybeder. Kapsam yüzdesi bir tanı aracıdır; hedef, sapmanın nedenlerinin
kapatılmasıdır.

İkincisi, taramanın **eksik saymasıdır**. Yukarıdaki program içe aktarım ve çağrı sayar;
bir bileşenin sistem bileşenini sarmalayarak yeniden yayımlaması ya da bir ekranın sistem
bileşenini kullanıp biçemini tümüyle ezmesi bu sayıma girmez. İkisi de sapmadır ve ayrı
denetimler ister: sarmalayan bileşenlerin listesi ve sistem bileşenlerine dışarıdan yazılan
biçem kurallarının sayımı.

#### Özet

- Benimseme, "sistem kullanılıyor mu" sorusuyla değil, bileşen çağrılarının yüzde kaçının
  sistemden geldiği sorusuyla ölçülür.
- Durağan tarama bakım yükünü, trafikle ağırlıklı ölçüm kullanıcı etkisini verir; yalnız
  ağırlıklı sayı raporlandığında düşük trafikli ürünlerin sistemden kopması gizlenir.
- Sapma listesi üç sınıfa ayrılır: yerel kopya kapatılacak bir boşluğa, katalog adayı yeni
  bir bileşene, ürüne özgü olan ise hiçbir işe işaret eder. Yerel kopyanın neden yazıldığı
  bulunmadan kopya kaldırılırsa aynı kopya başka adla geri döner.
- Ad benzerliğine dayanan eşleme sezgiseldir ve yanılır; ürettiği liste bir sonuç değil
  inceleme kuyruğudur.
- Katalogda kullanılmayan bir bileşenle ekiplerin yazdığı yerel karşılığı yan yana
  görüldüğünde sorun kapsam değil bulunabilirliktir; çözümü eş anlamlı adların
  kaydedilmesidir.
- Kapsam yüzdesi hedefe dönüştürülürse katalog kapsam ölçütünü kaybeder; ölçüm bir tanı
  aracıdır.

#### Sonraki Adım

Bu dersin taraması kod tarafında kaldı: sistem bileşeninin kullanılıp kullanılmadığına
bakıldı, ama o bileşenin taşıdığı değerlerin tasarım kitaplığındakiyle aynı olup olmadığına
bakılmadı. Bir ekip sistem düğmesini kullanıyor olabilir ve düğmenin dolgu rengi tasarım
dosyasındakinden farklı olabilir; kapsam yüzdesi bunu yüzde yüz olarak sayar. Sonraki ders
araç zincirini kurar: tasarım aracından çıkan belirteç dışa aktarımıyla koddaki belirteç
tanımını karşılaştıran bir fark raporu üretir, adları ve renk biçimlerini normalleştirerek
sahte farkları eler ve kalan gerçek sapmayı sınıflandırır.

### Araç Zinciri

Source: https://academia.sh/tr/kurslar/tasarim-sistemleri/arac-zinciri

Tasarım aracıyla kod arasında tek yönlü doğruluk kaynağı, belirteç dışa aktarımının normalleştirilmesi, sahte farkların elenmesi ve kalan gerçek sapmanın sınıflandırılarak eşitleme oranına bağlanması.

Önceki dersin taraması kod tarafında kaldı: sistem bileşeninin kullanılıp kullanılmadığına
bakıldı, ama o bileşenin taşıdığı değerlerin tasarım kitaplığındakiyle aynı olup olmadığına
bakılmadı. Bir ekip sistem düğmesini kullanıyor olabilir ve düğmenin dolgu rengi tasarım
dosyasındakinden farklı olabilir; kapsam yüzdesi bunu yüzde yüz olarak sayar.

Araç zinciri bu boşluğu kapatır. İşi, tasarım aracındaki değerlerle koddaki değerlerin
aynı olduğunu **sürekli olarak doğrulamaktır**. Bu ders zincirin yönünü tanımlar, fark
raporunu üretir ve raporun okunabilir hâle gelmesi için gereken normalleştirmeyi gösterir.

#### Doğruluğun Yönü

Bir değer iki yerde tutuluyorsa, hangisinin doğru olduğu önceden kararlaştırılmalıdır. Üç
düzen vardır ve üçü de tek yönlüdür:

- **Tasarım aracı kaynaktır.** Değerler tasarım kitaplığında tanımlanır, dışa aktarılır ve
  koda üretilir. Kodda elle değişiklik yapılmaz.
- **Kod kaynaktır.** Değerler bir belirteç dosyasında tanımlanır ve tasarım aracına
  aktarılır. Tasarımcı değeri araçta değil dosyada değiştirir.
- **Nötr bir kaynak vardır.** Değerler araçtan da koddan da bağımsız bir dosyada — bir JSON
  belgesinde — tanımlanır; hem tasarım aracı hem kod bu dosyadan beslenir.

Dördüncü bir seçenek olarak görünen **iki yönlü eşitleme** çalışmaz. İki taraf da yazma
yetkisine sahipse, aynı belirtecin iki tarafta birden değişmesi bir çakışma üretir ve
çakışmayı çözecek bir kural yoktur: hangi değerin daha yeni olduğu, hangisinin daha doğru
olduğunu söylemez. Bu yüzden araç zinciri kurulurken verilen ilk karar, hangi yönde
akacağıdır; teknik seçimler bu karardan sonra gelir.

Yön seçildikten sonra zincirin biçimi aynıdır: **dışa aktarım → dönüştürme → hedef
biçimler**. Dönüştürme adımı adları hedefin sözleşmesine çevirir, değerleri hedefin
birimlerine getirir ve türetilmiş belirteçleri hesaplar. Hedef biçimler birden çok olabilir
— aynı kaynaktan CSS özel özellikleri, mobil biçim dosyaları ve belge sayfaları üretilir.

#### Fark Raporunun Üretilmesi

Yön ne olursa olsun, iki tarafın aynı olduğunun denetlenmesi gerekir. Denetimin önündeki
asıl engel, iki tarafın **aynı değeri farklı biçimde yazmasıdır**: tasarım aracı rengi
büyük harfli onaltılık yazar, kod küçük harfli; tasarım aracı uzunluğu piksel yazar, kod
kök boyutuna göre oran yazar.

Aşağıdaki program on sekiz tasarım belirteci ile on sekiz kod belirtecini üç aşamada
karşılaştırır: ham hâlde, yalnız adlar normalleştirilerek ve hem adlar hem değerler
normalleştirilerek.

```js
// eslestirme.mjs — tasarim aracindan cikan belirtec disa aktarimi ile koddaki
// belirtec tanimi arasinda fark raporu

// Tasarim aracinin disa aktardigi ad-deger cifleri.
const TASARIM = {
  "Renk/Yüzey/Birincil": "#FFFFFF",
  "Renk/Yüzey/İkincil": "#F7F7F8",
  "Renk/Kenarlık/Varsayılan": "#B7BCC2",
  "Renk/Metin/Birincil": "rgb(31, 35, 40)",
  "Renk/Metin/İkincil": "#5A6169",
  "Renk/Eylem/Ana": "#275EA5",
  "Renk/Eylem/Ana Üzeri": "#FFF",
  "Renk/Eylem/İkincil": "#3B7BC8",
  "Aralık/Adım 1": "4px",
  "Aralık/Adım 2": "8px",
  "Aralık/Adım 3": "12px",
  "Aralık/Adım 4": "16px",
  "Tipografi/Gövde/Boyut": "16px",
  "Tipografi/Gövde/Satır Yüksekliği": "1.5",
  "Tipografi/Başlık/Boyut": "24px",
  "Yuvarlaklık/Küçük": "4px",
  "Yuvarlaklık/Orta": "8px",
  "Gölge/Yükselti 1": "0 1px 2px rgba(0, 0, 0, 0.08)",
};

// Kodda tanimli ozel ozellikler.
const KOD = {
  "--renk-yuzey-birincil": "#ffffff",
  "--renk-yuzey-ikincil": "#f7f7f8",
  "--renk-kenarlik-varsayilan": "#b7bcc2",
  "--renk-metin-birincil": "#1f2328",
  "--renk-metin-ikincil": "#5a6169",
  "--renk-eylem-ana": "#275ea5",
  "--renk-eylem-ana-uzeri": "#ffffff",
  "--renk-eylem-ikincil": "#2f6bb5",
  "--aralik-adim-1": "0.25rem",
  "--aralik-adim-2": "0.5rem",
  "--aralik-adim-3": "0.75rem",
  "--aralik-adim-4": "1rem",
  "--tipografi-govde-boyut": "1rem",
  "--tipografi-govde-satir-yuksekligi": "1.5",
  "--tipografi-baslik-boyut": "1.5rem",
  "--yuvarlaklik-kucuk": "0.25rem",
  "--yuvarlaklik-orta": "0.5rem",
  "--renk-eylem-tehlike": "#a12d2d",
};

// Ad normalizasyonu: 02. dersteki cevriyazim kuralinin aynisi.
const CEVRIM = { "ı": "i", "İ": "i", "ş": "s", "Ş": "s", "ç": "c", "Ç": "c", "ğ": "g", "Ğ": "g", "ü": "u", "Ü": "u", "ö": "o", "Ö": "o" };
const cevir = (s) => [...s].map((h) => CEVRIM[h] ?? h).join("");
const adNormal = (tasarimAdi) => "--" + cevir(tasarimAdi).toLowerCase().replace(/[/\s]+/g, "-");

// Deger normalizasyonu: renk -> alti haneli kucuk harfli onaltilik, uzunluk -> piksel.
const KOK_BOYUT = 16;
function degerNormal(deger) {
  const d = deger.trim().replace(/\s+/g, " ");
  const kisaOnaltilik = d.match(/^#([0-9a-fA-F]{3})$/);
  if (kisaOnaltilik) return "#" + [...kisaOnaltilik[1]].map((h) => h + h).join("").toLowerCase();
  if (/^#[0-9a-fA-F]{6}$/.test(d)) return d.toLowerCase();
  const rgb = d.match(/^rgb\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)$/);
  if (rgb) return "#" + rgb.slice(1, 4).map((n) => Number(n).toString(16).padStart(2, "0")).join("");
  const rem = d.match(/^(-?[\d.]+)rem$/);
  if (rem) return `${Number(rem[1]) * KOK_BOYUT}px`;
  const px = d.match(/^(-?[\d.]+)px$/);
  if (px) return `${Number(px[1])}px`;
  return d;
}

// Uc asamali karsilastirma: ham, yalniz ad normalizasyonu, tam normalizasyon.
function karsilastir(adDonusum, degerDonusum) {
  const sol = new Map(Object.entries(TASARIM).map(([a, d]) => [adDonusum(a), degerDonusum(d)]));
  const sag = new Map(Object.entries(KOD).map(([a, d]) => [a, degerDonusum(d)]));
  const yalnizTasarim = [...sol.keys()].filter((a) => !sag.has(a));
  const yalnizKod = [...sag.keys()].filter((a) => !sol.has(a));
  const degerFarki = [...sol.entries()].filter(([a, d]) => sag.has(a) && sag.get(a) !== d).map(([a]) => a);
  return { yalnizTasarim, yalnizKod, degerFarki, ortak: sol.size - yalnizTasarim.length };
}

const asamalar = [
  ["ham karşılaştırma", (a) => a, (d) => d],
  ["ad normalizasyonu", adNormal, (d) => d],
  ["ad + değer normalizasyonu", adNormal, degerNormal],
];

console.log("aşama                       ortak ad   yalnız tasarımda   yalnız kodda   değer farkı   toplam fark");
let son = null;
for (const [ad, aD, dD] of asamalar) {
  const s = karsilastir(aD, dD);
  son = s;
  const toplam = s.yalnizTasarim.length + s.yalnizKod.length + s.degerFarki.length;
  console.log(
    `${ad.padEnd(27)} ${String(s.ortak).padStart(8)} ${String(s.yalnizTasarim.length).padStart(18)} ` +
    `${String(s.yalnizKod.length).padStart(14)} ${String(s.degerFarki.length).padStart(13)} ${String(toplam).padStart(13)}`
  );
}

console.log("\n— GERÇEK SAPMALAR —");
for (const a of son.degerFarki) {
  const tasarimAnahtar = Object.keys(TASARIM).find((k) => adNormal(k) === a);
  console.log(`değer farkı: ${a}\n    tasarım: ${TASARIM[tasarimAnahtar]} → ${degerNormal(TASARIM[tasarimAnahtar])}\n    kod    : ${KOD[a]} → ${degerNormal(KOD[a])}`);
}
for (const a of son.yalnizTasarim) console.log(`yalnız tasarımda: ${a} (${TASARIM[Object.keys(TASARIM).find((k) => adNormal(k) === a)]})`);
for (const a of son.yalnizKod) console.log(`yalnız kodda: ${a} (${KOD[a]})`);

// Eslesme orani ve kapi: hangi esikte yayin durdurulur?
const toplamBelirtec = new Set([...Object.keys(TASARIM).map(adNormal), ...Object.keys(KOD)]).size;
const gercekFark = son.yalnizTasarim.length + son.yalnizKod.length + son.degerFarki.length;
console.log(`\ntoplam belirteç (birleşim): ${toplamBelirtec}`);
console.log(`gerçek sapma: ${gercekFark}   eşitleme oranı: %${(((toplamBelirtec - gercekFark) / toplamBelirtec) * 100).toFixed(1)}`);
```

```
aşama                       ortak ad   yalnız tasarımda   yalnız kodda   değer farkı   toplam fark
ham karşılaştırma                  0                 18             18             0            36
ad normalizasyonu                 17                  1              1            16            18
ad + değer normalizasyonu         17                  1              1             1             3

— GERÇEK SAPMALAR —
değer farkı: --renk-eylem-ikincil
    tasarım: #3B7BC8 → #3b7bc8
    kod    : #2f6bb5 → #2f6bb5
yalnız tasarımda: --golge-yukselti-1 (0 1px 2px rgba(0, 0, 0, 0.08))
yalnız kodda: --renk-eylem-tehlike (#a12d2d)

toplam belirteç (birleşim): 19
gerçek sapma: 3   eşitleme oranı: %84.2
```

#### Sahte Farkın Elenmesi

Üç aşamalı tablo, fark raporunun neden çoğu kuruluşta okunmadığını açıklıyor. Ham karşılaştırmada
36 fark var ve bunların **çoğu gerçek değil**; iki taraf yalnızca farklı yazım sözleşmeleri
kullanıyor. Böyle bir rapor bir kez okunur, sonra kapatılır.

Ad normalizasyonu farkı 18'e indiriyor. Kullanılan kural, ikinci derste kurulan çevriyazım
kuralının aynısıdır; adlandırma sözleşmesinin bedeli orada ödenmişti, karşılığı burada
alınıyor. Sözleşme olmasaydı bu adım için ayrı bir eşleme tablosu tutulması gerekirdi ve
tablo, belirteç eklendikçe eskirdi.

Değer normalizasyonu farkı 3'e indiriyor. Elenen 15 fark dört ayrı biçim ayrımından doğuyordu:
onaltılık gösterimin harf büyüklüğü, üç haneli kısa gösterim, `rgb` işlevsel gösterimi ve uzunluk
biriminin piksel yerine kök oranı olarak yazılması. Bunların hiçbiri görsel bir sapma üretmez;
ikisi de aynı rengi ve aynı uzunluğu anlatır.

Sıralamanın kendisi bir tasarım kararıdır: normalizasyon **karşılaştırmadan önce** yapılır,
sonra değil. Farkı önce hesaplayıp sonra elemeye çalışmak, hangi farkın sahte olduğunu her
seferinde yeniden karar vermek demektir.

#### Kalan Üç Sapmanın Okunması

Geriye kalan üç satırın her biri farklı bir iş üretir.

**Değer farkı** — `--renk-eylem-ikincil` iki tarafta farklı. Bu, doğruluk kaynağının
ihlalidir: taraflardan biri diğerini beklemeden değiştirilmiş. Kaynağın yönü belirlenmiş
olsa bile bu satır tek başına hangi tarafın haklı olduğunu söylemez; söylediği şey, akışın
bir yerde kırıldığıdır. Bu rengin ikincil eylem rengi olduğu ve tipografi ve renk
derslerinde birincil eylem rengiyle arasındaki kontrast oranının hesaplandığı düşünülürse,
sapmanın sonucu görsel değil işlevsel olabilir.

**Yalnız tasarımda** — `--golge-yukselti-1` tasarım kitaplığında var, kodda yok.
Gerçekleştirilmemiş bir tasarım kararıdır ve katkı sürecine bir iş olarak girer. Bu satırın
raporda kalması, kararın unutulmasını önler.

**Yalnız kodda** — `--renk-eylem-tehlike` kodda var, tasarımda yok. Bir ekip yıkıcı bir
eylem için renk gerektiğinde belirteci kod tarafında eklemiş. Bu satır iki sonuçtan birine
bağlanır: ya belirteç meşrudur ve tasarım kitaplığına eklenir, ya da değildir ve
kaldırılır. Kararsız bırakıldığında belirteç kalır, kullanılır ve bir sonraki tema
türetiminde karşılığı bulunamaz.

Eşitleme oranı — yüzde 84,2 — bu üç satırın tek sayıya indirilmiş hâlidir ve ikinci derste
ad eşleşmesi için kurulan kapı burada da kurulabilir: oran bir alt sınırın altına
düştüğünde yayın durdurulur. Sayının kendisinden çok, **her yayında ölçülüyor olması**
önemlidir; sapma bir olay değil, biriken bir niceliktir.

#### Zincirin Kapsayamadığı Katman

Araç zincirinin yapabildiği ile yapamadığı arasındaki sınır nettir ve bu ders boyunca
kurulan yapının neden yalnız araçla kurulamayacağını açıklar.

Zincir **değerleri** eşitler: renk, aralık, tipografi, yuvarlaklık, gölge. Bunlar ad-değer
çiftleridir, makinede karşılaştırılabilir ve otomatik üretilebilir.

Zincir **davranışı** eşitleyemez. Bir düğmenin hangi durumda hangi rengi aldığı, bir kip
pencerenin odağı nereye götürdüğü, bir açılır listenin klavyeyle nasıl gezildiği tasarım
aracından dışa aktarılamaz. Bu katman, ikinci derste kurulan adlandırma sözleşmesiyle,
üçüncü derste tanımlanan belge bölümleriyle ve beşinci derste kurulan inceleme kapılarıyla
eşitlenir. Araç zinciri belirteç katmanının bekçisidir; bileşen katmanının bekçisi
yönetişimdir.

#### Özet

- Bir değer iki yerde tutuluyorsa doğruluk kaynağının yönü önceden kararlaştırılmalıdır;
  iki yönlü eşitleme, çözülemeyen çakışmalar ürettiği için bir seçenek değildir. Zincirin
  biçimi yönden bağımsızdır: dışa aktarım, dönüştürme ve birden çok hedef biçim.
- Fark raporu normalleştirme olmadan okunmaz hâle gelir; örnekte 36 farkın 33'ü yalnızca
  yazım sözleşmesi ayrımından doğan sahte farktı.
- Normalizasyon karşılaştırmadan önce yapılır; ad kuralı adlandırma sözleşmesinden
  devralınır, değer kuralı renkleri tek gösterime ve uzunlukları tek birime indirir.
- Kalan sapmalar üç sınıftır: değer farkı akışın kırıldığını, yalnız tasarımda olan
  gerçekleştirilmemiş bir kararı, yalnız kodda olan kayıtsız eklenmiş bir belirteci gösterir.
- Eşitleme oranı her yayında ölçülür ve bir alt sınıra bağlanır; sapma bir olay değil,
  biriken bir niceliktir.
- Araç zinciri değerleri eşitler, davranışı eşitleyemez; bileşen katmanının eşitliği
  adlandırma sözleşmesi, belge bölümleri ve inceleme kapılarıyla sağlanır.

#### Kurs Kapanışı

Bu konu, tasarım sisteminin değerlerden sonra gelen katmanını kurdu. Kataloğa neyin gireceği bir
kapsam ölçütüne, giren şeyin ne kadar hazır olduğu olgunluk düzeylerinin çıkış koşullarına
bağlandı. Tasarımla kod arasındaki ortak dil bir türetim kuralı hâline getirildi ve eşleşmeyen
adlar sayıldı. Belgelendirme yedi zorunlu bölüme ayrıldı ve kapsamı kullanımla ağırlıklandırılarak
hesaplandı. Sürüm numarası bir tartışma konusu olmaktan çıkarılıp arayüz farkının sonucuna
dönüştürüldü; göç maliyeti çağrı yeriyle ölçüldü. Katkı akışı bir durum makinesi oldu, yönetişim
modelleri kuyruk ve sapma sayılarıyla karşılaştırıldı, benimseme depo taramasıyla ölçüldü ve son
olarak belirteç katmanının eşitliği bir fark raporuna bağlandı.

Bütün bu yapının ortak yanı, her kararın bir sayıya bağlanmış olmasıdır: kapsam yüzdesi,
eksik etkisi, göç maliyeti, kuyruk süresi, sapma sayısı, eşitleme oranı. Bir tasarım
sisteminin başarısı bu sayılarla izlenir; izlenmediğinde sistem, kimsenin ne zaman
bıraktığını fark etmediği bir kütüphaneye dönüşür.

Katalog kuruldu ve yönetiliyor. Ancak kataloğun her kaydı hâlâ bir soruyu yanıtsız
bırakıyor: bu bileşen klavyeyle nasıl kullanılır, odağı nereye götürür, ekran okuyucuya
kendini nasıl bildirir? Üçüncü derste "erişilebilirlik notu" bir belge bölümü olarak
tanımlandı ve kararlı olgunluk düzeyinin çıkış ölçütlerinden biri sayıldı, ama o notun
içine ne yazılacağı söylenmedi. Bir sonraki kurs olan **Erişilebilir Bileşen Kalıpları**
bu boşluğu doldurur: düğmeden açılır listeye, sekmelerden kip pencereye kadar kataloğun her
bileşeninin klavye davranışını, odak yönetimini ve ekran okuyucuya bildirdiği bilgiyi tek
tek belirtir; karmaşık etkileşim kalıplarının odak tuzaklarını tasarlar ve arayüz metniyle
yerelleştirme gereksinimlerini sistemin parçası hâline getirir.

