---
title: 'CSS ile Görsel Sunum'
source: 'https://academia.sh/tr/kurslar/css-temelleri'
language: tr
updated: '2026-08-17T04:40:51+00:00'
license: 'CC BY-SA 4.0'
lessons: 26
outcomes:
    - 'Seçicileri ve özgüllük kurallarını doğru kullanır'
    - 'Kutu modelini ve akış düzenini açıklar'
    - 'Renk, tipografi ve aralık ölçeklerini tutarlı uygular'
---

# CSS ile Görsel Sunum

Bir belgeyi biçimlendiren kuralların nasıl seçildiği, çakışmaların nasıl çözüldüğü, kutuların nasıl ölçüldüğü ve renk, tipografi, aralık ölçeklerinin nasıl tutarlı kurulduğu.

## CSS'in Çalışma Biçimi

Bir bildirimin sözdiziminden başlayarak seçicilerin belgeyi nasıl eşlediği, çakışan kuralların basamaklama ve özgüllükle nasıl çözüldüğü ve değerlerin ağaçta nasıl devralındığı.

### Kural, Bildirim ve Değer

Source: https://academia.sh/tr/kurslar/css-temelleri/kural-bildirim-ve-deger

Bir biçem dilinin en küçük birimleri; kuralın seçici ve bildirim bloğundan oluşması, özellik–değer çiftinin yapısı ve geçersiz bildirimin atılma kuralı.

Web'in Temelleri ve HTML kursu belgeyi bir düğüm ağacına dönüştürdü ve o ağacın
düğümlerine anlam yükledi: hangisi başlık, hangisi bölüm, hangisi çizelge hücresi. O
kursun ilk dersinde kurulan üçlü ayrımda bir soru yanıtsız bırakılmıştı — "nasıl
görünecek?" sorusu. Bu kurs onu yanıtlar.

Yanıt ayrı bir dille verilir. Bu dilin adı CSS'tir ve HTML gibi bildirimseldir: ne
yapılacağını değil, neyin nasıl olduğunu söyler. Bu ders o dilin en küçük birimlerini
tanımlar: kural, bildirim ve değer. Ayrımlar burada kurulmazsa sonraki derslerdeki
çakışma çözümü anlaşılmaz kalır, çünkü çakışma kurallar arasında değil, **bildirimler**
arasında çözülür.

#### Kural İki Parçadan Oluşur

Bir CSS kuralı iki parçalıdır: hangi ögelerin etkileneceğini söyleyen **seçici
(selector)** ve ne olacağını söyleyen **bildirim bloğu (declaration block)**.

```css
h1 {
  color: #143a52;
  margin-block-end: 0.5rem;
}
```

Burada `h1` seçicidir ve süslü parantezler arasındaki bölüm bildirim bloğudur. Seçici belgenin
ağacına bakar ve bir ögeler kümesi döndürür; bildirim bloğu o kümenin her üyesine
uygulanacak değişiklikleri sayar. İki parçanın birbirinden bağımsız olması önemlidir:
aynı bildirim bloğu başka bir seçiciyle, aynı seçici başka bir bloğa yazılabilir.

Bir seçici virgülle ayrılmış birkaç seçiciden oluşabilir. Bu, **seçici listesi (selector
list)** yazmaktır ve aynı bloğu her birine ayrı ayrı yazmakla eşdeğerdir:

```css
h1, h2 { color: #143a52; }
```

Bu yazım, `h1 { color: #143a52; }` ve `h2 { color: #143a52; }` kurallarını yazmakla aynı
sonucu verir. Eşdeğerlik tam değildir: seçici listesindeki bir üye geçersizse davranış
seçicinin türüne bağlıdır, bu ayrıntıya sözde sınıflar dersinde döneceğiz.

#### Bildirim: Özellik ve Değer

Bildirim bloğunun içindeki her satır bir **bildirim (declaration)**'dir ve iki parçalıdır:
iki nokta üst üstenin solunda **özellik (property)**, sağında **değer (value)**.
Bildirimler noktalı virgülle ayrılır.

```css
padding: 8px 12px;
```

Özellik adı sabit bir sözlükten gelir; uydurulamaz. Değerin hangi biçimleri kabul ettiği
her özellik için ayrı tanımlanmıştır: `color` bir renk bekler, `padding` bir ile dört
arasında uzunluk bekler, `text-align` sayılı anahtar sözcüklerden birini bekler.

Aşağıdaki program bir biçem metnini kurallara ve bildirimlere ayırır. Özellik adlarını
küçük bir sözlükle karşılaştırır ve sözlükte olmayanı işaretler.

```js
// bicem-cozumle.mjs — bir biçem metnini kural ve bildirimlerine ayırır
const bicem = `
/* istasyon sayfasi - ilk kurallar */
body { font-family: system-ui; line-height: 1.5 }
h1, h2 { color: #143a52; margin-block-end: 0.5rem; }
.olcum-cizelgesi td { padding: 8px 12px; text-align: right; renk: mavi }
`;

// yorumlari at, ardindan { } cifti ile kurallara bol
const temiz = bicem.replace(/\/\*[\s\S]*?\*\//g, "");
const kurallar = [];
for (const parca of temiz.split("}")) {
  const [secici, govde] = parca.split("{");
  if (govde === undefined) continue;
  const bildirimler = govde
    .split(";")
    .map((d) => d.trim())
    .filter((d) => d.length > 0)
    .map((d) => {
      const iki = d.indexOf(":");
      return { ozellik: d.slice(0, iki).trim(), deger: d.slice(iki + 1).trim() };
    });
  kurallar.push({ secici: secici.trim().replace(/\s+/g, " "), bildirimler });
}

const bilinen = new Set([
  "font-family", "line-height", "color", "margin-block-end", "padding", "text-align",
]);

for (const k of kurallar) {
  console.log(`secici  : ${k.secici}`);
  console.log(`bildirim: ${k.bildirimler.length}`);
  for (const b of k.bildirimler) {
    const durum = bilinen.has(b.ozellik) ? "gecerli" : "ATILIR";
    console.log(`  ${b.ozellik} = ${b.deger}   [${durum}]`);
  }
}
console.log(`toplam kural   : ${kurallar.length}`);
console.log(`toplam bildirim: ${kurallar.reduce((t, k) => t + k.bildirimler.length, 0)}`);
```

```
secici  : body
bildirim: 2
  font-family = system-ui   [gecerli]
  line-height = 1.5   [gecerli]
secici  : h1, h2
bildirim: 2
  color = #143a52   [gecerli]
  margin-block-end = 0.5rem   [gecerli]
secici  : .olcum-cizelgesi td
bildirim: 3
  padding = 8px 12px   [gecerli]
  text-align = right   [gecerli]
  renk = mavi   [ATILIR]
toplam kural   : 3
toplam bildirim: 7
```

Üç şey görünür oldu. Birincisi, blok içindeki **son** bildirimden sonraki noktalı virgül
zorunlu değildir; `body` ve son kuralda yazılmadığı hâlde bildirimler doğru ayrıldı. İkincisi,
yorumlar `/* … */` arasında yazılır ve ayrıştırma öncesinde atılır. Üçüncüsü, `renk`
diye bir özellik yoktur; bu bildirim atılır ve **yalnızca o bildirim** atılır — bloktaki
diğer iki bildirim uygulanır.

#### Geçersizlik Bildirim Düzeyinde Çözülür

Bu son nokta dilin tasarımına ilişkindir. HTML ayrıştırıcısının kusurlu belgeyi
reddetmediğini, tanımlı kurtarma kurallarıyla bir sonuç ürettiğini görmüştük. CSS aynı
ilkeyi izler ve kurtarmayı üç düzeyde tanımlar:

- Tanınmayan **özellik adı** varsa o bildirim atılır, blok yaşar.
- Özellik tanınıyor ama **değer** o özelliğin dilbilgisine uymuyorsa yine o bildirim
  atılır. `color: mavi` yazılırsa `color` özelliği önceki değerinde kalır.
- Seçici tanınmıyorsa **kuralın tamamı** atılır; bildirim bloğu hiçbir ögeye uygulanmaz.

Bu üç kuralın ortak sonucu, bir biçem dosyasının hiçbir zaman "derlenmemesi"dir. Anlaşılmayan
parça sessizce düşer, kalanı işler. Programlama Temelleri kursundaki sözdizim hatası
kavramı burada da işlemez: hata bir durma sebebi değil, bir atlama sebebidir.

Bu hoşgörünün bir bedeli vardır: yanlış yazılmış bir özellik adı hiçbir uyarı üretmeden yok
olur. Bir bildirimin işe yaramamasının iki ayrı sebebi olabilir — atılmış olması ya da
başka bir kural tarafından bastırılmış olması — ve ikisi dışarıdan aynı görünür. Bu ayrımı
basamaklama dersinde ölçülebilir hâle getireceğiz.

#### Kısayol Özellikler ve Açılımları

Bazı özellikler tek başına bir değer taşımaz; birden çok özelliği aynı anda ayarlar. Bunlara
**kısayol özellik (shorthand property)** denir; ayarladığı özelliklere de o kısayolun
**açılımı (longhand)** denir.

`padding: 8px 12px` bildirimi tek bir özelliğe değil, dördüne yazar. Değer sayısına göre
dağıtım kuralı şudur:

| Değer sayısı | Üst | Sağ | Alt | Sol |
|---|---|---|---|---|
| 1 → `8px` | 8px | 8px | 8px | 8px |
| 2 → `8px 12px` | 8px | 12px | 8px | 12px |
| 3 → `8px 12px 4px` | 8px | 12px | 4px | 12px |
| 4 → `8px 12px 4px 2px` | 8px | 12px | 4px | 2px |

Sıra saat yönündedir: üst, sağ, alt, sol. Eksik değerler karşı kenardan kopyalanır.

Kısayolun yazımı kadar önemli olan bir davranışı vardır: **yazılmayan açılımı varsayılana
döndürür.** `font: 16px serif` yazıldığında yalnızca boyut ve aile ayarlanmaz; `font-weight`,
`font-style`, `line-height` gibi o kısayolun kapsadığı bütün açılımlar başlangıç değerine
sıfırlanır. Bu davranış, bir kısayolu daha önce ayarlanmış tek bir açılımı değiştirmek için
kullanmayı tehlikeli kılar. Kural şudur: kısayol bir grubu **kurmak** için, açılım o gruptan
tek bir değeri **değiştirmek** için yazılır ve bu sırada kısayol önce gelir.

#### İstasyon Sayfasının İlk Kuralları

Bu kurs, önceki kursta kurulan ölçüm istasyonu sayfasını biçimlendirir. Sayfa, Kuzey Yamaç
adlı istasyonun künyesini, ölçüm çizelgesini, konum görselini ve düzeltme bildirimi formunu
taşıyordu. Şimdiye kadar hiçbir biçem yazılmadı; sayfanın görünümü tarayıcının kendi
varsayılan biçem dosyasından geliyordu — başlıkların kalın ve büyük, listelerin girintili,
bağlantıların altı çizili olması o dosyanın kararıdır.

Kursun eksenindeki biçem dosyası burada başlar:

```css
/* istasyon.css — 1. adım: taban bildirimleri */
body {
  font-family: system-ui, sans-serif;
  line-height: 1.5;
  color: #1c2733;
}

h1 {
  font-size: 2rem;
  margin-block-end: 0.5rem;
}

.olcum-cizelgesi td {
  padding: 8px 12px;
  text-align: right;
}
```

Üç kural, yedi bildirim. Her ders bu dosyaya bir katman ekleyecek ve eklenen katmanın
neden o biçimde yazıldığını gerekçelendirecek. Dosya kurs sonunda renk ölçeği, tipografi
ölçeği, aralık ölçeği ve durum biçemleri taşıyan bütün bir biçem dosyası olacaktır.

#### Özet

- Bir CSS kuralı seçici ve bildirim bloğundan oluşur; seçici belgeden bir ögeler kümesi
  döndürür, blok o kümeye uygulanacak bildirimleri sayar.
- Bildirim, iki nokta üst üsteyle ayrılmış bir özellik–değer çiftidir; özellik adı sabit
  bir sözlükten gelir, değerin dilbilgisi her özellik için ayrı tanımlıdır.
- Geçersizlik bildirim düzeyinde çözülür: tanınmayan özellik veya uymayan değer yalnızca o
  bildirimi düşürür, blok yaşamaya devam eder. Tanınmayan seçici ise kuralın tamamını düşürür.
- Kısayol özellik bir grup açılımı birden ayarlar ve yazılmayan açılımı başlangıç değerine
  döndürür; bu yüzden kısayol kurmak, açılım düzeltmek içindir.
- Sayfanın biçemsiz görünümü tarayıcının varsayılan biçem dosyasından gelir; yazılan her
  kural o dosyanın üzerine eklenir.

#### Sonraki Adım

Bu ders kuralın yapısını kurdu ama kuralların belgeye nasıl ulaştığını söylemedi. Bir
biçem dosyası belgeye üç ayrı yolla bağlanabilir ve bu üç yol yalnızca yazım kolaylığı
bakımından değil, yükleme sırası ve çakışma çözümü bakımından da farklıdır. Sonraki ders
bu üç yolu karşılaştırır ve Web'in Temelleri kursundaki kaynak yükleme sırası dersinde
kurulan çizimi engelleme davranışını biçem dosyaları açısından yeniden ele alır.

### Biçem Dosyasının Bağlanması

Source: https://academia.sh/tr/kurslar/css-temelleri/bicem-dosyasinin-baglanmasi

Bir biçem kümesinin belgeye satır içi, iç ve dış olmak üzere üç yolla bağlanması; bu üç yolun istek sayısı, yeniden kullanım ve çakışma çözümü bakımından farkları.

Önceki ders bir kuralın seçici ve bildirim bloğundan oluştuğunu kurdu ve istasyon
sayfasının ilk üç kuralını yazdı. Kurallar bir yerde durmalıdır ve durdukları yer, hem
bakım maliyetini hem de sayfanın ilk çiziminin ne zaman başlayacağını belirler.

Bu ders üç bağlama yolunu tanımlar, aralarındaki farkları ölçülebilir bir örnekle
gösterir ve Web'in Temelleri ve HTML kursundaki kaynak yükleme sırası dersinde kurulan
engelleme davranışını biçem tarafından yeniden ele alır.

#### Üç Yol

**Satır içi biçem (inline style)** ögenin `style` özniteliğine yazılır. Seçici yoktur:
bildirimler doğrudan o tek ögeye bağlanır.

```html
<h1 style="color: #8a1c1c; font-size: 2rem">Kuzey Yamaç Ölçüm İstasyonu</h1>
```

**İç biçem (internal style sheet)** belgenin baş bölümündeki bir `style` ögesinin içine
yazılır. Kurallar tam biçimlidir; seçiciler yalnızca o belgeye bakar.

```html
<head>
  <meta charset="utf-8">
  <title>Kuzey Yamaç Ölçüm İstasyonu</title>
  <style>
    h1 { font-size: 2rem; }
  </style>
</head>
```

**Dış biçem (external style sheet)** ayrı bir `.css` dosyasıdır ve belgeye `link` ögesiyle
bağlanır. `rel="stylesheet"` bağlantının türünü bildirir; `href` kaynağın adresini verir.

```html
<link rel="stylesheet" href="/istasyon.css">
```

Dördüncü bir yol daha vardır: bir biçem dosyasının içinden `@import url("…")` yazarak
başka bir dosya çağırmak. Bu yazım işler ama bir sıralılık getirir: içeri alınan dosyanın
adresi, onu çağıran dosya indirilip ayrıştırılmadan bilinemez. Bu yüzden `@import`
zinciri, aynı sayıda `link` ögesinden daha geç tamamlanır.

#### Dış Biçem Ayrı Bir Kaynaktır

Dış biçem dosyasının belgeden ayrı bir kaynak olduğu ölçülebilir. Aşağıdaki sunucu iki
adresi ayrı ayrı sunar ve aldığı her isteği kütüğe yazar.

```js
// bicem-sunucu.mjs — iki kaynagi ayri ayri sunar ve istekleri kutuge yazar
import { createServer } from "node:http";

const belge = `<!doctype html>
<html lang="tr">
<head>
<meta charset="utf-8">
<title>Kuzey Yamac Olcum Istasyonu</title>
<link rel="stylesheet" href="/istasyon.css">
<style>h1 { font-size: 2rem }</style>
</head>
<body>
<h1 style="color: #8a1c1c">Kuzey Yamac Olcum Istasyonu</h1>
</body>
</html>
`;

const bicem = `body { font-family: system-ui; line-height: 1.5 }
h1 { color: #143a52 }
`;

const sunucu = createServer((istek, yanit) => {
  console.log(`istek: ${istek.method} ${istek.url}`);
  if (istek.url === "/istasyon.css") {
    yanit.writeHead(200, { "content-type": "text/css; charset=utf-8" });
    yanit.end(bicem);
  } else {
    yanit.writeHead(200, { "content-type": "text/html; charset=utf-8" });
    yanit.end(belge);
  }
});

sunucu.listen(8123, "127.0.0.1", () => {
  console.log("dinliyor: 127.0.0.1:8123");
});
```

Sunucu arka planda çalıştırılıp iki adres ayrı ayrı istenir:

```sh
node bicem-sunucu.mjs > sunucu.log 2>&1 &
sleep 1
curl -s -o /dev/null -w "%{http_code} %{content_type}\n" http://127.0.0.1:8123/
curl -s -o /dev/null -w "%{http_code} %{content_type}\n" http://127.0.0.1:8123/istasyon.css
cat sunucu.log
```

```
200 text/html; charset=utf-8
200 text/css; charset=utf-8
dinliyor: 127.0.0.1:8123
istek: GET /
istek: GET /istasyon.css
```

Kütükteki iki satır, dış biçem dosyasının belgeyle birlikte gelmediğini gösterir: ikinci
bir istek yapılır. Bunun üç sonucu vardır.

Birincisi, dosya **yeniden kullanılabilir**. On sayfa aynı `istasyon.css` dosyasına
bağlanırsa dosya bir kez indirilir; sonraki sayfalarda önbellekten okunur. İnternet Nasıl
Çalışır kursunda tanımlanan koşullu istek ve yaşam süresi kavramları burada işler.

İkincisi, dosya **belgeden bağımsız değişebilir**. Belgenin baytları değişmeden biçem
güncellenebilir; tersi de doğrudur.

Üçüncüsü, ikinci istek bir **maliyettir**. Belge ayrıştırılırken bulunan `link` ögesi yeni
bir bağlantı gerektirebilir ve bu, ilk çizimin başlamasını geciktirir.

#### Çizim, Biçem Çözülmeden Başlamaz

Web'in Temelleri ve HTML kursundaki kaynak yükleme sırası dersinde betiklerin
ayrıştırmayı engellemesi anlatılmıştı. Biçem dosyası ayrıştırmayı engellemez — belge
ağacı kurulmaya devam eder — ama **çizimi** engeller.

Gerekçe, engellememenin sonucuna bakılınca görülür. Biçem çözülmeden çizim yapılsaydı,
sayfa önce varsayılan biçemle çizilir, dosya geldiğinde yeniden çizilirdi. Kullanıcı iki
farklı sayfa görürdü. Bu yüzden tanımlı davranış, biçem çözülene kadar beklemektir.

Bu kural üç pratik sonuç doğurur:

- Biçem dosyası belgenin **baş bölümünde** bağlanır. Gövdenin sonuna konursa çizim yine
  bekler, ama bekleme daha geç başlar; toplam süre uzar.
- Dosya **küçük tutulur**; bir sayfada kullanılmayan kurallar o sayfanın çizimini geciktirir.
- Bir bağlantı yalnızca belirli bir ortamda gerekiyorsa `media` özniteliğiyle koşullanır.
  Koşulu sağlamayan bir bağlantının dosyası yine indirilir, ancak çizimi engellemez:

```html
<link rel="stylesheet" href="/istasyon.css">
<link rel="stylesheet" href="/yazdirma.css" media="print">
```

İkinci dosya yazdırma çıktısı için yazılmış kuralları taşır; ekran çiziminin başlamasını
beklemez.

#### Üç Yolun Karşılaştırması

| Ölçüt | Satır içi | İç | Dış |
|---|---|---|---|
| Seçici yazılabilir mi | Hayır | Evet | Evet |
| Birden çok belgede kullanım | Hayır | Hayır | Evet |
| Ayrı istek | Hayır | Hayır | Evet |
| Önbelleğe alınabilir | Belgeyle birlikte | Belgeyle birlikte | Bağımsız |
| Çakışmada önceliği | En yüksek | Kaynak sırasına göre | Kaynak sırasına göre |
| Sözde sınıf yazılabilir mi | Hayır | Evet | Evet |

Tablodaki iki satır ayrıca açıklama ister.

**Satır içi biçemde seçici yoktur**, dolayısıyla sözde sınıf da yazılamaz. Bir ögenin üzerine
gelindiğinde ya da odaklandığında değişecek bir bildirim `style` özniteliğine yazılamaz;
bunun için bir kural gerekir.

**Satır içi biçemin önceliği en yüksektir.** Bir sonraki bölümlerde göreceğimiz özgüllük
hesabı satır içi bildirimleri kapsamaz; onlar hesabın dışında, daha üstte durur. Bu, satır
içi bir bildirimi bir kuralla bastırmayı olağan yollarla imkânsız kılar. Sonuç şudur: satır
içi biçem, bakımı en pahalı yoldur ve yalnızca değeri belge üretilirken hesaplanan
durumlarda gerekçelendirilebilir.

#### Göreli Adresler Dosyanın Kendisine Göre Çözülür

Dış biçem dosyasının ayrı bir kaynak olmasının sık gözden kaçan bir sonucu vardır: o
dosyanın içindeki göreli adresler, belgeye göre değil **biçem dosyasının kendi adresine**
göre çözülür.

Belge `/urunler/istasyon.html` adresinde, biçem dosyası `/bicem/istasyon.css` adresinde
olsun. Biçem dosyasında `background-image: url("desen.png")` yazılırsa istenecek adres
`/bicem/desen.png` olur — `/urunler/desen.png` değil. Aynı yazım belgenin `style`
özniteliğinde geçseydi belgeye göre çözülür ve `/urunler/desen.png` istenirdi.

Ayrım, bir biçem dosyasını yeniden kullanılabilir kılan şeyin ta kendisidir: dosya, kendi
komşusundaki varlıklara sabit bir yoldan başvurur ve hangi belgeden çağrıldığından
etkilenmez. Görsel dosyalarını biçem dosyasının yanında tutmak bu yüzden bir düzen
tercihinden fazlasıdır.

#### İstasyon Sayfasının Bağlanması

Kursun ekseninde tek bir dış dosya kullanılır. Belgenin baş bölümü şu hâle gelir:

```html
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Kuzey Yamaç Ölçüm İstasyonu</title>
  <link rel="stylesheet" href="/istasyon.css">
</head>
```

`istasyon.css` dosyası önceki dersteki üç kuralla başlar ve kurs boyunca büyür. Belgeye
tek bir noktadan bağlanması, sonraki derslerde bir kuralı değiştirmenin belgeye
dokunmadan yapılabilmesi anlamına gelir — yapı ve sunum ayrımının dosya düzeyindeki
karşılığı budur.

#### Özet

- Bir biçem kümesi belgeye üç yolla bağlanır: ögenin `style` özniteliği (satır içi), baş
  bölümündeki `style` ögesi (iç) ve `link` ögesiyle çağrılan ayrı dosya (dış).
- Dış biçem dosyası ayrı bir HTTP isteğidir; bağımsız önbelleğe alınabilir ve birden çok
  belgede yeniden kullanılabilir, ama bir istek maliyeti getirir.
- Biçem dosyası ayrıştırmayı engellemez, çizimi engeller; bu, sayfanın iki kez farklı
  görünmesini önlemek için tanımlanmış bir davranıştır.
- `media` özniteliğiyle koşullanan bir bağlantının dosyası indirilir ama koşul sağlanmıyorsa
  çizimi engellemez.
- Satır içi biçemde seçici yoktur, dolayısıyla durum biçemi yazılamaz; ayrıca önceliği
  özgüllük hesabının üstünde olduğu için bastırılması güçtür.

#### Sonraki Adım

Kurallar artık belgeye ulaşıyor, ama hangi ögelere ulaştıkları henüz tek bir örnekle
sınırlı. `h1` yazmak bütün birinci düzey başlıkları seçer; peki yalnızca ölçüm
çizelgesindeki eksik değerleri seçmek gerekirse? Sonraki ders seçicinin dört temel türünü
tanımlar — tür, sınıf, kimlik ve evrensel seçici — ve her birinin belge ağacında hangi
kümeyi döndürdüğünü çalıştırılabilir bir eşleyiciyle ölçer.

### Seçiciler

Source: https://academia.sh/tr/kurslar/css-temelleri/seciciler

Tür, sınıf, kimlik ve evrensel seçicilerin belge ağacında hangi kümeyi döndürdüğü; bileşik seçici kavramı ve sınıf adlarının anlamla adlandırılması.

Önceki ders biçem dosyasını belgeye bağladı. Dosyadaki her kural bir seçiciyle başlıyor ve
şimdiye kadar yazılan seçiciler yalnızca öge adıydı: `body`, `h1`, `td`. Bu, ölçüm
çizelgesindeki bütün hücreleri aynı biçimde ele almak demektir. Oysa çizelgede eksik
ölçümleri işaretleyen hücreler var ve onların ayrı görünmesi gerekiyor.

Bu ders dört temel seçiciyi tanımlar: **tür**, **sınıf**, **kimlik** ve **evrensel**
seçici. Her birinin belge ağacında hangi kümeyi döndürdüğü, çalıştırılabilir bir eşleyiciyle
ölçülür.

#### Seçici Bir Küme Döndürür

Bir seçicinin işi tektir: belge ağacındaki düğümlere bakar ve bir alt küme döndürür.
Döndürülen kümenin büyüklüğü sıfır da olabilir; boş küme bir hata değildir, kuralın hiçbir
ögeye uygulanmaması demektir.

Aşağıdaki program, önceki kurstaki istasyon sayfasının küçültülmüş bir ağacını kurar ve
dört seçici türünü bu ağaç üzerinde eşler. Ağaç, düğüm adını, sınıf listesini ve varsa
kimliğini taşır — seçici eşlemesi için gereken bilgi budur.

```js
// secici-esle.mjs — kucuk bir belge agaci uzerinde temel secicileri esler
const agac = {
  ad: "body", sinif: [], kimlik: null, cocuk: [
    { ad: "header", sinif: ["kunye"], kimlik: null, cocuk: [
      { ad: "h1", sinif: [], kimlik: null, cocuk: [] },
      { ad: "p", sinif: ["ozet"], kimlik: null, cocuk: [] },
    ]},
    { ad: "main", sinif: [], kimlik: "icerik", cocuk: [
      { ad: "table", sinif: ["olcum-cizelgesi"], kimlik: null, cocuk: [
        { ad: "tr", sinif: [], kimlik: null, cocuk: [
          { ad: "td", sinif: ["deger"], kimlik: null, cocuk: [] },
          { ad: "td", sinif: ["deger", "eksik"], kimlik: null, cocuk: [] },
        ]},
      ]},
      { ad: "p", sinif: ["ozet", "not"], kimlik: null, cocuk: [] },
    ]},
  ],
};

function* tumDugumler(d, yol = []) {
  const kendiYolu = [...yol, etiketle(d)];
  yield { dugum: d, yol: kendiYolu.join(" > ") };
  for (const c of d.cocuk) yield* tumDugumler(c, kendiYolu);
}

function etiketle(d) {
  return d.ad + (d.kimlik ? `#${d.kimlik}` : "") + d.sinif.map((s) => `.${s}`).join("");
}

// yalnizca basit secici: tur | .sinif | #kimlik | *
function esler(secici, d) {
  if (secici === "*") return true;
  if (secici.startsWith(".")) return d.sinif.includes(secici.slice(1));
  if (secici.startsWith("#")) return d.kimlik === secici.slice(1);
  return d.ad === secici;
}

const seciciler = ["*", "p", ".ozet", "#icerik", ".deger", ".eksik", "td", "#deger"];
for (const s of seciciler) {
  const bulunan = [...tumDugumler(agac)].filter(({ dugum }) => esler(s, dugum));
  console.log(`${s.padEnd(8)} -> ${bulunan.length} oge`);
  for (const b of bulunan) console.log(`           ${b.yol}`);
}
```

```
*        -> 10 oge
           body
           body > header.kunye
           body > header.kunye > h1
           body > header.kunye > p.ozet
           body > main#icerik
           body > main#icerik > table.olcum-cizelgesi
           body > main#icerik > table.olcum-cizelgesi > tr
           body > main#icerik > table.olcum-cizelgesi > tr > td.deger
           body > main#icerik > table.olcum-cizelgesi > tr > td.deger.eksik
           body > main#icerik > p.ozet.not
p        -> 2 oge
           body > header.kunye > p.ozet
           body > main#icerik > p.ozet.not
.ozet    -> 2 oge
           body > header.kunye > p.ozet
           body > main#icerik > p.ozet.not
#icerik  -> 1 oge
           body > main#icerik
.deger   -> 2 oge
           body > main#icerik > table.olcum-cizelgesi > tr > td.deger
           body > main#icerik > table.olcum-cizelgesi > tr > td.deger.eksik
.eksik   -> 1 oge
           body > main#icerik > table.olcum-cizelgesi > tr > td.deger.eksik
td       -> 2 oge
           body > main#icerik > table.olcum-cizelgesi > tr > td.deger
           body > main#icerik > table.olcum-cizelgesi > tr > td.deger.eksik
#deger   -> 0 oge
```

#### Dört Tür

**Tür seçici (type selector)** öge adını yazar ve o adı taşıyan bütün ögeleri döndürür.
`p` seçicisi iki paragrafı da döndürdü: biri künyede, biri ana içerikte. Tür seçici
belgedeki yerine bakmaz.

**Sınıf seçici (class selector)** nokta ile başlar ve `class` özniteliğinde o adı taşıyan
ögeleri döndürür. Bir öge birden çok sınıf taşıyabilir; `class="deger eksik"` yazan hücre
hem `.deger` hem `.eksik` seçicisiyle eşleşti. Sınıf adları boşlukla ayrılmış bir listedir
ve sıraları anlam taşımaz.

**Kimlik seçici (id selector)** diyez ile başlar ve `id` özniteliği eşleşen ögeyi döndürür.
Kimlikler belge içinde benzersiz olmalıdır; bu yüzden kimlik seçicisi en çok bir öge
döndürür. `#icerik` bir öge döndürdü.

**Evrensel seçici (universal selector)** yıldızdır ve her ögeyle eşleşir. Örnekteki ağaçta
on düğüm vardı ve on düğümü de döndürdü.

Son satır bir ayrımı gösterir: `#deger` sıfır öge döndürdü, çünkü `deger` bir **sınıf**
adıdır, kimlik değil. Nokta ile diyezin karıştırılması, boş küme döndüren ve hiçbir uyarı
üretmeyen bir kuraldır. Bir kuralın işlememesinin en sık sebeplerinden biri budur.

#### Bileşik Seçici

Basit seçiciler aralarında boşluk bırakılmadan yan yana yazıldığında **bileşik seçici
(compound selector)** oluşur ve **hepsini birden** sağlayan ögeleri döndürür. Küme
işlemleriyle söylenirse: kesişim.

```css
td.eksik      { color: #8a1c1c; }
p.ozet.not    { font-style: italic; }
```

Birinci seçici, hem `td` olan hem `eksik` sınıfını taşıyan hücreleri döndürür. Örnek
ağaçta bu tek hücredir. İkinci seçici üç koşulu birden ister ve yine tek ögeye ulaşır.

Bileşik seçicide tür seçici varsa **en başta** yazılır: `td.eksik` geçerlidir, `.eksiktd` başka
bir şeydir. Tür seçici yazılmadığında evrensel seçici varsayılır; `.eksik` ile `*.eksik` aynı
kümeyi döndürür.

#### Sınıf mı, Kimlik mi

İkisi de belgeye yazarın koyduğu adlardır. Aralarındaki teknik fark tektir: kimlik
benzersizdir, sınıf değildir. Bu farktan iki ayrı sonuç çıkar.

Kimlik, belgeye **tek bir yere** işaret eden bir çapa koyar. Bağlantı hedefi
(`href="#icerik"`), etiket–alan ilişkisi (`for` özniteliği) ve erişilebilirlik
başvuruları kimlikle kurulur. Bunlar biçemle ilgili değildir; kimliğin asıl işi budur.

Sınıf ise **bir sınıfa ait olmayı** bildirir. Biçemin ihtiyaç duyduğu şey tam olarak
budur: "bu hücre eksik ölçüm taşır", "bu paragraf özettir". Aynı bildirim belgede kaç kez
geçerse geçsin aynı biçemi almalıdır.

Biçemde kimlik seçicisi kullanmanın sorunu, işlemediğinde değil, **işlediğinde** ortaya
çıkar: kimlik seçicisinin özgüllüğü sınıf seçicisinden yüksektir ve bir kez yazıldığında
onu bastırmak için yine bir kimlik seçicisi gerekir. Bu hesabı basamaklama ve özgüllük
dersinde sayıyla yapacağız. Şimdilik uygulanabilir kural şudur: **biçem sınıfla yazılır,
kimlik başvuru içindir.**

#### Sınıf Adı Anlamı Adlandırır, Görünümü Değil

Bir sınıf adı iki türlü seçilebilir. `.kirmizi` görünümü adlandırır; `.eksik` durumu
adlandırır. İkisi de aynı hücreye takılabilir ve başlangıçta aynı sonucu verir.

Fark, karar değiştiğinde görünür. Eksik ölçümlerin kırmızı yerine gri gösterilmesine karar
verilirse, `.kirmizi` adı yalan söylemeye başlar: adı kırmızı, rengi gri bir sınıf kalır.
Düzeltmek için belgenin kendisine dokunmak, sınıf adını her geçtiği yerde değiştirmek
gerekir. `.eksik` adında ise değişen tek şey bir bildirimin değeri olur; belge sabit kalır.

Bu, Web'in Temelleri ve HTML kursunda kurulan yapı–sunum ayrımının sınıf adlarındaki
karşılığıdır: belge **ne olduğunu** söyler, biçem dosyası **nasıl görüneceğini**.

İstasyon sayfasının sınıf adları bu ilkeye göre seçilmiştir:

```css
/* istasyon.css — 2. adım: durum sınıfları */
.olcum-cizelgesi td.deger { text-align: right; }
.olcum-cizelgesi td.eksik { color: #8a1c1c; }
.kunye .ozet              { font-size: 1.125rem; }
```

Son kuralda iki seçici arasında bir boşluk var ve bu boşluk bir işleçtir — bileşik seçici
değil, bir ilişki bildirimidir. Sonraki ders bu işleci ve akrabalarını tanımlar.

#### Özet

- Bir seçici belge ağacından bir alt küme döndürür; boş küme döndürmesi hata değildir,
  yalnızca kuralın hiçbir ögeye uygulanmaması demektir.
- Tür seçici öge adıyla, sınıf seçici nokta ile, kimlik seçici diyez ile yazılır; evrensel
  seçici yıldızdır ve her ögeyle eşleşir.
- Basit seçiciler boşluksuz yan yana yazıldığında bileşik seçici olur ve koşulların
  kesişimini döndürür; tür seçici en başta yazılır.
- Kimlik belge içinde benzersizdir ve asıl işi başvuru hedefi olmaktır; biçem sınıfla
  yazılır, çünkü bir biçem kararı birden çok ögeye uygulanır.
- Sınıf adı görünümü değil anlamı adlandırır; görünümü adlandıran ad, biçem kararı
  değiştiğinde belgeyi de değiştirmeye zorlar.

#### Sonraki Adım

Bu dersteki seçiciler ögenin **kendisine** baktı: adı ne, hangi sınıfları taşıyor, kimliği
var mı. Belge ağacındaki konumu hiç kullanılmadı — oysa "yalnızca ölçüm çizelgesinin
içindeki hücreler" gibi bir istek, ögenin nerede durduğunu sormayı gerektirir. Sonraki
ders ağaçtaki ilişkileri seçiciye taşıyan birleştiricileri tanımlar ve bir seçicinin
sağdan sola nasıl değerlendirildiğini gösterir.

### Birleştirici Seçiciler

Source: https://academia.sh/tr/kurslar/css-temelleri/birlestirici-seciciler

Alt öge, çocuk, bitişik kardeş ve genel kardeş birleştiricileri; bir seçicinin sağdan sola değerlendirilmesi ve anahtar seçici kavramı.

Önceki dersin son kuralında `.kunye .ozet` yazılmıştı ve iki seçici arasındaki boşluğun bir
işleç olduğu söylenip geçilmişti. O boşluk **birleştirici (combinator)** adını taşır ve
seçiciye ağaçtaki konum sorusunu ekler: ögenin kendisi değil, nerede durduğu sorulur.

Bu ders dört birleştiriciyi tanımlar ve bir seçicinin nasıl değerlendirildiğini
gösterir. Değerlendirme sırası, hem sonucu hem de bir seçicinin maliyetini anlamak için
gereklidir.

#### Dört Birleştirici

| Yazım | Ad | Sorduğu soru |
|---|---|---|
| `A B` | alt öge birleştiricisi (descendant) | B'nin atalarından biri A mı? |
| `A > B` | çocuk birleştiricisi (child) | B'nin doğrudan üstü A mı? |
| `A + B` | bitişik kardeş (adjacent sibling) | B'nin bir önceki kardeşi A mı? |
| `A ~ B` | genel kardeş (general sibling) | B'nin önceki kardeşlerinden biri A mı? |

Dördü de aynı biçimde okunur: **sağdaki seçilir, soldaki koşuldur.** `.olcum-cizelgesi td`
kuralı `td` ögelerini seçer; `.olcum-cizelgesi` yalnızca hangi `td` ögelerinin seçileceğini
daraltır. Sol taraf hiçbir zaman biçimlendirilmez.

Kardeş birleştiricileri yalnızca **geriye** bakar. `A + B` yazımı "A'dan sonra gelen B"
anlamındadır; "B'den sonra gelen A" diye bir birleştirici yoktur.

#### Değerlendirme Sağdan Sola Yapılır

Bir seçici soldan sağa okunur ama sağdan sola değerlendirilir. Sebep, iki yönün maliyet
farkındadır.

Soldan sağa gidilseydi `.olcum-cizelgesi td` için önce bütün belgede o sınıfı taşıyan
ögeler bulunur, sonra her birinin **bütün alt ağacı** taranırdı. Sağdan sola gidildiğinde
ise her `td` ögesi için yalnızca atalar zinciri yukarı doğru izlenir; zincirin uzunluğu
ağacın derinliği kadardır. İkinci yol, bir ögenin biçemi hesaplanırken sorulan sorunun da
doğal yönüdür: "bu ögeye hangi kurallar uyuyor?"

Sağdaki bileşik seçiciye **anahtar seçici (key selector)** denir. Bir kuralın hangi ögeler
için sınanacağını belirleyen odur.

Aşağıdaki program dört birleştiriciyi sağdan sola değerlendirir. Ağaç, ölçüm çizelgesinin
gerçek yapısını taşır: satırlar `thead` ve `tbody` içinde gruplanmıştır.

```js
// birlestirici.mjs — dort birlestiriciyi sagdan sola degerlendirir
const agac = { ad: "body", sinif: [], cocuk: [
  { ad: "main", sinif: [], cocuk: [
    { ad: "table", sinif: ["olcum-cizelgesi"], cocuk: [
      { ad: "thead", sinif: [], cocuk: [
        { ad: "tr", sinif: [], cocuk: [{ ad: "th", sinif: [], cocuk: [] }] },
      ]},
      { ad: "tbody", sinif: [], cocuk: [
        { ad: "tr", sinif: [], cocuk: [
          { ad: "td", sinif: ["ad"], cocuk: [] },
          { ad: "td", sinif: ["deger"], cocuk: [] },
          { ad: "td", sinif: ["deger", "eksik"], cocuk: [] },
        ]},
      ]},
    ]},
    { ad: "p", sinif: ["not"], cocuk: [] },
    { ad: "table", sinif: [], cocuk: [
      { ad: "tbody", sinif: [], cocuk: [
        { ad: "tr", sinif: [], cocuk: [{ ad: "td", sinif: ["deger"], cocuk: [] }] },
      ]},
    ]},
  ]},
]};

// her dugume ust ve kardes bilgisi ekle
function bagla(d, ust = null) {
  d.ust = ust;
  d.cocuk.forEach((c, i) => { c.oncekiKardes = i > 0 ? d.cocuk[i - 1] : null; bagla(c, d); });
  return d;
}
bagla(agac);

const yol = (d) => (d.ust ? yol(d.ust) + " " : "") + d.ad + d.sinif.map((s) => "." + s).join("");
function* hepsi(d) { yield d; for (const c of d.cocuk) yield* hepsi(c); }

const basitEsler = (s, d) =>
  s === "*" ? true : s.startsWith(".") ? d.sinif.includes(s.slice(1)) : d.ad === s;

// bilesik secici: bosluksuz basit seciciler
function bilesikEsler(bilesik, d) {
  const parcalar = bilesik.match(/(^[a-z]+|\.[a-z-]+|\*)/g) ?? [];
  return parcalar.every((p) => basitEsler(p, d));
}

// secici: bilesik (birlestirici bilesik)*  — sagdan sola cozulur
function secERler(secici, dugum) {
  const jetonlar = secici.trim().split(/\s+/);
  let d = dugum;
  let i = jetonlar.length - 1;
  if (!bilesikEsler(jetonlar[i], d)) return false;
  i--;
  while (i >= 0) {
    const birlestirici = ">+~".includes(jetonlar[i]) ? jetonlar[i--] : " ";
    const hedef = jetonlar[i--];
    if (birlestirici === ">") { d = d.ust; if (!d || !bilesikEsler(hedef, d)) return false; }
    else if (birlestirici === "+") { d = d.oncekiKardes; if (!d || !bilesikEsler(hedef, d)) return false; }
    else if (birlestirici === "~") {
      let k = d.oncekiKardes;
      while (k && !bilesikEsler(hedef, k)) k = k.oncekiKardes;
      if (!k) return false; d = k;
    } else {
      let a = d.ust;
      while (a && !bilesikEsler(hedef, a)) a = a.ust;
      if (!a) return false; d = a;
    }
  }
  return true;
}

const denemeler = [
  "table td",
  ".olcum-cizelgesi td",
  ".olcum-cizelgesi > td",
  "tbody > tr > td",
  "td.ad + td",
  "td.ad ~ td",
  ".olcum-cizelgesi + p",
];
for (const s of denemeler) {
  const bulunan = [...hepsi(agac)].filter((d) => secERler(s, d));
  console.log(`${s.padEnd(26)} -> ${bulunan.length}`);
  for (const b of bulunan) console.log(`  ${yol(b)}`);
}
```

```
table td                   -> 4
  body main table.olcum-cizelgesi tbody tr td.ad
  body main table.olcum-cizelgesi tbody tr td.deger
  body main table.olcum-cizelgesi tbody tr td.deger.eksik
  body main table tbody tr td.deger
.olcum-cizelgesi td        -> 3
  body main table.olcum-cizelgesi tbody tr td.ad
  body main table.olcum-cizelgesi tbody tr td.deger
  body main table.olcum-cizelgesi tbody tr td.deger.eksik
.olcum-cizelgesi > td      -> 0
tbody > tr > td            -> 4
  body main table.olcum-cizelgesi tbody tr td.ad
  body main table.olcum-cizelgesi tbody tr td.deger
  body main table.olcum-cizelgesi tbody tr td.deger.eksik
  body main table tbody tr td.deger
td.ad + td                 -> 1
  body main table.olcum-cizelgesi tbody tr td.deger
td.ad ~ td                 -> 2
  body main table.olcum-cizelgesi tbody tr td.deger
  body main table.olcum-cizelgesi tbody tr td.deger.eksik
.olcum-cizelgesi + p       -> 1
  body main p.not
```

Bu gerçekleştirim, alt öge ve genel kardeş adımlarında geriye dönüş yapmaz: eşleşen ilk
atayı ya da ilk kardeşi seçer ve orada kalır. Gerçek bir eşleyici, sonraki adım
başarısızsa geri döner ve başka bir ata dener. Yukarıdaki yedi örnekte iki davranış aynı
sonucu verir.

#### Sıfır Öge Döndüren Seçici

Üçüncü satır dikkat ister: `.olcum-cizelgesi > td` hiçbir öge döndürmedi, oysa
`.olcum-cizelgesi td` üç öge döndürdü. Aradaki tek fark bir işarettir.

Sebep ağaçtadır. Hücreler tablonun doğrudan çocuğu değildir; aradaki zincir
`table > tbody > tr > td` biçimindedir. Çocuk birleştiricisi tam bir adım ister ve o adım
`tbody` ögesine denk gelir.

Web'in Temelleri ve HTML kursunda ayrıştırıcının kaynakta yazılmamış ögeleri kurduğu
anlatılmıştı; `tbody` bunların en sık karşılaşılanıdır. Kaynak metinde `tbody` etiketi
yazılmamış olsa bile öge belgede vardır ve seçici o ögeyi görür. Bu, "etiket" ile "öge"
ayrımının biçem tarafındaki ilk somut sonucudur: **seçici kaynak metne değil, ayrıştırma
sonrası ağaca bakar.**

#### Çocuk Birleştiricisi Ne Zaman Gerekir

Alt öge birleştiricisi derinlik gözetmez ve bu çoğu zaman istenen davranıştır. Ancak iç
içe geçmiş aynı türden yapılarda istenmeyen sonuç verir.

İstasyon sayfasının gezinme bölümü iç içe listeler taşıyorsa, `.gezinme li` seçicisi hem
üst düzey maddeleri hem alt maddeleri döndürür. Yalnızca üst düzeyi hedeflemek için
`.gezinme > ul > li` yazılır. Kural şudur: derinlik anlam taşıyorsa çocuk birleştiricisi,
taşımıyorsa alt öge birleştiricisi kullanılır.

#### Kardeş Birleştiricileri Sıraya Bağlıdır

`td.ad + td` bir hücre döndürdü: ad hücresinden hemen sonraki değer hücresi. `td.ad ~ td`
ise ikisini birden döndürdü, çünkü genel kardeş birleştiricisi araya kaç kardeş girdiğine
bakmaz.

Kardeş birleştiricilerinin tipik kullanımı, iki ögenin **arasındaki** ilişkiyi
biçimlendirmektir. Ard arda gelen iki paragraf arasına boşluk koymak için:

```css
p + p { margin-block-start: 0.75em; }
```

Bu yazım, bir bölümün **ilk** paragrafına boşluk eklemez — çünkü onun önünde bir paragraf
kardeş yoktur. Aynı sonucu her paragrafa boşluk verip ilkini geri almakla da elde etmek
mümkündür; kardeş birleştiricisi ile yazılan biçim, kuralı tek bir yerde ve istisnasız
tutar.

#### İstasyon Sayfasının Kuralları

Biçem dosyası bu dersle birlikte ağaçtaki konumları kullanmaya başlar:

```css
/* istasyon.css — 3. adım: ilişkiye bağlı kurallar */
.olcum-cizelgesi tbody td   { padding: 8px 12px; }
.olcum-cizelgesi tbody > tr { border-block-end: 1px solid #d5dbe0; }
.gezinme > ul > li          { display: inline-block; }
.olcum-cizelgesi + p        { font-size: 0.875rem; }
```

Son kural, çizelgeden hemen sonra gelen açıklama paragrafını küçültür. Paragrafa ayrı bir
sınıf vermek de aynı sonucu verirdi; aradaki fark, kuralın belgeye bir sınıf ekleme
gerektirip gerektirmemesidir. Belgeye dokunmadan çözülebilen bir biçem sorunu, biçem
dosyasında çözülür.

#### Özet

- Birleştirici, seçiciye ağaçtaki konum sorusunu ekler; dördü de aynı biçimde okunur —
  sağdaki seçilir, soldaki koşuldur.
- Alt öge birleştiricisi derinlik gözetmez, çocuk birleştiricisi tam bir adım ister;
  bitişik kardeş bir önceki kardeşe, genel kardeş önceki kardeşlerin herhangi birine bakar.
- Seçici sağdan sola değerlendirilir; en sağdaki bileşik seçici anahtar seçicidir ve
  kuralın hangi ögeler için sınanacağını belirler.
- Seçici, kaynak metne değil ayrıştırma sonrası ağaca bakar; kaynakta yazılmayan `tbody`
  gibi ögeler zincire dâhildir ve çocuk birleştiricisini etkiler.
- Kardeş birleştiricileri yalnızca geriye bakar; "sonraki kardeşin öncesi" diye bir
  birleştirici yoktur.

#### Sonraki Adım

Seçiciler şimdiye kadar öge adına, sınıfa, kimliğe ve ağaçtaki konuma baktı. Belgede bir
bilgi daha var ve henüz kullanılmadı: öznitelik değerleri. Form alanlarının türü, bir
bağlantının hedef adresi, veri öznitelikleriyle iliştirilen ölçüm birimleri — hepsi
özniteliklerde durur. Sonraki ders öznitelik seçicilerini ve değer eşleme biçimlerini
tanımlar.

### Öznitelik Seçicileri

Source: https://academia.sh/tr/kurslar/css-temelleri/oznitelik-secicileri

Bir özniteliğin varlığına ve değerine göre eşleme; tam eşleme, liste üyeliği, tire önekli eşleme ve alt dizgi eşlemeleri ile harf duyarlılığı işareti.

Seçiciler şimdiye kadar öge adına, sınıfa, kimliğe ve ağaçtaki konuma baktı. Belgede bir
bilgi katmanı daha var: **öznitelikler**. Bir bağlantının hedef adresi, bir form alanının
türü, bir hücreye veri özniteliğiyle iliştirilen ölçüm birimi — hepsi orada durur ve
hiçbiri sınıf listesine yazılmamıştır.

Bu ders öznitelik seçicilerini tanımlar. Yedi eşleme biçimi vardır ve aralarındaki farklar
dizgi işlemleridir; bu yüzden hepsi çalıştırılarak gösterilebilir.

#### Yedi Eşleme Biçimi

Öznitelik seçicisi köşeli parantez içinde yazılır. En basit biçimi özniteliğin
**varlığını** sorar; değerine bakmaz.

| Yazım | Koşul |
|---|---|
| `[ad]` | öznitelik var mı |
| `[ad="x"]` | değer tam olarak `x` mi |
| `[ad~="x"]` | boşlukla ayrılmış değer listesinde `x` var mı |
| `[ad\|="x"]` | değer `x` mi, ya da `x-` ile mi başlıyor |
| `[ad^="x"]` | değer `x` ile mi başlıyor |
| `[ad$="x"]` | değer `x` ile mi bitiyor |
| `[ad*="x"]` | değer `x` içeriyor mu |

Son üçü alt dizgi eşlemesidir ve değeri yapısız bir metin gibi ele alır. Ortadaki ikisi
ise değerin bir yapısı olduğunu varsayar: `~=` boşlukla ayrılmış liste, `|=` tire ile
bölümlenmiş kod.

Aşağıdaki program yedi biçimi de uygular ve istasyon sayfasından alınmış ögeler üzerinde
sınar.

```js
// oznitelik-esle.mjs — yedi oznitelik esleme bicimini uygular
function esle(bicim, ad, deger, oznitelikler, duyarsiz = false) {
  if (!(ad in oznitelikler)) return false;
  let v = oznitelikler[ad];
  let d = deger;
  if (duyarsiz) { v = v.toLowerCase(); d = d.toLowerCase(); }
  switch (bicim) {
    case "var":  return true;                       // [ad]
    case "=":    return v === d;                    // [ad="x"]
    case "~=":   return v.split(/\s+/).includes(d); // [ad~="x"]
    case "|=":   return v === d || v.startsWith(d + "-");
    case "^=":   return d !== "" && v.startsWith(d);
    case "$=":   return d !== "" && v.endsWith(d);
    case "*=":   return d !== "" && v.includes(d);
    default: throw new Error("bilinmeyen bicim: " + bicim);
  }
}

const ogeler = [
  { ad: "a",     oz: { href: "https://veri.ornek/istasyon.pdf", rel: "noopener yardim" } },
  { ad: "a",     oz: { href: "/olcumler", rel: "yardim" } },
  { ad: "a",     oz: { href: "mailto:gozlem@ornek" } },
  { ad: "input", oz: { type: "number", name: "sicaklik", required: "" } },
  { ad: "input", oz: { type: "text", name: "gozlemci" } },
  { ad: "td",    oz: { "data-birim": "derece-C" } },
  { ad: "td",    oz: { "data-birim": "derece" } },
  { ad: "html",  oz: { lang: "tr-TR" } },
];

const sinamalar = [
  ["[required]",              "var", "required", ""],
  ['[type="number"]',         "=",   "type",     "number"],
  ['[rel~="yardim"]',         "~=",  "rel",      "yardim"],
  ['[lang|="tr"]',            "|=",  "lang",     "tr"],
  ['[href^="https://"]',      "^=",  "href",     "https://"],
  ['[href$=".pdf"]',          "$=",  "href",     ".pdf"],
  ['[href*="olcum"]',         "*=",  "href",     "olcum"],
  ['[data-birim|="derece"]',  "|=",  "data-birim", "derece"],
];

for (const [yazim, bicim, ad, deger] of sinamalar) {
  const bulunan = ogeler.filter((o) => esle(bicim, ad, deger, o.oz));
  console.log(`${yazim.padEnd(26)} -> ${bulunan.length}`);
  for (const b of bulunan) {
    const oz = Object.entries(b.oz).map(([k, v]) => `${k}="${v}"`).join(" ");
    console.log(`   <${b.ad} ${oz}>`);
  }
}

// buyuk-kucuk harf duyarliligi
console.log("--- harf duyarliligi ---");
const oge = { type: "NUMBER" };
console.log('[type="number"]   ->', esle("=", "type", "number", oge));
console.log('[type="number" i] ->', esle("=", "type", "number", oge, true));
```

```
[required]                 -> 1
   <input type="number" name="sicaklik" required="">
[type="number"]            -> 1
   <input type="number" name="sicaklik" required="">
[rel~="yardim"]            -> 2
   <a href="https://veri.ornek/istasyon.pdf" rel="noopener yardim">
   <a href="/olcumler" rel="yardim">
[lang|="tr"]               -> 1
   <html lang="tr-TR">
[href^="https://"]         -> 1
   <a href="https://veri.ornek/istasyon.pdf" rel="noopener yardim">
[href$=".pdf"]             -> 1
   <a href="https://veri.ornek/istasyon.pdf" rel="noopener yardim">
[href*="olcum"]            -> 1
   <a href="/olcumler" rel="yardim">
[data-birim|="derece"]     -> 2
   <td data-birim="derece-C">
   <td data-birim="derece">
--- harf duyarliligi ---
[type="number"]   -> false
[type="number" i] -> true
```

#### Liste Üyeliği ile Alt Dizgi Farkı

`[rel~="yardim"]` iki bağlantıyı da döndürdü. Birinin `rel` değeri `"noopener yardim"`,
diğerininki `"yardim"`. Tilde işareti değeri boşluklardan bölüp listede arar; bu yüzden
sıradan bağımsızdır ve kısmi bir sözcüğe takılmaz.

Aynı sonuç `[rel*="yardim"]` ile de alınırdı ama bu yazım yanlış eşleşmelere açıktır:
`rel="yardimci-degil"` değeri de eşleşirdi, oysa liste üyeliği yoktur. Bir öznitelik
boşlukla ayrılmış anahtar sözcük listesi taşıyorsa **`~=` yazılır**, `*=` değil.

Aynı ayrım `|=` için de geçerlidir. `[lang|="tr"]` yazımı `lang="tr"` ve `lang="tr-TR"`
değerlerini eşler, `lang="trk"` değerini eşlemez. Bu, dil etiketlerinin tire ile
bölümlenmiş yapısına göre tasarlanmış bir eşlemedir. Çıktının son satırında aynı biçimin
veri özniteliğinde de işlediği görülüyor: `data-birim="derece"` ve `data-birim="derece-C"`
birlikte eşleşti.

#### Sınıf Seçicisi Bir Öznitelik Seçicisidir

Çıktıdaki `~=` davranışı tanıdık gelmelidir. `class` özniteliği de boşlukla ayrılmış bir
listedir ve sınıf seçicisi tam olarak liste üyeliğini sorar. İki yazım eşdeğerdir:

```css
.eksik        { color: #8a1c1c; }
[class~="eksik"] { color: #8a1c1c; }
```

İkinci yazım pratikte kullanılmaz, ama bir kavramı yerine oturtur: sınıf, dilin ayrıcalıklı
bir kavramı değil, bir özniteliğin üzerine konmuş bir kısayoldur. Aynı biçimde `#icerik`
seçicisi de `[id="icerik"]` ile aynı kümeyi döndürür — özgüllük hesabındaki ağırlıkları
farklı olsa da eşleme davranışları aynıdır.

#### Harf Duyarlılığı ve Boş Değer

Çıktının son iki satırı bir tuzağı gösteriyor. `[type="number"]` seçicisi, değeri `NUMBER`
olan bir ögeyle eşleşmedi. Öznitelik değerleri genel kural olarak harf duyarlıdır; bazı
HTML öznitelikleri için tanımlı bir duyarsızlık vardır, ancak buna bel bağlamak yerine
işaretle açıkça bildirmek daha güvenlidir:

```css
input[type="number" i] { text-align: right; }
```

Kapanış parantezinden önce yazılan `i` işareti eşlemeyi harf duyarsız yapar. `s` işareti
tersini, açıkça duyarlı eşlemeyi ister.

İkinci tuzak boş dizgidir. Programdaki üç alt dizgi biçiminde `d !== ""` denetimi
bulunuyor ve bu, dilin kuralını yansıtır: `[href*=""]` yazımı **hiçbir** ögeyle eşleşmez.
Sezgi tersini söyler — her dizgi boş dizgiyi içerir — ama tanım bu durumu dışarıda
bırakır. Bir değeri dinamik olarak seçiciye yerleştiren bir üretim varsa, boş değer
durumunda kuralın sessizce ölmesi beklenmelidir.

#### Öznitelik Seçicisi Ne Zaman Doğru Araçtır

Bir sınıf eklemek her zaman mümkündür; öyleyse öznitelik seçicisi neden gereksin?

Öznitelik seçicisi, ayrımın **zaten belgede** olduğu durumlarda doğru araçtır. Bir form
alanının türü `type` özniteliğinde yazılıdır; ona ayrıca `class="sayi-alani"` eklemek aynı
bilgiyi iki yere yazmak ve ikisini eşzamanlı tutma yükümlülüğü almaktır. İki kaynak
ayrıştığında hangisinin doğru olduğu belirsizleşir.

Aynı gerekçe zorunluluk durumu için de geçerlidir:

```css
/* istasyon.css — 4. adım: form alanları ve dış bağlantılar */
input[type="number"]  { text-align: right; }
input[required]       { border-inline-start: 3px solid #8a5b00; }
a[href^="http"]       { padding-inline-end: 1em; }
td[data-birim|="derece"] { font-variant-numeric: tabular-nums; }
```

İkinci kural, zorunlu alanları belgedeki `required` özniteliğinden okur. Alan zorunlu
olmaktan çıkarıldığında öznitelik silinir ve biçem kendiliğinden düşer; hiçbir sınıf
güncellemesi gerekmez. Doğrulama davranışı ile görsel işaret aynı tek kaynaktan beslenir.

Üçüncü kuralın gerekçesi farklıdır: dış bağlantılara yer açar. Adresin `http` ile başlaması
bir yazar kararı değil, adresin kendi biçimidir; onu ayrıca sınıfla bildirmek gereksiz
tekrar olurdu.

Sınırı da söylemek gerekir. Öznitelik seçicisi, ayrım belgede yoksa uydurulamaz. "Bu
paragraf uyarıdır" bilgisi hiçbir özniteliğe yazılı değilse, onu taşıyacak yer bir sınıf
adıdır.

#### Özet

- Öznitelik seçicisi köşeli parantezle yazılır; en basit biçimi özniteliğin varlığını
  sorar, kalan altı biçim değeri sınar.
- `~=` değeri boşluklardan bölüp liste üyeliği arar, `|=` tire ile bölümlenmiş kodun
  önekini eşler; `^= $= *=` ise değeri yapısız bir metin gibi ele alır.
- Sınıf seçicisi `[class~="…"]` ile, kimlik seçicisi `[id="…"]` ile aynı kümeyi döndürür;
  ikisi de öznitelik eşlemesinin kısayoludur.
- Öznitelik değerleri harf duyarlı eşlenir; duyarsızlık `i` işaretiyle açıkça istenir.
  Boş dizgiyle yazılan alt dizgi eşlemeleri hiçbir ögeyle eşleşmez.
- Ayrım belgede zaten bir öznitelikte duruyorsa öznitelik seçicisi yazılır; aynı bilgiyi
  bir de sınıfa yazmak iki kaynağı eşzamanlı tutma yükümlülüğü getirir.

#### Sonraki Adım

Seçicilerin tümü şimdiye kadar belgede yazılı olan bir şeye baktı: ad, sınıf, kimlik,
öznitelik, konum. Belgede yazılı olmayan şeyler de vardır — bir bağlantının üzerinde durulup
durulmadığı, bir alanın odakta olup olmadığı, bir satırın kaçıncı sırada bulunduğu. Sonraki
ders bu durumları seçen sözde sınıfları ve belgede hiç bulunmayan içeriği üreten sözde
elemanları tanımlar.

### Sözde Sınıflar ve Sözde Elemanlar

Source: https://academia.sh/tr/kurslar/css-temelleri/sozde-siniflar-ve-sozde-elemanlar

Belgede yazılı olmayan durumları seçen sözde sınıflar, nth-child aritmetiği, mantıksal seçiciler ve ağaçta bulunmayan içerik üreten sözde elemanlar.

Şimdiye kadarki bütün seçiciler belgede yazılı olan bir şeye baktı: öge adı, sınıf, kimlik,
öznitelik, ağaçtaki konum. Belgede yazılı olmayan bilgiler de vardır. Bir bağlantının
üzerinde durulup durulmadığı, bir form alanının odakta olup olmadığı, bir satırın kaçıncı
sırada bulunduğu — bunların hiçbiri işaretlemede geçmez, çünkü hiçbiri belgenin bir
özelliği değildir.

Bu ders iki mekanizmayı tanımlar: belgede yazılı olmayan **durumları** seçen sözde
sınıfları ve belge ağacında hiç bulunmayan **parçaları** hedefleyen sözde elemanları.
İkisi farklı işler görür ve yazımdaki tek fark iki nokta üst üstenin sayısıdır.

#### Sözde Sınıf Bir Durumu Seçer

**Sözde sınıf (pseudo-class)** tek iki nokta üst üsteyle yazılır ve seçiciye, ögenin o
andaki durumuna ilişkin bir koşul ekler. İki aileye ayrılır.

Birinci aile **etkileşim durumunu** sorar: `:hover` işaretleyici ögenin üzerindeyken,
`:focus` öge klavye odağını taşırken, `:active` öge etkinleştirilirken, `:disabled` bir
denetim devre dışıyken, `:checked` bir seçenek işaretliyken, `:invalid` bir alanın değeri
kısıtları sağlamıyorken eşleşir. Bu durumların hiçbiri belgede yazılı değildir; kullanıcı
etkileşimi ya da doğrulama sonucudur.

İkinci aile **ağaçtaki konumu** sorar: `:first-child`, `:last-child`, `:only-child`,
`:empty`, `:root` ve sayılı bir ailenin başı olan `:nth-child()`.

#### nth-child Bir Aritmetik İfadedir

`:nth-child()` bir sayı değil, bir **formül** alır: `an+b`. Burada `n` sıfırdan başlayarak
artan bir tam sayıdır; formülün ürettiği ve 1 ile kardeş sayısı arasında kalan her değer
bir sıraya karşılık gelir.

Aşağıdaki program bu formülü çözer ve dokuz satırlık bir ölçüm çizelgesinde hangi sıraların
eşleştiğini listeler.

```js
// nth-hesap.mjs — :nth-child(an+b) formulunu cozer ve eslesen siralari listeler
function ayristir(ifade) {
  const s = ifade.replace(/\s+/g, "").toLowerCase();
  if (s === "odd")  return { a: 2, b: 1 };
  if (s === "even") return { a: 2, b: 0 };
  const m = s.match(/^([+-]?\d*)n([+-]\d+)?$/);
  if (!m) return { a: 0, b: Number(s) };
  const aMetin = m[1];
  const a = aMetin === "" || aMetin === "+" ? 1 : aMetin === "-" ? -1 : Number(aMetin);
  return { a, b: m[2] ? Number(m[2]) : 0 };
}

// n = 0,1,2,... icin an+b degeri; 1 <= sira <= toplam olanlar eslesir
function eslesenler(ifade, toplam) {
  const { a, b } = ayristir(ifade);
  const c = [];
  for (let sira = 1; sira <= toplam; sira++) {
    if (a === 0) { if (sira === b) c.push(sira); continue; }
    const n = (sira - b) / a;
    if (Number.isInteger(n) && n >= 0) c.push(sira);
  }
  return { a, b, eslesen: c };
}

const TOPLAM = 9; // olcum cizelgesinde dokuz satir
for (const ifade of ["odd", "even", "3", "2n+1", "3n", "3n+1", "-n+3", "n+4", "5n-2"]) {
  const { a, b, eslesen } = eslesenler(ifade, TOPLAM);
  const formul = `${a}n${b >= 0 ? "+" : ""}${b}`;
  console.log(`:nth-child(${ifade.padEnd(5)}) = ${formul.padEnd(7)} -> ${eslesen.join(" ") || "(bos)"}`);
}
```

```
:nth-child(odd  ) = 2n+1    -> 1 3 5 7 9
:nth-child(even ) = 2n+0    -> 2 4 6 8
:nth-child(3    ) = 0n+3    -> 3
:nth-child(2n+1 ) = 2n+1    -> 1 3 5 7 9
:nth-child(3n   ) = 3n+0    -> 3 6 9
:nth-child(3n+1 ) = 3n+1    -> 1 4 7
:nth-child(-n+3 ) = -1n+3   -> 1 2 3
:nth-child(n+4  ) = 1n+4    -> 4 5 6 7 8 9
:nth-child(5n-2 ) = 5n-2    -> 3 8
```

Üç satır ayrıca okunmalıdır. `odd` ve `even` anahtar sözcükleri `2n+1` ve `2n` formüllerinin
adlarıdır; ayrı bir mekanizma değildir. Negatif katsayılı `-n+3` "ilk üç" anlamına gelir:
`n` büyüdükçe değer küçülür ve 1'in altına düştüğünde eşleşme biter. Karşıtı olan `n+4`
ise "dördüncüden sonuna kadar" demektir. Bu ikisi, bir aralığı seçmenin yoludur.

Bir uyarı: `:nth-child()` **bütün kardeşleri** sayar, türe bakmaz. Bir bölümde bir başlık
ve ardından paragraflar varsa, ilk paragraf `p:nth-child(1)` ile eşleşmez — o sırada başlık
durur. Türe göre saymak gerekiyorsa `:nth-of-type()` yazılır; bu, yalnızca aynı öge adını
taşıyan kardeşleri sayar.

#### Mantıksal Seçiciler

Dört sözde sınıf, seçici listesi alır ve mantıksal işlem yapar.

`:is()` içindeki listeden **herhangi biriyle** eşleşen ögeleri döndürür; uzun seçicileri
kısaltır. `:where()` aynı eşlemeyi yapar ama özgüllüğe **sıfır** katkı verir — bu ayrımın
neden önemli olduğunu bir sonraki derste hesapla göreceğiz.

```css
:is(h1, h2, h3) + p { margin-block-start: 0.5em; }
```

Bu kural, üç ayrı kural yazmakla aynı ögeleri seçer.

`:not()` listedeki hiçbiriyle eşleşmeyen ögeleri döndürür:

```css
.olcum-cizelgesi td:not(.ad) { text-align: right; }
```

`:has()` ise yönü tersine çevirir: bir ögeyi, **içeriğine** göre seçer. `tr:has(td.eksik)`
seçicisi, içinde eksik hücre bulunan satırları döndürür. Bu, birleştiricilerle
yapılamayan bir şeydir; birleştiriciler yalnızca sağdaki ögeyi seçebiliyordu.

Bir seçicinin gerçekleştirimlerce tanınıp tanınmadığı sınanabilir. Önceki derslerde
tanınmayan bir seçicinin kuralın tamamını düşürdüğünü görmüştük; bu, sınamanın da yolunu
verir:

```css
@supports selector(:has(*)) {
  tr:has(td.eksik) { background-color: #fbf4f4; }
}
```

`@supports` bloğu, içindeki koşul sağlanmazsa bloğun tamamını yok sayar. Özellik saptama
yazmak, bir özelliğin desteklendiğini varsaymaktan farklıdır: koşul sağlanmadığında
sayfanın hangi hâlde kalacağı yazar tarafından belirlenmiş olur.

#### Sözde Eleman Bir Parçayı Hedefler

**Sözde eleman (pseudo-element)** çift iki nokta üst üsteyle yazılır ve belge ağacında
bulunmayan bir parçayı hedefler.

`::first-line` bir paragrafın ilk satırını, `::first-letter` ilk harfini seçer. Bu ikisi
belgede öge olarak yoktur; satır bölünmesi yerleşim sonucunda oluşur ve kutunun genişliği
değişince ilk satırın kapsamı da değişir.

`::marker` liste madde işaretini, `::placeholder` bir form alanının yer tutucu metnini,
`::selection` kullanıcının seçtiği metin aralığını hedefler.

`::before` ve `::after` ise farklıdır: bir parçayı seçmezler, **üretirler**. İkisi de
`content` özelliği yazılmadan var olmaz.

```css
td.eksik::after { content: " —"; }
```

Bu kural, eksik ölçüm hücrelerinin içeriğinin sonuna bir tire ekler. Üretilen içerik ögenin
içindedir, dışında değil: `::before` ögenin ilk çocuğu, `::after` son çocuğu gibi davranır.

Üretilmiş içeriğin iki sınırı vardır ve ikisi de tasarım gereğidir.

Birincisi, **belgede yoktur**. Metin olarak seçilip kopyalanması, arama motoru tarafından
dizinlenmesi ya da yardımcı teknolojiye aktarılması güvence altında değildir. Bu yüzden
anlam taşıyan hiçbir metin buraya yazılmaz. "Eksik" bilgisi belgede bir sınıf ve gerekiyorsa
görünür bir metinle bildirilmeli; sözde eleman yalnızca görsel işaret eklemelidir.

İkincisi, **seçilemez**. Bir sözde elemanın içinde başka bir seçici çalışmaz; üretilen
içeriğin kendi alt yapısı yoktur.

Yazım kuralına ilişkin bir not: çift iki nokta yazımı sözde elemanı sözde sınıftan ayırmak
için tanımlanmıştır. Eski gerçekleştirimlerle uyum için `:before` yazımı da kabul edilir;
ayrımı korumak adına bu kursta çift iki nokta kullanılır.

#### İstasyon Sayfasının Durum Biçemleri

```css
/* istasyon.css — 5. adım: durumlar ve sıralar */
.olcum-cizelgesi tbody tr:nth-child(odd) { background-color: #f5f7f8; }
.olcum-cizelgesi tbody tr:last-child     { border-block-end: none; }
.olcum-cizelgesi td:not(.ad)             { text-align: right; }
.olcum-cizelgesi td.eksik::after         { content: " —"; color: #8a5b00; }

a:hover                { text-decoration-thickness: 2px; }
a:focus-visible        { outline: 3px solid #143a52; outline-offset: 2px; }
input:invalid          { border-color: #8a1c1c; }
input:disabled         { color: #6b767f; }
```

İkinci grubun ilk iki kuralı bir ayrımı taşır. `:hover` yalnızca işaretleyici aygıtlarda
oluşan bir durumdur; klavyeyle gezen bir kullanıcı hiçbir zaman ona ulaşmaz. Bu yüzden
`:hover` ile verilen her ipucunun bir odak karşılığı olmalıdır.

`:focus-visible`, `:focus` yerine kullanılır çünkü ikisi farklı kümeler döndürür: `:focus`
bir alana işaretleyiciyle tıklandığında da eşleşir, `:focus-visible` odak göstergesinin
gerekli olduğu durumlarda eşleşir. Odak çerçevesini büsbütün kaldırmak
(`outline: none` yazmak) klavyeyle gezinmeyi olanaksızlaştırır; çerçeve kaldırılacaksa
yerine görünür bir başkası konmalıdır.

#### Özet

- Sözde sınıf tek iki nokta üst üsteyle yazılır ve belgede yazılı olmayan bir durumu
  seçer; iki aileye ayrılır — etkileşim durumu ve ağaçtaki konum.
- `:nth-child()` bir `an+b` formülü alır; `n` sıfırdan başlar ve formülün ürettiği geçerli
  sıralar eşleşir. `odd` ve `even` bu formülün adlandırılmış hâlleridir.
- `:nth-child()` bütün kardeşleri sayar, `:nth-of-type()` yalnızca aynı türdekileri;
  aradaki fark başlık gibi araya giren ögelerde ortaya çıkar.
- `:is()`, `:where()`, `:not()` ve `:has()` seçici listesi alır; `:has()` bir ögeyi
  içeriğine göre seçer ve birleştiricilerin ters yönünü sağlar.
- Sözde eleman çift iki nokta ile yazılır; `::before` ve `::after` belgede bulunmayan
  içerik üretir. Bu içerik metin olarak güvenilir biçimde erişilebilir değildir, bu yüzden
  yalnızca görsel işaret taşır.

#### Sonraki Adım

Artık aynı ögeye birden çok kural uyabiliyor: `td` bir kuralla, `.eksik` başka bir kuralla,
`.olcum-cizelgesi td:not(.ad)` bir üçüncüsüyle eşleşiyor. Üçü de `text-align` özelliğine
yazarsa hangisi kazanır? Sonraki ders bu soruyu bir sezgiyle değil, sayılabilir bir
sıralamayla yanıtlar: özgüllük üçlüsünü hesaplar ve basamaklamanın adımlarını sırayla
uygular.

### Basamaklama ve Özgüllük

Source: https://academia.sh/tr/kurslar/css-temelleri/basamaklama-ve-ozgulluk

Aynı özelliğe yazan bildirimlerin kaynak, önem, özgüllük ve sıra ölçütleriyle sıralanması; özgüllük üçlüsünün hesabı ve onluk tabanda okunamayacağı.

Önceki derslerin sonunda aynı ögeye birden çok kural uyar hâle geldi. Bir hücre `td`
seçicisiyle, `.olcum-cizelgesi td` seçicisiyle, `td.eksik` seçicisiyle ve `#icerik td`
seçicisiyle birden eşleşiyor. Dördü de `text-align` özelliğine yazarsa ögenin hizalaması ne
olur?

Bu sorunun yanıtı bir sezgi değil, tanımlı bir sıralamadır. Sıralamanın adı
**basamaklama (cascade)**'dır ve dilin adındaki ilk harf ondan gelir. Bu ders
basamaklamanın adımlarını sırayla uygular ve her adımı hesaplanabilir kılar.

#### Basamaklama Bildirimleri Sıralar

Önemli bir ayrımla başlamak gerekir: basamaklama kuralları değil, **bildirimleri** sıralar.
Sıralama tek bir öge ve tek bir özellik için yapılır. Aynı kuralın iki bildirimi ayrı ayrı
yarışır; birinin kazanması diğerinin de kazanacağı anlamına gelmez.

Bir öge–özellik çifti için geçerli bildirimler toplandıktan sonra dört ölçüt sırayla
uygulanır. Önceki ölçütte bir kazanan çıkarsa sonraki ölçüte bakılmaz.

1. **Kaynak ve önem.** Bildirimin nereden geldiği ve `!important` taşıyıp taşımadığı.
2. **Satır içi olması.** Ögenin `style` özniteliğinden gelen bildirimler öne geçer.
3. **Özgüllük.** Seçicinin ne kadar dar bir küme hedeflediği.
4. **Sıra.** Aynı özgüllükteki bildirimlerden sonra yazılan kazanır.

Kaynak üç tanedir: tarayıcının kendi varsayılan biçemi, kullanıcının tanımladığı biçem ve
belgenin yazarının yazdığı biçem. Bu üçünün normal sıralaması zayıftan güçlüye şöyledir:
tarayıcı, kullanıcı, yazar.

#### Özgüllük Bir Üçlüdür

Özgüllük tek bir sayı değil, üç sayıdan oluşan sıralı bir üçlüdür: $(a, b, c)$.

- $a$: seçicideki kimlik seçicilerinin sayısı.
- $b$: sınıf seçicileri, öznitelik seçicileri ve sözde sınıfların toplam sayısı.
- $c$: tür seçicileri ve sözde elemanların toplam sayısı.

Evrensel seçici `*` ve birleştiriciler hiçbir sütuna katkı vermez.

Aşağıdaki program bir seçici dizesini bu üçlüye çevirir.

```js
// ozgulluk.mjs — bir secici dizesini (a, b, c) uclusune cevirir
function ozgulluk(secici) {
  let s = secici;
  let a = 0, b = 0, c = 0;

  // :where(...) ozgullige katki vermez -> once cikar
  s = s.replace(/:where\([^()]*\)/g, "");

  // :is( ) / :not( ) / :has( ) -> icindeki en yuksek ozgullugu devral
  const kapsayan = /:(?:is|not|has)\(([^()]*)\)/g;
  let m;
  while ((m = kapsayan.exec(s)) !== null) {
    const enYuksek = m[1]
      .split(",")
      .map((p) => ozgulluk(p.trim()))
      .reduce((en, o) => (karsilastir(o, en) > 0 ? o : en), [0, 0, 0]);
    a += enYuksek[0]; b += enYuksek[1]; c += enYuksek[2];
  }
  s = s.replace(kapsayan, "");

  // sozde elemanlar (::x) -> c; sozde siniflar (:x) -> b
  const sozdeEleman = s.match(/::[a-z-]+/g) ?? [];
  c += sozdeEleman.length;
  s = s.replace(/::[a-z-]+/g, "");

  a += (s.match(/#[\w-]+/g) ?? []).length;
  s = s.replace(/#[\w-]+/g, "");

  b += (s.match(/\.[\w-]+/g) ?? []).length;      // sinif
  s = s.replace(/\.[\w-]+/g, "");
  b += (s.match(/\[[^\]]*\]/g) ?? []).length;    // oznitelik
  s = s.replace(/\[[^\]]*\]/g, "");
  b += (s.match(/:[a-z-]+(\([^()]*\))?/g) ?? []).length; // sozde sinif
  s = s.replace(/:[a-z-]+(\([^()]*\))?/g, "");

  c += (s.match(/[a-z][\w-]*/gi) ?? []).length;  // tur secici (* sayilmaz)
  return [a, b, c];
}

const karsilastir = (x, y) => x[0] - y[0] || x[1] - y[1] || x[2] - y[2];

const seciciler = [
  "*",
  "td",
  ".olcum-cizelgesi td",
  "td.eksik",
  "tbody > tr > td",
  ".olcum-cizelgesi tbody td:not(.ad)",
  "input[type=\"number\"]",
  "#icerik td",
  ".olcum-cizelgesi td.deger.eksik",
  "td.eksik::after",
  ":where(.olcum-cizelgesi) td",
  ":is(#icerik, .yan) p",
  "tr:has(td.eksik)",
];

for (const s of seciciler) {
  const [a, b, c] = ozgulluk(s);
  console.log(`${a},${b},${c}   ${s}`);
}
```

```
0,0,0   *
0,0,1   td
0,1,1   .olcum-cizelgesi td
0,1,1   td.eksik
0,0,3   tbody > tr > td
0,2,2   .olcum-cizelgesi tbody td:not(.ad)
0,1,1   input[type="number"]
1,0,1   #icerik td
0,3,1   .olcum-cizelgesi td.deger.eksik
0,1,2   td.eksik::after
0,0,1   :where(.olcum-cizelgesi) td
1,0,1   :is(#icerik, .yan) p
0,1,2   tr:has(td.eksik)
```

Beş satır ayrıca okunmalıdır.

`.olcum-cizelgesi td` ile `td.eksik` **aynı** üçlüyü verdi: her ikisinde bir sınıf ve bir
tür var. Seçicilerin uzunluğu ya da ne kadar "kesin göründüğü" hesaba girmez; yalnızca
sayılar girer.

`:not()`, `:is()` ve `:has()` kendileri hesaba katılmaz; içlerindeki listenin **en yüksek
özgüllüklü** üyesini devralırlar. `:is(#icerik, .yan) p` bu yüzden $1,0,1$ verdi — listede
bir kimlik seçicisi bulunduğu için.

`:where()` ise sıfır katkı verir. `:where(.olcum-cizelgesi) td` üçlüsü, yalnızca `td`
yazılmış gibi $0,0,1$'dir. Bu, bir seçiciyi daraltırken özgüllüğü artırmama yoludur.

Sözde eleman üçüncü sütuna girer: `td.eksik::after` üçlüsünde $c = 2$'dir.

#### Üçlü Onluk Tabanda Bir Sayı Değildir

Sık yapılan bir yanlış, üçlüyü yüzler–onlar–birler basamağı gibi okumaktır. Bu okuma on ve
üzeri sayılarda bozulur, çünkü sütunlar arasında elde taşınmaz. Karşılaştırma sözlük
sırasıdır: önce $a$, eşitse $b$, eşitse $c$.

```js
// onluk-degil.mjs — ozgulluk uclusu onluk tabanda bir sayi degildir
const karsilastir = (x, y) => x[0] - y[0] || x[1] - y[1] || x[2] - y[2];
const ciftler = [
  [[0, 11, 0], [1, 0, 0]],
  [[0, 0, 12], [0, 1, 0]],
  [[1, 0, 0], [0, 99, 99]],
];
for (const [x, y] of ciftler) {
  const s = karsilastir(x, y);
  const kazanan = s > 0 ? x : s < 0 ? y : "esit";
  console.log(`${x.join(",")} vs ${y.join(",")} -> kazanan ${Array.isArray(kazanan) ? kazanan.join(",") : kazanan}`);
  console.log(`   onluk okunsaydi: ${x[0]*100+x[1]*10+x[2]} vs ${y[0]*100+y[1]*10+y[2]}`);
}
```

```
0,11,0 vs 1,0,0 -> kazanan 1,0,0
   onluk okunsaydi: 110 vs 100
0,0,12 vs 0,1,0 -> kazanan 0,1,0
   onluk okunsaydi: 12 vs 10
1,0,0 vs 0,99,99 -> kazanan 1,0,0
   onluk okunsaydi: 100 vs 1089
```

İlk satır kritiktir: on bir sınıf taşıyan bir seçici, tek bir kimlik seçicisini yenemez.
Onluk okuma tersini söylerdi. Sonuç şudur: bir kimlik seçicisiyle yazılmış bildirimi
bastırmanın tek yolu, yine bir kimlik seçicisi yazmaktır. Önceki derste "biçem sınıfla
yazılır" kuralının gerekçesi budur.

#### Basamaklamanın Uygulanışı

Aşağıdaki program yedi bildirimi tam sıralamaya sokar. Kaynak katmanları zayıftan güçlüye
numaralanmıştır; sıralama ölçütleri sırayla uygulanır.

```js
// basamaklama.mjs — ayni ozellige yazan bildirimleri siralar ve kazanani bulur
// (ozgulluk hesabi onceki programdan; burada elle verilmistir)

// katman sirasi: kucuk sayi = zayif
const KATMAN = {
  "tarayici-normal": 0,
  "kullanici-normal": 1,
  "yazar-normal": 2,
  "yazar-satirici": 3,
  "yazar-onemli": 4,
  "kullanici-onemli": 5,
  "tarayici-onemli": 6,
};

const bildirimler = [
  { kaynak: "tarayici-normal", secici: "td",                       ozgulluk: [0,0,1], sira: 1, deger: "left"   },
  { kaynak: "yazar-normal",    secici: "td",                       ozgulluk: [0,0,1], sira: 2, deger: "left"   },
  { kaynak: "yazar-normal",    secici: ".olcum-cizelgesi td",      ozgulluk: [0,1,1], sira: 3, deger: "right"  },
  { kaynak: "yazar-normal",    secici: "td.ad",                    ozgulluk: [0,1,1], sira: 4, deger: "start"  },
  { kaynak: "yazar-normal",    secici: "#icerik td",               ozgulluk: [1,0,1], sira: 5, deger: "center" },
  { kaynak: "yazar-onemli",    secici: ".olcum-cizelgesi td.eksik",ozgulluk: [0,2,1], sira: 6, deger: "end"    },
  { kaynak: "kullanici-onemli",secici: "td",                       ozgulluk: [0,0,1], sira: 7, deger: "justify"},
];

const karsilastir = (x, y) =>
  KATMAN[x.kaynak] - KATMAN[y.kaynak] ||
  x.ozgulluk[0] - y.ozgulluk[0] ||
  x.ozgulluk[1] - y.ozgulluk[1] ||
  x.ozgulluk[2] - y.ozgulluk[2] ||
  x.sira - y.sira;

function coz(aday) {
  const sirali = [...aday].sort(karsilastir);
  console.log("zayiftan gucluye:");
  for (const b of sirali) {
    const o = b.ozgulluk.join(",");
    console.log(`  ${b.kaynak.padEnd(17)} ${o}  sira=${b.sira}  ${b.secici.padEnd(28)} -> ${b.deger}`);
  }
  console.log(`kazanan: text-align: ${sirali.at(-1).deger}`);
  console.log("");
}

console.log("=== tum bildirimler ===");
coz(bildirimler);

console.log("=== yalnizca yazar-normal ===");
coz(bildirimler.filter((b) => b.kaynak === "yazar-normal"));

console.log("=== ozgulluk esit, sira belirler ===");
coz(bildirimler.filter((b) => b.kaynak === "yazar-normal" && b.ozgulluk.join() === "0,1,1"));
```

```
=== tum bildirimler ===
zayiftan gucluye:
  tarayici-normal   0,0,1  sira=1  td                           -> left
  yazar-normal      0,0,1  sira=2  td                           -> left
  yazar-normal      0,1,1  sira=3  .olcum-cizelgesi td          -> right
  yazar-normal      0,1,1  sira=4  td.ad                        -> start
  yazar-normal      1,0,1  sira=5  #icerik td                   -> center
  yazar-onemli      0,2,1  sira=6  .olcum-cizelgesi td.eksik    -> end
  kullanici-onemli  0,0,1  sira=7  td                           -> justify
kazanan: text-align: justify

=== yalnizca yazar-normal ===
zayiftan gucluye:
  yazar-normal      0,0,1  sira=2  td                           -> left
  yazar-normal      0,1,1  sira=3  .olcum-cizelgesi td          -> right
  yazar-normal      0,1,1  sira=4  td.ad                        -> start
  yazar-normal      1,0,1  sira=5  #icerik td                   -> center
kazanan: text-align: center

=== ozgulluk esit, sira belirler ===
zayiftan gucluye:
  yazar-normal      0,1,1  sira=3  .olcum-cizelgesi td          -> right
  yazar-normal      0,1,1  sira=4  td.ad                        -> start
kazanan: text-align: start
```

İkinci çalıştırma, ölçütlerin **sırayla** uygulandığını gösterir: tek bir kaynakta
kalındığında kazanan özgüllüğe göre belirlendi. Üçüncü çalıştırmada özgüllük de eşitti ve
karar yalnızca yazım sırasına kaldı; sonra yazılan kazandı.

#### Önem İşareti Sıralamayı Tersine Çevirir

Bir bildirimin sonuna `!important` yazıldığında bildirim ayrı bir katmana taşınır. Katman
listesindeki sıra dikkatle okunmalıdır: önemli bildirimlerde kaynak sıralaması **ters
döner**. Normal bildirimlerde yazar kullanıcıyı yener; önemli bildirimlerde kullanıcı yazarı
yener.

Bu ters çevirme bir kaza değil, bir tasarım kararıdır. Bir kullanıcının kendi
gereksinimlerini karşılamak üzere yazdığı biçem — yazı boyutunu büyütmek, kontrastı
artırmak, hareketi durdurmak — sayfa yazarının kararlarını geçebilmelidir. Çıktının ilk
bölümünde `kullanici-onemli` katmanının en üstte yer alması bunu gösteriyor.

Yazar tarafında `!important` yazmanın bedeli, kendi biçem dosyasında bir istisna katmanı
açmaktır. Bir kez yazıldığında onu bastırmanın tek yolu yine `!important` yazmaktır ve o
noktada özgüllük hesabı yeniden başlar. Uygulanabilir kural şudur: `!important`, bir çakışma
çözülemediği için değil, çözümü aranmadığı için yazılır; önce özgüllüğü düşürmek denenir.

Kaynak ve önemin arasına yerleşen bir mekanizma daha vardır: `@layer` ile tanımlanan
**basamaklama katmanları**. Yazar biçemi içinde adlandırılmış katmanlar açar ve katman
sırası özgüllükten **önce** değerlendirilir; böylece bir taban katmanının tamamı, özgüllüğü
ne olursa olsun, üstteki katman tarafından bastırılabilir.

#### Özgüllüğü Düşük Tutmak

Bu dersin pratik sonucu bir yazım disiplinidir.

- Seçiciler olabildiğince **düz** tutulur. `.olcum-cizelgesi tbody tr td.deger` yerine
  `.olcum-cizelgesi .deger` yazmak, aynı ögeye daha düşük bir üçlüyle ulaşır.
- Biçemde kimlik seçicisi kullanılmaz; ilk sütunu bir kez açan bir kural, sonraki bütün
  düzeltmeleri kimlik yazmaya zorlar.
- Bir seçiciyi bağlamla daraltmak gerekiyorsa `:where()` yazılır; daraltma yapılır,
  özgüllük artmaz.

```css
/* istasyon.css — 6. adım: özgüllüğü düz tutan yazım */
:where(.olcum-cizelgesi) .deger { text-align: right; }   /* 0,1,0 */
:where(.olcum-cizelgesi) .ad    { text-align: start; }   /* 0,1,0 */
.eksik                          { color: #8a1c1c; }      /* 0,1,0 */
```

Üç kural da aynı üçlüyü taşır. Bu, aralarındaki çakışmaların yalnızca yazım sırasıyla
çözüleceği anlamına gelir — okunması ve öngörülmesi en kolay durum budur.

#### Özet

- Basamaklama kuralları değil bildirimleri sıralar; sıralama tek bir öge ve tek bir özellik
  için yapılır.
- Ölçütler sırayla uygulanır: kaynak ve önem, satır içi olma, özgüllük, yazım sırası.
  Önceki ölçüt bir kazanan çıkarırsa sonrakine bakılmaz.
- Özgüllük $(a, b, c)$ üçlüsüdür: kimlik sayısı, sınıf–öznitelik–sözde sınıf sayısı,
  tür–sözde eleman sayısı. Karşılaştırma sözlük sırasıdır; sütunlar arasında elde taşınmaz.
- `:is()`, `:not()` ve `:has()` içindeki en yüksek özgüllüğü devralır; `:where()` sıfır
  katkı verir ve daraltmayı özgüllük artırmadan yapmayı sağlar.
- `!important` bildirimi ayrı bir katmana taşır ve kaynak sıralamasını tersine çevirir;
  bu, kullanıcı biçeminin yazar kararlarını geçebilmesi için tanımlanmıştır.

#### Sonraki Adım

Basamaklama, bir ögeye **uyan** bildirimler arasından kazananı seçiyor. Peki hiçbir bildirim
uymuyorsa? Bir paragrafa hiçbir kural `color` yazmamışsa o paragrafın rengi ne olur? Sonraki
ders bu boşluğun iki ayrı mekanizmayla doldurulduğunu gösterir — kalıtım ve başlangıç değeri
— ve bu ikisini açıkça çağıran anahtar sözcükleri tanımlar.

### Kalıtım ve Varsayılan Değerler

Source: https://academia.sh/tr/kurslar/css-temelleri/kalitim-ve-varsayilan-degerler

Hiçbir bildirimin ulaşmadığı bir özelliğin değerinin nasıl belirlendiği; kalıtılan ve kalıtılmayan özellikler, başlangıç değeri ve inherit, initial, unset, revert anahtar sözcükleri.

Basamaklama, bir ögeye uyan bildirimler arasından kazananı seçiyor. Bir sorun kaldı: hiçbir
bildirim uymuyorsa ne olur? Ölçüm çizelgesindeki bir hücreye hiçbir kural `color`
yazmamışsa o hücrenin metin rengi nedir?

Boşluk iki ayrı mekanizmayla doldurulur. Bazı özellikler değerlerini üst ögeden **devralır**;
kalanlar tanımlı bir **başlangıç değerine** düşer. Bu ders iki mekanizmayı ayırır, hangi
özelliğin hangisine tabi olduğunu gerekçelendirir ve ikisini açıkça çağıran anahtar
sözcükleri tanımlar.

#### Kalıtım Ağaçta Aşağı Doğru İşler

**Kalıtım (inheritance)**, bir özelliğin değerinin belge ağacında üst ögeden alt ögeye
geçmesidir. Her özellik kalıtılmaz; hangi özelliğin kalıtıldığı özellik tanımının bir
parçasıdır ve rastgele değildir.

Kabaca: **metnin görünümünü belirleyen** özellikler kalıtılır — `color`, `font-family`,
`font-size`, `font-weight`, `line-height`, `text-align`, `letter-spacing`, `visibility`.
**Kutunun geometrisini belirleyen** özellikler kalıtılmaz — `margin`, `padding`, `border`,
`width`, `height`, `background`, `display`, `position`.

Ayrımın gerekçesi kullanışlılıktır. Bir bölümün yazı tipini bir kez ayarlayıp içindeki
bütün metnin onu almasını istemek olağandır; bir bölüme kenarlık verip içindeki her
paragrafın da kenarlık almasını istemek olağan değildir. Kalıtım, tekrarı olağan olan
kararlarda açıktır.

Kalıtılan değer, üst ögenin **hesaplanmış** değeridir; kaynakta yazılan metin değil.
Bir üst ögede `font-size: 150%` yazılıysa, çocuğa geçen şey `150%` değil, o yüzdenin
çözülmüş sonucudur. Bu ayrım birimler dersinde sayıyla ele alınacak.

#### Başlangıç Değeri Tarayıcı Varsayılanı Değildir

Kalıtılmayan bir özelliğe hiçbir bildirim ulaşmazsa **başlangıç değeri (initial value)**
kullanılır. Bu değer, özelliğin tanımında yazılıdır ve gerçekleştirimden bağımsızdır.

Burada sık karıştırılan bir ayrım vardır. `div` ögesinin blok kutu üretmesi, `display`
özelliğinin başlangıç değerinden gelmez — `display` özelliğinin başlangıç değeri `inline`
'dır. `div` ögesinin blok olması, tarayıcının kendi varsayılan biçem dosyasında
`div { display: block }` yazdığı içindir.

Ayrım şöyle özetlenir: **başlangıç değeri dilin tanımından, tarayıcı varsayılanı bir biçem
dosyasından gelir.** Birincisi hiçbir kural yazılmadığında değil, hiçbir kural **kazanmadığında**
devreye girer; ikincisi ise kazanabilecek bir bildirimdir ve önceki derste gördüğümüz
basamaklamada en zayıf kaynaktır.

#### Değer Çözümlemesinin Yürütülmesi

Aşağıdaki program, basamaklama sonucu kazanmış bildirimleri girdi alır ve ağaçta aşağı
inerek her ögenin dört özellik için değerini çözer. Her satırın sonunda değerin **nereden**
geldiği yazılıdır.

```js
// kalitim.mjs — deger cozumlemesini agacta asagi dogru yurutur
const KALITILAN = new Set(["color", "font-family", "line-height", "text-align", "visibility"]);
const BASLANGIC = {
  "color": "canvastext", "font-family": "serif", "line-height": "normal",
  "text-align": "start", "visibility": "visible", "border-color": "currentcolor",
  "background-color": "transparent",
};

// kazanan bildirimler (basamaklama sonucu) — dugum yolu -> {ozellik: deger}
const kazananlar = {
  "body":                         { "color": "#1c2733", "font-family": "system-ui" },
  "body/main":                    {},
  "body/main/table":              { "text-align": "right", "border-color": "#d5dbe0" },
  "body/main/table/tr":           {},
  "body/main/table/tr/td.ad":     { "text-align": "start" },
  "body/main/table/tr/td.eksik":  { "color": "inherit", "text-align": "unset",
                                    "border-color": "unset", "font-family": "initial" },
};

const agac = { ad: "body", cocuk: [ { ad: "main", cocuk: [ { ad: "table", cocuk: [
  { ad: "tr", cocuk: [ { ad: "td.ad", cocuk: [] }, { ad: "td.eksik", cocuk: [] } ] },
] } ] } ] };

const OZELLIKLER = ["color", "font-family", "text-align", "border-color"];

function coz(dugum, yol, ustDegerler) {
  const tamYol = yol ? `${yol}/${dugum.ad}` : dugum.ad;
  const yazilan = kazananlar[tamYol] ?? {};
  const degerler = {};
  for (const oz of OZELLIKLER) {
    let d = yazilan[oz];
    let kaynak;
    if (d === undefined) {
      // bildirim yok: kalitilan ozellikse ustten al, degilse baslangic degeri
      if (KALITILAN.has(oz) && ustDegerler) { d = ustDegerler[oz]; kaynak = "kalitim"; }
      else { d = BASLANGIC[oz]; kaynak = "baslangic"; }
    } else if (d === "inherit") {
      d = ustDegerler ? ustDegerler[oz] : BASLANGIC[oz]; kaynak = "inherit";
    } else if (d === "initial") {
      d = BASLANGIC[oz]; kaynak = "initial";
    } else if (d === "unset") {
      if (KALITILAN.has(oz) && ustDegerler) { d = ustDegerler[oz]; kaynak = "unset->kalitim"; }
      else { d = BASLANGIC[oz]; kaynak = "unset->baslangic"; }
    } else {
      kaynak = "bildirim";
    }
    degerler[oz] = d;
    console.log(`  ${oz.padEnd(12)} = ${String(d).padEnd(12)} (${kaynak})`);
  }
  for (const c of dugum.cocuk) {
    console.log(`${tamYol}/${c.ad}`);
    coz(c, tamYol, degerler);
  }
}

console.log("body");
coz(agac, "", null);
```

```
body
  color        = #1c2733      (bildirim)
  font-family  = system-ui    (bildirim)
  text-align   = start        (baslangic)
  border-color = currentcolor (baslangic)
body/main
  color        = #1c2733      (kalitim)
  font-family  = system-ui    (kalitim)
  text-align   = start        (kalitim)
  border-color = currentcolor (baslangic)
body/main/table
  color        = #1c2733      (kalitim)
  font-family  = system-ui    (kalitim)
  text-align   = right        (bildirim)
  border-color = #d5dbe0      (bildirim)
body/main/table/tr
  color        = #1c2733      (kalitim)
  font-family  = system-ui    (kalitim)
  text-align   = right        (kalitim)
  border-color = currentcolor (baslangic)
body/main/table/tr/td.ad
  color        = #1c2733      (kalitim)
  font-family  = system-ui    (kalitim)
  text-align   = start        (bildirim)
  border-color = currentcolor (baslangic)
body/main/table/tr/td.eksik
  color        = #1c2733      (inherit)
  font-family  = serif        (initial)
  text-align   = right        (unset->kalitim)
  border-color = currentcolor (unset->baslangic)
```

Çıktıdan üç gözlem çıkar.

`color` ve `font-family` yalnızca `body` ögesinde bildirildi ve dört kuşak aşağı, hücrelere
kadar taşındı. Bir bildirim, ağacın kökünde yazıldığında bütün alt ağacı etkiler.

`border-color` tabloda `#d5dbe0` iken satırda yeniden `currentcolor`'a düştü. Kenarlık
rengi kalıtılan bir özellik değildir; tabloya verilen kenarlık rengi hücrelere geçmez.
Tabloda kenarlık kurmak için hücrelere ayrıca kural yazmak gerekmesinin sebebi budur.

`text-align` tabloda `right` oldu ve satır ile hücrelere kalıtımla indi; `td.ad` ögesinde
bir bildirimle `start`'a çevrildi. Kalıtım, alt ögede bildirim varsa geçersizdir — kalıtım
en zayıf kaynaktır, bir bildirimle her zaman bastırılır.

#### Dört Anahtar Sözcük

Her özelliğin değerine yazılabilen dört anahtar sözcük vardır. Çıktının son bloğu dördünden
üçünü gösteriyor.

`inherit` — kalıtılmayan bir özelliği bile üst ögeden almaya zorlar. `td.eksik` ögesinde
`color: inherit` yazıldı ve değer üstten geldi. Bu, kalıtılmayan bir özellikte anlamlıdır;
`border-color: inherit` yazmak, kenarlık rengini üstten çekmenin yoludur.

`initial` — özelliğin başlangıç değerine döner. Çıktıda `font-family: initial` yazan hücre
`serif` değerine düştü; üstteki `system-ui` kalıtımı kesildi. Bu sonuç sıklıkla istenmeyen
bir sonuçtur: `initial`, dilin tanımına döner, sayfanın tasarımına değil.

`unset` — koşullu davranır: özellik kalıtılan bir özellikse `inherit` gibi, değilse
`initial` gibi işler. Çıktıda `text-align: unset` kalıtıma, `border-color: unset` başlangıç
değerine düştü. Bu, "bu bildirimi hiç yazmamış olsaydım ne olurdu" sorusunun yanıtıdır.

`revert` — dördüncüsüdür ve farklı bir soruya yanıt verir: değeri, **daha zayıf bir kaynağın**
kazanacağı hâle döndürür. Yazar biçeminde `revert` yazmak, o özellikte bir önceki kökene dönmek
demektir: kullanıcı biçeminde o özellik için bir kural varsa ona, yoksa tarayıcı varsayılanına.
`initial` ile farkı burada görülür: bir bağlantıda `color: initial` yazmak rengi dilin başlangıç
değerine götürür, `color: revert` ise tarayıcının bağlantılar için tanımladığı renge götürür.

Dördü de `all` kısayoluyla bütün özelliklere birden yazılabilir. `all: revert` bir ögeyi
yazar biçeminden yalıtır; `all: unset` ise kalıtılanları koruyup kalanları sıfırlar.

#### Değer Aşamaları

Bu bölümde geçen "hesaplanmış değer" terimi tanımlanmalıdır. Bir bildirimin yazımından
ekrandaki sonuca kadar değer birkaç aşamadan geçer:

| Aşama | Ne olur |
|---|---|
| bildirilen değer | Kaynakta yazılan metin |
| basamaklanmış değer | Basamaklamayı kazanan bildirim |
| belirtilmiş değer | Kazanan yoksa kalıtım veya başlangıç değeriyle doldurulmuş hâli |
| hesaplanmış değer | Göreli birimlerin çözüldüğü, kalıtımla aktarılan hâli |
| kullanılan değer | Yerleşim yapıldıktan sonra kesinleşen hâli |

Kalıtımla aktarılan aşama **hesaplanmış** değerdir. Bir üst ögede `font-size: 1.5em`
yazılıysa, çocuk `1.5em` metnini değil, o ifadenin çözülmüş sonucunu devralır — aksi hâlde
her kuşakta yeniden çarpılır ve yazı boyutu üstel büyürdü.

Kullanılan değer aşaması ise yerleşimi bekler. `width: 50%` bildiriminde hesaplanmış değer
hâlâ yüzdedir; kesin sayı, kapsayan kutunun genişliği bilindikten sonra ortaya çıkar. Bu
aşamayı kutu modeli konusunda ayrıntısıyla göreceğiz.

#### İstasyon Sayfasında Kalıtımın Kullanımı

```css
/* istasyon.css — 7. adım: kalıtımı taban olarak kullanmak */
body {
  color: #1c2733;
  font-family: system-ui, sans-serif;
  line-height: 1.5;
}

/* form denetimleri tipografiyi kendiliğinden devralmaz */
input, select, textarea, button {
  font: inherit;
  color: inherit;
}

.olcum-cizelgesi { border-collapse: collapse; }
.olcum-cizelgesi th,
.olcum-cizelgesi td { border-block-end: 1px solid #d5dbe0; }
```

İkinci kural bir istisnayı kapatır. Form denetimleri tipografilerini belgeden almaz;
kendi varsayılan yazı tiplerini taşırlar. `font: inherit` yazmak, bu istisnayı kaldırıp
denetimleri belgenin tipografisine bağlar.

Üçüncü grup, kenarlık rengi kalıtılmadığı için kenarlığın hücrelere ayrı ayrı yazılması
gerektiğini gösteriyor — çıktının dördüncü bloğunda gördüğümüz davranışın doğrudan sonucu.

#### Özet

- Bir özellik ya kalıtılır ya da bildirim yoksa başlangıç değerine düşer; hangisinin
  geçerli olduğu özelliğin tanımında yazılıdır ve metin özellikleri kalıtılır, kutu
  geometrisi özellikleri kalıtılmaz.
- Başlangıç değeri dilin tanımından gelir; tarayıcının varsayılan görünümü ise bir biçem
  dosyasından gelir ve basamaklamada en zayıf kaynaktır. `display: block` bir başlangıç
  değeri değil, o dosyada yazan bir bildirimdir.
- Kalıtım en zayıf kaynaktır: alt ögede o özelliğe yazan bir bildirim varsa kalıtım
  geçersizdir.
- `inherit` üstten almaya zorlar, `initial` dilin başlangıç değerine döner, `unset` ikisi
  arasında özelliğin türüne göre seçer, `revert` daha zayıf bir kaynağın değerine döner.
- Kalıtımla aktarılan şey hesaplanmış değerdir; göreli birimler aktarılmadan önce çözülür,
  aksi hâlde her kuşakta yeniden çarpılırlardı.

#### Sonraki Adım

Bu konu boyunca kuralların hangi ögelere ulaştığı ve hangi değerin kazandığı çözüldü. Değer
kesinleşti; ama o değerin ne anlama geldiği henüz kurulmadı. `padding: 8px` bir hücrenin
neresine 8 piksel ekler? Genişlik verilen bir kutunun toplam eni ne olur? Sonraki konu her
ögenin bir kutuya dönüştüğü modeli tanımlar ve bu kutunun bölümlerini aritmetikle ele alır.

## Kutu Modeli ve Akış

Her ögenin bir kutuya dönüşmesi, kutunun bölümlerinin aritmetiği, normal akışın kuralları ve akıştan çıkaran konumlandırma ile yığılma düzeni.

### Kutu Modeli

Source: https://academia.sh/tr/kurslar/css-temelleri/kutu-modeli

Her ögenin dört iç içe dikdörtgenden oluşan bir kutuya dönüşmesi; içerik, iç boşluk, kenarlık ve dış boşluk bölgelerinin sınırları ve aritmetiği.

Önceki konu bir ögenin hangi bildirimleri alacağını çözdü. Değer artık kesin: bir hücrenin
`padding` değeri `8px 12px`. Ama bu değerin ne anlama geldiği söylenmedi. Hücrenin
neresine 8 piksel eklenir? Eklenen boşluk hücrenin toplam enini büyütür mü, yoksa içindeki
metnin alanını mı daraltır?

Bu soruların yanıtı **kutu modeli (box model)**'dir. Model tek bir cümleyle özetlenir:
belge ağacındaki her öge, yerleşim sırasında bir ya da daha çok dikdörtgen kutuya
dönüştürülür ve her kutu dört iç içe bölgeden oluşur. Bu ders o dört bölgeyi ve
aralarındaki aritmetiği tanımlar.

#### Dört Bölge

Dıştan içe doğru:

**Dış boşluk kutusu (margin box)** en dıştaki dikdörtgendir. Kutunun komşularıyla arasındaki
mesafeyi taşır. Saydamdır; arka plan buraya çizilmez.

**Kenarlık kutusu (border box)** dış boşluğun içindedir. Kenarlığın kendisi bu bölgenin
sınırında çizilir.

**İç boşluk kutusu (padding box)** kenarlığın içindedir. İçerikle kenarlık arasındaki
mesafeyi taşır. Dış boşluktan farklı olarak **arka plan buraya kadar uzanır**.

**İçerik kutusu (content box)** en içteki dikdörtgendir. Metin, görsel ya da alt kutular
buraya yerleşir.

Bölgeleri ayıran dört sınır vardır ve her birinin ayrı bir adı vardır: içerik kenarı, iç boşluk
kenarı, kenarlık kenarı, dış boşluk kenarı. Bu adlar `background-clip`, `background-origin`
ve `box-sizing` gibi özelliklerin değerlerinde geçer.

#### Aritmetik

Her bölgenin ölçüsü, bir içteki bölgenin ölçüsüne iki kenar eklenerek bulunur. Yatay eksende:

$$\text{dolgu eni} = \text{içerik eni} + p_{\text{sol}} + p_{\text{sağ}}$$
$$\text{kenarlık eni} = \text{dolgu eni} + b_{\text{sol}} + b_{\text{sağ}}$$
$$\text{dış eni} = \text{kenarlık eni} + m_{\text{sol}} + m_{\text{sağ}}$$

Dikey eksende aynı toplamlar üst ve alt kenarlarla yapılır. Aşağıdaki program iki kutu için
bu toplamları yürütür: ölçüm çizelgesinin bir hücresi ve sayfanın künye bölümü.

```js
// kutu-olc.mjs — bir kutunun dort bolgesinin sinirlarini hesaplar
function kutuOlc({ icerikW, icerikH, padding, border, margin }) {
  const [pu, ps, pa, pl] = padding;   // ust, sag, alt, sol
  const [bu, bs, ba, bl] = border;
  const [mu, ms, ma, ml] = margin;
  const dolguW = icerikW + pl + ps;
  const dolguH = icerikH + pu + pa;
  const kenarlikW = dolguW + bl + bs;
  const kenarlikH = dolguH + bu + ba;
  const disW = kenarlikW + ml + ms;
  const disH = kenarlikH + mu + ma;
  return { dolguW, dolguH, kenarlikW, kenarlikH, disW, disH };
}

const hucre = {
  icerikW: 120, icerikH: 24,
  padding: [8, 12, 8, 12],
  border:  [0, 0, 1, 0],
  margin:  [0, 0, 0, 0],
};
const kunye = {
  icerikW: 640, icerikH: 96,
  padding: [24, 32, 24, 32],
  border:  [0, 0, 4, 0],
  margin:  [0, 16, 32, 16],
};

for (const [ad, k] of [["hucre", hucre], ["kunye", kunye]]) {
  const o = kutuOlc(k);
  console.log(`--- ${ad} ---`);
  console.log(`icerik   : ${k.icerikW} x ${k.icerikH}`);
  console.log(`dolgu    : ${o.dolguW} x ${o.dolguH}`);
  console.log(`kenarlik : ${o.kenarlikW} x ${o.kenarlikH}`);
  console.log(`dis      : ${o.disW} x ${o.disH}`);
  console.log(`cizilen alan (kenarlik kutusu): ${o.kenarlikW * o.kenarlikH} px2`);
  console.log(`yer kaplanan (dis kutusu)     : ${o.disW * o.disH} px2`);
}
```

```
--- hucre ---
icerik   : 120 x 24
dolgu    : 144 x 40
kenarlik : 144 x 41
dis      : 144 x 41
cizilen alan (kenarlik kutusu): 5904 px2
yer kaplanan (dis kutusu)     : 5904 px2
--- kunye ---
icerik   : 640 x 96
dolgu    : 704 x 144
kenarlik : 704 x 148
dis      : 736 x 180
cizilen alan (kenarlik kutusu): 104192 px2
yer kaplanan (dis kutusu)     : 132480 px2
```

Hücrede iki gözlem var. `padding: 8px 12px` bildirimi, dört değere açıldığında yatayda
$12 + 12 = 24$, dikeyde $8 + 8 = 16$ piksel ekledi: içerik eni 120'den 144'e, boyu 24'ten
40'a çıktı. **İç boşluk metnin alanını daraltmaz; kutuyu büyütür.** Bu, önceki konuda
sorulan sorunun yanıtıdır.

Yalnızca alt kenarlık verildiği için genişlik değişmedi ve yükseklik 1 piksel arttı. Dış
boşluk sıfır olduğundan kenarlık kutusu ile dış kutu aynı.

Künyede dış boşluğun etkisi görülüyor: kenarlık kutusu 704 birim genişken dış kutu 736
birim. Aradaki 32 birim çizilmez, ama komşu kutuları uzakta tutar. İki alan hesabının
farkı — 132480 ve 104192 — bunu sayıyla verir.

#### Hangi Özellik Hangi Bölgeye Uygulanır

Kutu modelinin pratik değeri, görsel özelliklerin hangi bölgeye kadar uzandığını bilmektir.

| Özellik | Kapsadığı bölge |
|---|---|
| `background-color` | İçerik + iç boşluk + kenarlık (kenarlık kutusuna kadar) |
| `background-image` | Öntanımlı olarak iç boşluk kutusuna yerleşir, kenarlık kutusuna kadar çizilir |
| `border` | Kenarlık kutusunun sınırında |
| `box-shadow` | Kenarlık kutusunun dışına (ya da `inset` ile içine) |
| `outline` | Kenarlık kutusunun dışına, yer kaplamadan |

İki satır ayrıca vurgulanmalıdır.

**Arka plan iç boşluğu kapsar.** Bir düğmeye arka plan rengi ve iç boşluk verildiğinde,
renkli alan metinden büyük olur — iç boşluk boyanmıştır. Aynı boşluk dış boşlukla verilseydi
renkli alan yalnızca metin kadar olurdu. İki bildirimin görsel sonucu bu yüzden farklıdır.

**Çerçeve yer kaplamaz.** `outline` kenarlık kutusunun dışına çizilir ama yerleşimi
etkilemez; komşu kutular sanki orada bir şey yokmuş gibi durur. Odak göstergesinin
`outline` ile verilmesinin sebebi budur: odak değiştiğinde sayfa sarsılmaz. Aynı iş
`border` ile yapılsaydı, odaklanan her ögede kutu büyür ve çevresindeki her şey kayardı.

#### Mantıksal Kenarlar

Kenarları adlandırmanın iki yolu vardır. Fiziksel adlar ekranı temel alır: `margin-top`,
`padding-left`, `border-right`. Mantıksal adlar yazma yönünü temel alır: `margin-block-start`,
`padding-inline-start`, `border-inline-end`.

Mantıksal adlar iki eksen tanımlar. **Satır içi eksen (inline axis)** metnin aktığı yöndür;
soldan sağa yazılan bir belgede soldan sağa. **Blok eksen (block axis)** satırların
yığıldığı yöndür; aynı belgede yukarıdan aşağı.

Sağdan sola yazılan bir belgede `padding-inline-start` sağ kenara denk gelirken `padding-left`
yine sol kenarda kalır. Belge dili değiştiğinde biçemi yeniden yazmamak için mantıksal adlar
tercih edilir. Bu kurs boyunca mantıksal adlar kullanılır; Web'in Temelleri ve HTML kursunda
`lang` özniteliğinin taşıdığı bilgi, burada karşılığını bulur.

Dört kenarın kısayolları da mantıksal biçimde vardır: `margin-block` iki blok kenarı,
`margin-inline` iki satır içi kenarı birden ayarlar. `margin-inline: auto` yazımı, bir bloku
yatayda ortalamanın standart yoludur.

#### İstasyon Sayfasının Ölçüleri

```css
/* istasyon.css — 8. adım: kutu ölçüleri */
.kunye {
  padding-block: 24px;
  padding-inline: 32px;
  margin-inline: 16px;
  margin-block-end: 32px;
  border-block-end: 4px solid #143a52;
  background-color: #f5f7f8;
}

.olcum-cizelgesi th,
.olcum-cizelgesi td {
  padding-block: 8px;
  padding-inline: 12px;
  border-block-end: 1px solid #d5dbe0;
}
```

Künyenin arka plan rengi iç boşluğu kapsar; başlık ile renkli alanın kenarı arasında 24 ve
32 birimlik bir aralık kalır. Dış boşluk ise renkli alanın dışında durur ve künyeyi
kenarlardan uzak tutar.

Hücrelerde iç boşluk yatayda 12, dikeyde 8 birimdir. Çizelgede okunabilirliği sağlayan şey
metnin kendisi değil, hücreler arasındaki bu aralıktır: iç boşluk kaldırıldığında sütunlar
birbirine yapışır ve alt kenarlık sütunları ayırt etmekte tek başına yetersiz kalır.

#### Özet

- Her öge yerleşimde bir kutuya dönüşür; kutu dıştan içe dış boşluk, kenarlık, iç boşluk ve
  içerik bölgelerinden oluşur.
- Her bölgenin ölçüsü bir içtekine iki kenar eklenerek bulunur; iç boşluk içeriği daraltmaz,
  kutuyu büyütür.
- Arka plan kenarlık kutusuna kadar uzanır ve iç boşluğu kapsar; dış boşluk saydamdır ve
  yalnızca komşuları uzakta tutar.
- `outline` kenarlık kutusunun dışına çizilir ama yer kaplamaz; odak göstergesi bu yüzden
  onunla verilir, `border` ile verilseydi odak değiştikçe yerleşim kayardı.
- Mantıksal kenar adları yazma yönüne göre çözülür; blok eksen satırların yığıldığı yön,
  satır içi eksen metnin aktığı yöndür.

#### Sonraki Adım

Bu derste kutuya `width` verilmedi; içerik eni verildi ve dıştaki ölçüler ondan hesaplandı.
Oysa biçem yazarken genellikle tersi yapılır: bir genişlik bildirilir ve içerik ölçüsü
ondan türetilir. Bu türetmenin iki ayrı modeli vardır ve aralarındaki fark bir kutunun
toplam enini değiştirir. Sonraki ders iki modeli aynı sayılarla karşılaştırır.

### Kutu Boyutlandırma

Source: https://academia.sh/tr/kurslar/css-temelleri/kutu-boyutlandirma

Bildirilen genişliğin hangi bölgeyi ölçtüğü; content-box ve border-box modellerinin aynı sayılarla karşılaştırılması ve en küçük–en büyük sınırların etkisi.

Önceki derste kutunun bölgeleri içerik ölçüsünden başlanarak dışa doğru hesaplandı. Biçem
yazarken genellikle tersi yapılır: bir `width` bildirilir ve kalan ölçüler ondan türetilir.
Burada bir soru ortaya çıkar — bildirilen genişlik **hangi bölgeyi** ölçer?

Bu sorunun iki yanıtı vardır ve ikisi de tanımlıdır. Hangisinin geçerli olacağı
`box-sizing` özelliğiyle seçilir. Bu ders iki modeli aynı sayılarla karşılaştırır.

#### İki Model

`box-sizing: content-box` — bildirilen genişlik **içerik kutusunu** ölçer. İç boşluk ve
kenarlık bu değerin dışına eklenir; kutunun kapladığı yer bildirilen değerden büyüktür.
Bu, özelliğin başlangıç değeridir.

`box-sizing: border-box` — bildirilen genişlik **kenarlık kutusunu** ölçer. İç boşluk ve
kenarlık bu değerin içinden alınır; içerik kutusu geriye kalan kadardır.

İkisi arasındaki fark bir tercih meselesi değil, farklı bir aritmetiktir:

$$\text{content-box:}\quad W_{\text{kenarlık}} = w + p_{\text{sol}} + p_{\text{sağ}} + b_{\text{sol}} + b_{\text{sağ}}$$
$$\text{border-box:}\quad W_{\text{içerik}} = w - p_{\text{sol}} - p_{\text{sağ}} - b_{\text{sol}} - b_{\text{sağ}}$$

```js
// boyutlandirma.mjs — content-box ile border-box farkini sayiyla verir
function coz({ width, padding, border, kutuModeli }) {
  const [pl, ps] = padding, [bl, bs] = border;
  const ek = pl + ps + bl + bs;
  if (kutuModeli === "content-box") {
    return { icerik: width, kenarlikKutusu: width + ek };
  }
  // border-box: bildirilen deger kenarlik kutusudur; icerik geriye kalandir
  return { icerik: Math.max(0, width - ek), kenarlikKutusu: width };
}

const durumlar = [
  { ad: "kart",      width: 300, padding: [16, 16], border: [1, 1] },
  { ad: "dar kutu",  width: 40,  padding: [16, 16], border: [1, 1] },
];

for (const d of durumlar) {
  for (const m of ["content-box", "border-box"]) {
    const r = coz({ ...d, kutuModeli: m });
    console.log(`${d.ad.padEnd(9)} ${m.padEnd(11)} width=${d.width}  -> icerik=${r.icerik}  kenarlikKutusu=${r.kenarlikKutusu}`);
  }
}

console.log("--- uc sutun, her biri %33.333, ic bosluk 16, kenarlik 1 ---");
const kapsayan = 960;
const yuzde = 100 / 3;
for (const m of ["content-box", "border-box"]) {
  const w = kapsayan * yuzde / 100;
  const r = coz({ width: w, padding: [16, 16], border: [1, 1], kutuModeli: m });
  const toplam = r.kenarlikKutusu * 3;
  console.log(`${m.padEnd(11)} sutun kenarlik kutusu=${r.kenarlikKutusu.toFixed(2)}  ucunun toplami=${toplam.toFixed(2)}  tasma=${(toplam - kapsayan).toFixed(2)}`);
}
```

```
kart      content-box width=300  -> icerik=300  kenarlikKutusu=334
kart      border-box  width=300  -> icerik=266  kenarlikKutusu=300
dar kutu  content-box width=40  -> icerik=40  kenarlikKutusu=74
dar kutu  border-box  width=40  -> icerik=6  kenarlikKutusu=40
--- uc sutun, her biri %33.333, ic bosluk 16, kenarlik 1 ---
content-box sutun kenarlik kutusu=354.00  ucunun toplami=1062.00  tasma=102.00
border-box  sutun kenarlik kutusu=320.00  ucunun toplami=960.00  tasma=0.00
```

#### Sayıların Söylediği

İlk iki satırda aynı bildirim iki farklı kutu üretti: `width: 300px` yazan bir kart,
`content-box` modelinde 334 birim yer kaplarken `border-box` modelinde tam 300 birim
kapladı. Aradaki 34 birim, iç boşluk ve kenarlığın toplamıdır ($16 + 16 + 1 + 1$).

Dar kutu satırı bir sınır durumunu gösterir. `border-box` modelinde `width: 40px` verilen bir
kutuda, 34 birimlik ek çıkarıldıktan sonra içeriğe yalnızca 6 birim kaldı. Ek toplamı
bildirilen genişliği aşsaydı içerik ölçüsü sıfıra kelepçelenirdi; eksiye düşmez. Bu, kutu
görünmez olmaz demektir — kenarlık kutusu yine 40 birimdir, ama içine hiçbir şey sığmaz.

Üçüncü blok pratikteki asıl sorunu gösteriyor. Üç sütunu 960 birimlik bir kapsayıcıya
yerleştirmek için her birine `width: 33.333%` yazıldığında `content-box` modelinde toplam
1062 birim tutuyor: 102 birimlik bir taşma. Sebep, yüzdenin **içerik kutusunu** ölçmesi ve
iç boşlukla kenarlığın buna eklenmesidir. Aynı bildirim `border-box` modelinde tam
oturuyor.

Bu, `border-box` modelinin neden yaygın bir taban tercihi olduğunu açıklar: bir kutunun
yer kaplamasını, o kutunun iç dolgusundan bağımsız kılar. İç boşluk 16'dan 24'e
çıkarıldığında yerleşim bozulmaz; yalnızca içeriğe kalan alan daralır.

Model bütün belgede tek biçimde kurulur:

```css
*, *::before, *::after { box-sizing: border-box; }
```

Evrensel seçici sözde elemanları kapsamaz; bu yüzden üçü birlikte yazılır. Özgüllüğü
$0,0,0$ olduğu için sonraki her kural bunu bastırabilir.

#### Yükseklik Farklı Davranır

`width` ve `height` simetrik görünür ama davranışları ayrılır.

Bir blok kutunun genişliği, bildirilmediğinde kapsayan kutuyu doldurur. Yüksekliği ise
bildirilmediğinde **içeriğine göre** belirlenir. Bu asimetri normal akışın bir sonucudur ve
sonraki derslerde ele alınacaktır.

Bunun bir sonucu, `height` yüzdesinin çoğu zaman işlememesidir. `height: 50%` bildirimi,
kapsayan kutunun yüksekliği bilinmedikçe çözülemez; kapsayan kutunun yüksekliği de kendi
içeriğine bağlıysa döngüsel bir tanım oluşur. Bu durumda yüzde `auto` gibi işlenir.

Sabit yükseklik vermek ayrıca kırılgandır: metin beklenenden uzun olduğunda kutudan taşar.
Yükseklik, taşma yönetimiyle birlikte düşünülmediği sürece bildirilmez.

#### En Küçük ve En Büyük Sınırlar

`min-width`, `max-width`, `min-height` ve `max-height` özellikleri hesaplanan ölçüyü
sınırlar. Uygulama sırası tanımlıdır ve önemlidir:

1. Önce `width` (ya da `auto`) çözülür.
2. Sonuç `max-width` değerini aşıyorsa `max-width` değerine indirilir.
3. Bu sonuç `min-width` değerinin altındaysa `min-width` değerine yükseltilir.

Sıra, `min-width` değerinin `max-width` değerini yendiği anlamına gelir. `min-width: 400px`
ve `max-width: 300px` birlikte yazılırsa sonuç 400'dür.

Bu üçlünün en sık kullanımı okunabilir bir metin sütunu kurmaktır:

```css
main {
  max-width: 70ch;
  margin-inline: auto;
}
```

`max-width` bir üst sınır koyar; kapsayıcı dardır diye kutu taşmaz, genişse de metin
okunamayacak kadar uzun satırlara yayılmaz. `margin-inline: auto` kalan alanı iki yana eşit
dağıtır. Bu ikisi birlikte, sabit bir genişlik bildirmeden hem sınırlı hem uyumlu bir sütun
verir.

`ch` birimi bir sonraki konuda tanımlanacak; şimdilik yaklaşık karakter genişliği olarak
okunabilir.

#### İstasyon Sayfasının Boyutlandırması

```css
/* istasyon.css — 9. adım: boyutlandırma modeli ve sınırlar */
*, *::before, *::after { box-sizing: border-box; }

main {
  max-width: 70ch;
  margin-inline: auto;
  padding-inline: 16px;
}

.olcum-cizelgesi { width: 100%; }

.olcum-cizelgesi .ad { width: 40%; }

.konum-gorseli img {
  max-width: 100%;
  height: auto;
}
```

Son kural sık gereken bir çifttir. Görsele `max-width: 100%` verilmesi, kapsayıcıdan geniş
bir görselin taşmasını önler. `height: auto` ise en–boy oranını korur: yalnızca genişlik
sınırlanırsa ve yükseklik belgede bildirilmiş kalırsa görsel ezilir.

`.ad` sütununa yüzdeli genişlik verilmesi tablo yerleşiminde bir öneri gibi işler; tablo
algoritması sütun genişliklerini içerikle birlikte değerlendirir. Tabloların ölçü davranışı
kendi kurallarına tabidir ve buradaki modelin bütün ayrıntılarını izlemez.

#### Özet

- Bildirilen `width` değerinin hangi bölgeyi ölçtüğü `box-sizing` ile seçilir:
  `content-box` içerik kutusunu, `border-box` kenarlık kutusunu ölçer.
- `content-box` modelinde iç boşluk ve kenarlık bildirilen değerin dışına eklenir; yüzdeli
  genişliklerde bu, taşmanın doğrudan sebebidir.
- `border-box` modelinde içerik ölçüsü geriye kalan kadardır ve hiçbir zaman eksiye düşmez;
  ek toplamı bildirilen genişliği aşarsa sıfıra kelepçelenir.
- Genişlik bildirilmediğinde kapsayanı doldurur, yükseklik bildirilmediğinde içeriğe göre
  belirlenir; bu asimetri yüzde yüksekliklerin çoğu zaman çözülememesinin sebebidir.
- Sınırlar sırayla uygulanır: önce `max`, sonra `min`. Bu sıra `min` değerini `max`
  değerinden güçlü kılar.

#### Sonraki Adım

Bu iki derste kutunun ölçüleri hesaplandı ama kutunun **ne tür** bir kutu olduğu hiç
sorulmadı. Bir paragraf neden kendi satırını kaplar da bir vurgu ögesi metnin içinde
kalır? İkisine de `width` yazıldığında neden yalnızca biri etkilenir? Sonraki ders
görüntüleme türlerini tanımlar ve bu farkın nereden geldiğini gösterir.

### Görüntüleme Türleri

Source: https://academia.sh/tr/kurslar/css-temelleri/goruntuleme-turleri

Blok, satır içi ve satır içi blok kutuların davranış farkları; dış ve iç görüntüleme türü ayrımı ve gizlemenin üç ayrı yolu.

Önceki iki derste kutuların ölçüleri hesaplandı ama kutunun **ne tür** bir kutu olduğu
sorulmadı. Bir paragraf neden kendi satırını kaplar da içindeki vurgu ögesi metnin akışında
kalır? İkisine de `width: 200px` yazıldığında neden yalnızca biri değişir?

Fark `display` özelliğinden gelir. Bu ders üç temel türü — blok, satır içi ve satır içi blok
— dört ölçüt üzerinden karşılaştırır ve gizlemenin ayrı bir mesele olduğunu gösterir.

#### Üç Tür, Dört Ölçüt

```js
// goruntuleme.mjs — display degerinin hangi ozellikleri etkilediğini cizelgeler
const TURLER = {
  "block":        { satirKaplar: true,  widthUygulanir: true,  dikeyBosluk: true,  aralikDuyarli: false },
  "inline":       { satirKaplar: false, widthUygulanir: false, dikeyBosluk: false, aralikDuyarli: true  },
  "inline-block": { satirKaplar: false, widthUygulanir: true,  dikeyBosluk: true,  aralikDuyarli: true  },
  "none":         { satirKaplar: false, widthUygulanir: false, dikeyBosluk: false, aralikDuyarli: false },
};

const basliklar = ["display", "kendi satiri", "width/height", "dikey margin", "bosluga duyarli"];
console.log(basliklar.map((b, i) => b.padEnd(i === 0 ? 14 : 16)).join(""));
for (const [ad, o] of Object.entries(TURLER)) {
  const e = (v) => (v ? "evet" : "hayir").padEnd(16);
  console.log(ad.padEnd(14) + e(o.satirKaplar) + e(o.widthUygulanir) + e(o.dikeyBosluk) + e(o.aralikDuyarli));
}

// satir ici kutularin yan yana dizilmesi: aradaki bosluk karakteri de bir kutudur
console.log("\n--- satir ici dizilim (bosluk karakteri dahil) ---");
const parcalar = [
  { tur: "inline-block", en: 80 }, { tur: "bosluk", en: 4 },
  { tur: "inline-block", en: 80 }, { tur: "bosluk", en: 4 },
  { tur: "inline-block", en: 80 },
];
let x = 0;
for (const p of parcalar) { console.log(`${String(x).padStart(4)} .. ${x + p.en}  ${p.tur}`); x += p.en; }
console.log(`toplam satir eni: ${x}  (kutular: ${80 * 3}, bosluklar: ${x - 240})`);
```

```
display       kendi satiri    width/height    dikey margin    bosluga duyarli 
block         evet            evet            evet            hayir           
inline        hayir           hayir           hayir           evet            
inline-block  hayir           evet            evet            evet            
none          hayir           hayir           hayir           hayir           

--- satir ici dizilim (bosluk karakteri dahil) ---
   0 .. 80  inline-block
  80 .. 84  bosluk
  84 .. 164  inline-block
 164 .. 168  bosluk
 168 .. 248  inline-block
toplam satir eni: 248  (kutular: 240, bosluklar: 8)
```

Çizelge, önceki dersteki soruların yanıtını veriyor.

**Blok kutu (block box)** kapsayan kutunun satır içi eksenini baştan sona doldurur ve
kendinden sonrakini alt satıra iter. `width` ve `height` uygulanır; dört kenardaki dış
boşluk da uygulanır. Paragraf, başlık, bölüm ve liste bu türdendir.

**Satır içi kutu (inline box)** metnin akışında kalır ve yalnızca içeriği kadar yer kaplar.
`width` ve `height` bildirimlerinin **hiçbir etkisi yoktur**; kutunun ölçüsü içeriğinden
gelir. Dikey dış boşluk da yok sayılır. Vurgu, bağlantı, kısaltma ve genel amaçlı `span`
bu türdendir.

Bir uyarı gerekir: satır içi kutuda yatay iç boşluk ve kenarlık **uygulanır** ama çizilen
alan satır yüksekliğini büyütmez. Bir bağlantıya dikey iç boşluk verildiğinde renkli alan
büyür, satırlar arasındaki mesafe değişmez ve alanlar üst üste binebilir.

**Satır içi blok kutu (inline-block box)** ikisinin arasındadır: dışarıdan satır içi gibi
davranır — kendi satırını kaplamaz, komşularıyla yan yana durur — ama içeriden bir blok
kutudur; `width`, `height` ve dört kenar dış boşluğu uygulanır.

#### Satır İçi Dizilimde Boşluk Karakteri Sayılır

Çıktının ikinci bölümü sık karşılaşılan bir sorunu gösteriyor. Üç adet 80 birimlik satır
içi blok kutu yan yana konduğunda toplam satır eni 240 değil 248 çıktı. Aradaki 8 birim,
kaynak metinde kutuların **arasındaki boşluk karakterlerinden** gelir.

Sebep, satır içi düzenin metin düzeni olmasıdır. Kaynak metindeki satır sonları ve boşluklar
birer boşluk karakterine indirgenir ve bu karakter de yer kaplar. Genişliği yazı tipine
bağlıdır; buradaki 4 birim örnek bir değerdir.

Sonuç, üç kutuya `width: 33.333%` verildiğinde bir satıra sığmamalarıdır — boşluklar
eklenince toplam yüzde yüzü aşar. Bu, satır içi blok ile ızgara benzeri düzenler kurmanın
kırılgan yanıdır ve bir sonraki kurstaki düzen sistemlerinde ortadan kalkar; o modellerde
kutular arasındaki metin düğümleri yerleşime katılmaz.

#### Dış ve İç Görüntüleme Türü

`display` özelliği tek bir şey değil, iki şey birden söyler.

**Dış görüntüleme türü (outer display type)** kutunun *kapsayıcısında* nasıl davranacağını
belirler: `block` ya da `inline`.

**İç görüntüleme türü (inner display type)** kutunun *çocuklarını* nasıl dizeceğini
belirler: `flow`, `flow-root`, `flex`, `grid`, `table`.

İki değerli yazımda ikisi ayrı ayrı verilir:

```css
.gezinme { display: block flex; }
```

Tek değerli eski yazımlar bu ikilinin kısaltmalarıdır: `block` sözcüğü `block flow`,
`inline-block` sözcüğü `inline flow-root`, `flex` sözcüğü `block flex` anlamına gelir. Bu
ayrım, `inline-block` türünün neden "içeriden blok, dışarıdan satır içi" diye tarif
edildiğini açıklar — sözcük gerçekten iki parçadan oluşur.

`flow-root` değeri ayrıca anılmalıdır: kutuyu, çocuklarının akışını kendi içinde tamamlayan
bağımsız bir kök yapar. Bunun ne işe yaradığını kayan öğeler dersinde göreceğiz.

#### Türü Değiştirmek Anlamı Değiştirmez

Bir ögenin görüntüleme türü tarayıcının varsayılan biçem dosyasından gelir ve bir bildirimle
değiştirilebilir. `li { display: inline; }` yazmak liste maddelerini yan yana dizer.

Değişen yalnızca yerleşimdir. Belgedeki anlam — bunun bir liste maddesi olduğu — yerinde
kalır ve belge ağacını okuyan programlar onu görmeye devam eder. Bu, Web'in Temelleri ve
HTML kursunda kurulan yapı–sunum ayrımının doğrudan sonucudur: bir başlığı satır içi yapmak
onu başlık olmaktan çıkarmaz.

Tersi de doğrudur ve bir uyarı içerir: bir ögeyi görsel olarak başka bir şeye benzetmek onu
o şey yapmaz. Bir `div` ögesine düğme görünümü vermek, klavyeyle odaklanabilir ve
etkinleştirilebilir bir düğme üretmez. Görünüm biçemin, davranış ve anlam işaretlemenin
işidir.

#### Gizlemenin Üç Yolu

`display: none` çizelgede dördüncü satırdaydı ve bütün ölçütlerde "hayır" veriyordu. Bunun
sebebi, bu değerin kutu **üretmemesidir**. Öge belge ağacında durur ama yerleşime hiç
katılmaz.

Üç ayrı gizleme yolu vardır ve sonuçları farklıdır:

| Yazım | Kutu üretir mi | Yer kaplar mı | Yardımcı teknolojiye görünür mü |
|---|---|---|---|
| `display: none` | Hayır | Hayır | Hayır |
| `visibility: hidden` | Evet | Evet | Hayır |
| `opacity: 0` | Evet | Evet | Evet |

Üçüncü satır dikkat ister: saydamlığı sıfırlanmış bir öge görünmez ama hâlâ oradadır —
odaklanabilir, tıklanabilir ve ekran okuyucuya okunur. Bir şeyi kullanıcıdan gizlemek için
`opacity: 0` yazmak, onu erişilebilirlik ağacından kaldırmaz.

Dördüncü bir durum daha vardır: bir metni **görsel olarak** gizleyip yardımcı teknolojiye
bırakmak. Bu, `display: none` ile yapılamaz; ögeyi bir piksellik bir alana kırpan bir kural
grubuyla yapılır ve genellikle `.gorsel-gizli` gibi bir yardımcı sınıfla adlandırılır.

#### İstasyon Sayfasında Türler

```css
/* istasyon.css — 10. adım: görüntüleme türleri */
.gezinme li { display: inline-block; margin-inline-end: 12px; }

.olcum-cizelgesi caption { display: table-caption; text-align: start; }

.durum-rozeti {
  display: inline-block;
  padding-block: 2px;
  padding-inline: 8px;
  border-radius: 4px;
}

.gorsel-gizli {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}
```

`.durum-rozeti` sınıfı satır içi blok olarak tanımlandı çünkü iki şeyi birden istiyor:
metnin akışında kalmak ve dikey iç boşluk alabilmek. Satır içi olsaydı iç boşluk çizilir ama
satır yüksekliğini büyütmezdi; blok olsaydı kendi satırına inerdi.

`.gorsel-gizli` sınıfı yukarıdaki dördüncü durumu gerçekleştirir: kutu üretilir, bir
piksellik bir alana kırpılır ve erişilebilirlik ağacında kalır. Ölçüm çizelgesinde birimleri
görsel olarak sütun başlığında bir kez yazıp her hücrede yardımcı teknolojiye tekrarlamak
için kullanılır.

#### Özet

- Blok kutu kapsayanının satır içi eksenini doldurur ve kendi satırını kaplar; `width`,
  `height` ve dört kenar dış boşluğu uygulanır.
- Satır içi kutu metnin akışında kalır; `width`, `height` ve dikey dış boşluk uygulanmaz,
  yatay iç boşluk ve kenarlık çizilir ama satır yüksekliğini büyütmez.
- Satır içi blok kutu dışarıdan satır içi, içeriden bloktur; yan yana dizilir ama ölçü ve
  boşluk bildirimlerini alır.
- Satır içi dizilimde kaynak metindeki boşluk karakterleri de yer kaplar; bu, yan yana
  dizilen kutuların toplamının beklenenden geniş çıkmasının sebebidir.
- Gizlemenin üç yolu farklı sonuç verir: `display: none` kutu üretmez, `visibility: hidden`
  yer kaplar, `opacity: 0` ise erişilebilirlik ağacında kalır ve etkileşime açıktır.

#### Sonraki Adım

Bu ders kutuların türünü tanımladı ama nasıl dizildiklerini ayrıntısıyla anlatmadı. Blok
kutular neden yukarıdan aşağı yığılır, satır içi kutular hangi kurala göre satırlara
bölünür, iki blok kutu arasındaki dış boşluk neden ikisinin toplamı değildir? Sonraki ders
normal akışın kurallarını tanımlar ve dış boşluk çökmesini sayıyla hesaplar.

### Akış Düzeni

Source: https://academia.sh/tr/kurslar/css-temelleri/akis-duzeni

Normal akışın blok ve satır içi yerleştirme kuralları; dış boşluk çökmesinin üç durumu ve çökmenin sayısal sonucu.

Önceki ders kutuların türünü tanımladı: blok kutu kendi satırını kaplar, satır içi kutu
metnin akışında kalır. "Akış" sözcüğü orada tanımsız kullanıldı. Bu ders onu tanımlar.

**Normal akış (normal flow)**, hiçbir konumlandırma bildirimi yazılmadığında kutuların
yerleştirilme biçimidir. Bütün yerleşim modellerinin varsayılanı budur ve bir kutunun
akıştan çıkarılması ayrıca bildirilmesi gereken bir şeydir. Akışın kurallarını bilmek,
çoğu yerleşim sorununun sanılandan az bildirimle çözülmesini sağlar.

#### Blok Yerleştirme

Bir blok kutu içindeki blok kutular, blok ekseninde arka arkaya yığılır. Kurallar üç
maddedir:

1. Her blok kutunun satır içi eksendeki ölçüsü, bildirilmediğinde kapsayan kutunun içerik
   kutusunu doldurur.
2. Blok eksendeki ölçüsü, bildirilmediğinde içeriğine göre belirlenir.
3. Bir sonraki kutu, bir öncekinin dış boşluk kenarından sonra başlar.

Üçüncü kural eksik yazılmıştır. İki kutunun karşılıklı dış boşlukları toplanmaz; **çöker**.

#### Dış Boşluk Çökmesi

**Çökme (margin collapsing)**, blok eksendeki iki dış boşluğun tek bir boşluğa
indirgenmesidir. Kural şudur:

- Her iki değer de pozitifse sonuç **en büyüğüdür**.
- Her iki değer de negatifse sonuç **en küçüğüdür** (mutlak değeri en büyük olanı).
- Biri pozitif biri negatifse sonuç **ikisinin toplamıdır**.

Aşağıdaki program bu kuralı uygular ve akışta üç paragrafın yerini hesaplar.

```js
// cokme.mjs — dis bosluk cokmesi kurallarini uygular
// kural: cokup birlesen bosluklarin sonucu
//   hepsi pozitifse en buyugu
//   hepsi negatifse en kucugu (mutlak degeri en buyugu)
//   karisiksa en buyuk pozitif + en kucuk negatif
function cok(...bosluklar) {
  const p = bosluklar.filter((b) => b > 0);
  const n = bosluklar.filter((b) => b < 0);
  const enP = p.length ? Math.max(...p) : 0;
  const enN = n.length ? Math.min(...n) : 0;
  return enP + enN;
}

console.log("--- iki kardes arasi ---");
const durumlar = [[32, 16], [16, 32], [24, 24], [32, -8], [-8, -16], [0, 24]];
for (const [alt, ust] of durumlar) {
  console.log(`alt=${String(alt).padStart(3)} ust=${String(ust).padStart(3)} -> aralik=${String(cok(alt, ust)).padStart(3)}   (toplasaydi ${alt + ust})`);
}

console.log("\n--- ust oge ile ilk cocuk (kenarlik/dolgu yoksa disari tasar) ---");
function ustCocuk({ ustMargin, cocukMargin, ustPadding, ustBorder }) {
  if (ustPadding > 0 || ustBorder > 0) {
    return { disari: ustMargin, iceride: cocukMargin, cokme: false };
  }
  return { disari: cok(ustMargin, cocukMargin), iceride: 0, cokme: true };
}
for (const d of [
  { ustMargin: 16, cocukMargin: 32, ustPadding: 0,  ustBorder: 0 },
  { ustMargin: 16, cocukMargin: 32, ustPadding: 1,  ustBorder: 0 },
  { ustMargin: 16, cocukMargin: 32, ustPadding: 0,  ustBorder: 1 },
]) {
  const r = ustCocuk(d);
  console.log(`ustPadding=${d.ustPadding} ustBorder=${d.ustBorder} -> disari=${r.disari} iceride=${r.iceride} cokme=${r.cokme}`);
}

console.log("\n--- bos kutu kendi icinde coker ---");
console.log(`ust=20 alt=12, icerik yok -> kutunun toplam dikey yeri = ${cok(20, 12)}`);

console.log("\n--- akista uc paragraf ---");
const p = [
  { ad: "p1", mUst: 0,  yukseklik: 24, mAlt: 16 },
  { ad: "p2", mUst: 16, yukseklik: 48, mAlt: 16 },
  { ad: "p3", mUst: 32, yukseklik: 24, mAlt: 0  },
];
let y = 0;
for (let i = 0; i < p.length; i++) {
  const aralik = i === 0 ? p[i].mUst : cok(p[i - 1].mAlt, p[i].mUst);
  y += aralik;
  console.log(`${p[i].ad}: aralik=${String(aralik).padStart(2)}  ust=${String(y).padStart(3)}  alt=${y + p[i].yukseklik}`);
  y += p[i].yukseklik;
}
console.log(`toplam yukseklik: ${y}`);
```

```
--- iki kardes arasi ---
alt= 32 ust= 16 -> aralik= 32   (toplasaydi 48)
alt= 16 ust= 32 -> aralik= 32   (toplasaydi 48)
alt= 24 ust= 24 -> aralik= 24   (toplasaydi 48)
alt= 32 ust= -8 -> aralik= 24   (toplasaydi 24)
alt= -8 ust=-16 -> aralik=-16   (toplasaydi -24)
alt=  0 ust= 24 -> aralik= 24   (toplasaydi 24)

--- ust oge ile ilk cocuk (kenarlik/dolgu yoksa disari tasar) ---
ustPadding=0 ustBorder=0 -> disari=32 iceride=0 cokme=true
ustPadding=1 ustBorder=0 -> disari=16 iceride=32 cokme=false
ustPadding=0 ustBorder=1 -> disari=16 iceride=32 cokme=false

--- bos kutu kendi icinde coker ---
ust=20 alt=12, icerik yok -> kutunun toplam dikey yeri = 20

--- akista uc paragraf ---
p1: aralik= 0  ust=  0  alt=24
p2: aralik=16  ust= 40  alt=88
p3: aralik=32  ust=120  alt=144
toplam yukseklik: 144
```

İlk blokta üçüncü satır kuralı en açık gösteriyor: iki komşu 24'er birim boşluk verdiğinde
aradaki mesafe 48 değil 24 oluyor. Toplansaydı, her paragrafa aynı boşluğu vermek
paragraflar arasını iki katına çıkarırdı.

Çökmenin gerekçesi budur: bir metin belgesinde her bloka "benden önce ve sonra bu kadar
boşluk olsun" denir ve komşu istekler birbirini iki katına çıkarmamalıdır. Çökme kuralı,
her bloğun kendi isteğini bağımsız bildirmesini ve sonucun ikisinden büyüğü olmasını sağlar.

#### Çökmenin Üç Durumu

Çökme yalnızca kardeşler arasında olmaz.

**Kardeşler arasında.** Bir kutunun alt dış boşluğu ile sonrakinin üst dış boşluğu çöker.
Çıktının ilk bloğu bunu gösterir.

**Üst öge ile ilk/son çocuk arasında.** Bir kutunun üst dış boşluğu ile ilk çocuğunun üst
dış boşluğu çöker ve **dışarı taşar**. Çıktının ikinci bloğu bunu veriyor: 16 ve 32 birimlik
boşluklar birleşip 32 olarak kutunun dışına çıktı, içeride sıfır kaldı. Alt tarafta aynı
şey son çocukla olur.

Bu davranış çoğu zaman şaşırtıcıdır: bir bölüme arka plan verildiğinde çocuğun üst boşluğu
renkli alanın içinde değil, dışında görünür. Aynı bloğun sonraki iki satırı çözümü de
gösteriyor: üst ögeye bir birimlik iç boşluk ya da kenarlık eklendiğinde çökme durur ve
boşluk içeride kalır.

**Boş kutu kendi içinde çöker.** İçeriği, yüksekliği, iç boşluğu ve kenarlığı olmayan bir
kutunun kendi üst ve alt dış boşlukları birbiriyle çöker. Çıktının üçüncü bloğunda 20 ve 12
birimlik boşluklar tek bir 20 birime indi.

#### Çökme Ne Zaman Olmaz

Çökme evrensel değildir. Şu durumlarda olmaz:

- **Satır içi eksende hiç olmaz.** Yan yana duran iki kutunun yatay dış boşlukları her zaman
  toplanır. Çökme yalnızca blok eksende tanımlıdır.
- Kutular arasında **kenarlık ya da iç boşluk** varsa.
- Kutu **akıştan çıkarılmışsa**: mutlak konumlandırılmış ya da kaydırılmış kutular çökmeye
  katılmaz.
- Kutu bir **bağımsız akış kökü** ise: `display: flow-root`, `overflow` değeri `visible`
  dışında olan kutular ve esnek kutu ya da ızgara düzeni içindeki çocuklar çökmez.

Son madde pratikte en çok kullanılanıdır. Bir düzen modelinde `gap` ile verilen aralıklar
çökmez; bildirilen değer olduğu gibi uygulanır. Bu, aralıkları öngörülebilir kılar ve bir
sonraki kursta düzen sistemlerinin çökme sorununu neden ortadan kaldırdığını açıklar.

#### Satır İçi Yerleştirme

Satır içi kutular blok eksende yığılmaz; **satır kutularına (line box)** yerleştirilir. Bir
satır kutusu, kapsayan bloğun satır içi eksenini doldurur ve içine sığdığı kadar satır içi
kutu alır. Sığmayan bir sonraki satır kutusuna geçer.

Satır kutusunun yüksekliği, içindeki kutuların yüksekliklerinden ve `line-height`
değerlerinden hesaplanır. Bu hesabın ayrıntısı tipografi dersinde ele alınacak; burada
önemli olan iki sonucudur.

Birincisi, bir satır içi kutuya dikey dış boşluk verilmesinin satır kutusunu büyütmemesidir
— önceki derste çizelgede görülen davranışın sebebi budur.

İkincisi, satır kutuları arasındaki mesafeyi belirleyen şeyin dış boşluk değil,
`line-height` olmasıdır. Paragraf içi satır aralığı bu yüzden dış boşlukla ayarlanamaz.

#### Akışın Pratik Sonucu

Normal akışın kuralları, bir belgenin **hiçbir düzen bildirimi olmadan** okunabilir
olmasını sağlar. Bloklar yığılır, metin satırlara bölünür, kutular kapsayanlarını doldurur.
Web'in Temelleri ve HTML kursunda yazılan istasyon belgesi, tek bir CSS satırı olmadan da
baştan sona okunabilirdi.

Buradan çıkan yazım disiplini şudur: bir yerleşim sorunu önce akışın kurallarıyla çözülmeye
çalışılır. Kutuları akıştan çıkaran bildirimler — kaydırma ve konumlandırma — akışın
veremediği bir şey gerektiğinde yazılır, akış bilinmediği için değil.

```css
/* istasyon.css — 11. adım: akışa dayalı aralıklar */
main > * + * { margin-block-start: 1rem; }

main > section { margin-block-start: 2.5rem; }

.kunye > :first-child { margin-block-start: 0; }
```

İlk kural, ana içerikteki her kardeş çifti arasına bir aralık koyar ve ilk ögeye
dokunmaz — kardeş birleştiricisinin doğal sonucu. İkinci kural bölümler arasını genişletir
ve çökme sayesinde birinciyle **toplanmaz**: 1rem ve 2.5rem çöker ve sonuç 2.5rem olur.
Üçüncü kural, künyenin ilk çocuğunun boşluğunun dışarı taşmasını engellemek yerine sıfırlar.

#### Özet

- Normal akışta blok kutular blok ekseninde yığılır, satır içi kutular satır kutularına
  yerleştirilir; sığmayan bir sonraki satır kutusuna geçer.
- Blok eksendeki iki komşu dış boşluk toplanmaz, çöker: ikisi de pozitifse en büyüğü, ikisi
  de negatifse en küçüğü, karışıksa toplamı geçerlidir.
- Çökme üç durumda olur: kardeşler arasında, üst öge ile ilk/son çocuk arasında ve boş bir
  kutunun kendi içinde. İkincisinde boşluk üst kutunun dışına taşar.
- Çökme satır içi eksende hiç olmaz; araya kenarlık ya da iç boşluk girdiğinde, kutu akıştan
  çıktığında ya da bağımsız bir akış kökü olduğunda durur.
- Satır kutuları arasındaki mesafe dış boşlukla değil `line-height` ile belirlenir; satır
  içi kutuya verilen dikey dış boşluk satır yüksekliğini değiştirmez.

#### Sonraki Adım

Akış kutuları tek yönde diziyor: bloklar alt alta, satır içi kutular yan yana. Bir görselin
metnin yanında durup metnin çevresinden akmasını istemek bu iki kalıba da uymaz. Sonraki
ders bu ihtiyaç için tanımlanmış olan kaydırmayı ve onun getirdiği yükseklik sorununu ele
alır.

### Kayan Öğeler

Source: https://academia.sh/tr/kurslar/css-temelleri/kayan-ogeler

Kaydırmanın satır kutularını kısaltarak metni bir kutunun çevresinden akıtması; kapsayan kutunun yükseklik sorunu ve temizleme ile akış kökü çözümleri.

Normal akış kutuları iki kalıpla diziyordu: bloklar alt alta, satır içi kutular yan yana.
İstasyon sayfasının konum bölümünde bir istek var ki iki kalıba da uymuyor — konum görseli
metnin solunda dursun, açıklama metni de görselin **çevresinden aksın**.

Bu davranış için tanımlanmış mekanizmanın adı **kaydırma (float)**'dır. Kaydırma, bir kutuyu
satır içi eksenin bir kenarına iter ve akıştaki satır kutularının onun etrafından geçmesini
sağlar. Bu ders kaydırmanın ne yaptığını, ne yapmadığını ve getirdiği yükseklik sorununu
ele alır.

#### Kaydırma Satır Kutularını Kısaltır

Kaydırılmış bir kutu normal akıştan çıkarılır ama akışı **etkilemeye devam eder**. Etkisi
tek bir yerdedir: kendisiyle aynı dikey aralıkta kalan satır kutuları kısalır.

```js
// kaydirma.mjs — kaydirilmis kutunun satir kutularini nasil kisalttigini hesaplar
function satirlar({ kapsayanEn, kaydiranEn, kaydiranYukseklik, satirYuksekligi, satirSayisi }) {
  const c = [];
  for (let i = 0; i < satirSayisi; i++) {
    const ust = i * satirYuksekligi;
    const kaydiranIcinde = ust < kaydiranYukseklik;
    const kullanilabilir = kaydiranIcinde ? kapsayanEn - kaydiranEn : kapsayanEn;
    c.push({ satir: i + 1, ust, kullanilabilir, baslangic: kaydiranIcinde ? kaydiranEn : 0 });
  }
  return c;
}

for (const s of satirlar({ kapsayanEn: 640, kaydiranEn: 200, kaydiranYukseklik: 120, satirYuksekligi: 24, satirSayisi: 7 })) {
  console.log(`satir ${s.satir}: ust=${String(s.ust).padStart(3)}  x=${String(s.baslangic).padStart(3)}..640  kullanilabilir en=${s.kullanilabilir}`);
}

console.log("\n--- kapsayanin yuksekligi ---");
function kapsayanYukseklik({ akisIcerikYuksekligi, kaydiranYukseklik, akisKoku }) {
  return akisKoku ? Math.max(akisIcerikYuksekligi, kaydiranYukseklik) : akisIcerikYuksekligi;
}
for (const akisKoku of [false, true]) {
  const h = kapsayanYukseklik({ akisIcerikYuksekligi: 48, kaydiranYukseklik: 120, akisKoku });
  console.log(`akis koku=${String(akisKoku).padEnd(5)} -> kapsayan yukseklik=${h}`);
}
```

```
satir 1: ust=  0  x=200..640  kullanilabilir en=440
satir 2: ust= 24  x=200..640  kullanilabilir en=440
satir 3: ust= 48  x=200..640  kullanilabilir en=440
satir 4: ust= 72  x=200..640  kullanilabilir en=440
satir 5: ust= 96  x=200..640  kullanilabilir en=440
satir 6: ust=120  x=  0..640  kullanilabilir en=640
satir 7: ust=144  x=  0..640  kullanilabilir en=640

--- kapsayanin yuksekligi ---
akis koku=false -> kapsayan yukseklik=48
akis koku=true  -> kapsayan yukseklik=120
```

İlk beş satır kutusu 640 yerine 440 birim genişlikte ve 200 biriminden başlıyor: kaydırılmış
görsel o aralıkta duruyor. Altıncı satır, görselin alt kenarını geçtiği için tam genişliğe
dönüyor. Metnin görselin çevresinden akması budur — gizemli bir sarma davranışı değil,
satır kutularının kısaltılmasıdır.

Buradan iki sonuç çıkar.

**Kaydırma yalnızca satır kutularını etkiler, blok kutuları değil.** Kaydırılmış bir kutunun
yanındaki blok kutu daralmaz; kapsayanı doldurmaya devam eder ve kaydırılmış kutunun
altından geçer. Yalnızca o blok kutunun içindeki metin satırları kısalır. Bu, kaydırmayı
sütun düzeni kurmak için kullanmanın neden kırılgan olduğunu gösterir.

**Kaydırma yönlüdür.** `float: inline-start` kutuyu satır içi eksenin başına,
`float: inline-end` sonuna iter. Fiziksel karşılıkları `left` ve `right`'tır; mantıksal
değerler yazma yönüne uyum sağlar.

#### Kapsayanın Yüksekliği Çöker

Çıktının ikinci bloğu, kaydırmanın asıl sorununu veriyor. Kaydırılmış kutu akıştan
çıkarıldığı için kapsayanının yüksekliğine **katılmaz**. Kapsayan kutuda 48 birimlik akış
içeriği ve 120 birimlik kaydırılmış bir görsel varsa, kapsayanın yüksekliği 48 olur; görsel
kutunun dışına taşar.

Bunun görünür sonucu, kapsayana verilen arka planın ya da kenarlığın görseli kapsamamasıdır.
Sorun bir hata değil, tanımın doğrudan sonucudur: akıştan çıkmış bir kutu, akışın
yüksekliğine katkı vermez.

İki çözüm vardır.

#### Çözüm 1: Temizleme

`clear` özelliği bir kutuya, kendisinden önceki kaydırılmış kutuların **altına** inmesini
söyler.

```css
.temiz { clear: both; }
```

`clear: inline-start` yalnızca başa kaydırılmışların, `inline-end` yalnızca sona
kaydırılmışların, `both` ikisinin de altına iner.

Temizleme, kapsayanın yüksekliğini dolaylı olarak düzeltmekte kullanılır: kapsayanın son
çocuğu olarak temizleyen bir kutu konursa, o kutu kaydırılanın altına iner ve kapsayan
onu içermek için uzar. Bu, bir sözde elemanla belge kirletmeden yapılabilir:

```css
.temizleyen::after {
  content: "";
  display: block;
  clear: both;
}
```

Çözüm işler ama dolaylıdır: yükseklik sorununu çözmek için bir içerik ögesi üretilir.

#### Çözüm 2: Bağımsız Akış Kökü

Doğrudan çözüm, kapsayanı bir **bağımsız akış kökü (block formatting context)** yapmaktır.
Böyle bir kutu, içindeki kaydırılmış kutuları kendi yüksekliğine katar; çıktının son
satırında `akis koku=true` durumunda yüksekliğin 120 olması bunu gösteriyor.

Bir kutuyu akış kökü yapmanın açık yolu:

```css
.konum-bolumu { display: flow-root; }
```

Aynı sonucu veren dolaylı yollar da vardır: `overflow` değerinin `visible` dışında bir şey
olması, kutunun esnek kutu ya da ızgara düzeni içinde bulunması, mutlak konumlandırılmış
olması. Bunlar yan etkileriyle birlikte gelir — `overflow: hidden` yazmak taşan içeriği de
kırpar. `flow-root` ise yalnızca istenen işi yapar.

Akış kökünün üç etkisi vardır ve üçü de önceki derslerde karşımıza çıkmıştı:

- İçindeki kaydırılmış kutular yüksekliğe katılır.
- Dışarıdaki kaydırılmış kutular içine taşmaz.
- Dış boşluk çökmesi kutunun sınırında durur.

Üçü de aynı tanımdan gelir: kutu, blok yerleşimini kendi içinde tamamlar.

#### Kaydırılmış Kutunun Kendi Ölçüsü

Kaydırılmış bir kutu blok kutu üretir ama kapsayanını doldurmaz. Genişliği
bildirilmediğinde **içeriğine göre daralır**: kutu, içine sığdırabileceği en dar ölçüye
çekilir. Bu, akıştaki bir blok kutunun davranışının tersidir.

Sonuç, kaydırılmış bir kutuya genişlik bildirmenin çoğu zaman zorunlu olmasıdır. Metin
taşıyan bir kutu daraltıldığında satır sayısı artar ve kutu beklenmedik biçimde uzar;
görsel taşıyan bir kutu ise görselin doğal ölçüsüne oturur.

İkinci bir davranış, kaydırılmış kutuların **birbirini itmesidir**. Aynı kenara birden çok
kutu kaydırıldığında yan yana dizilirler; satıra sığmayan bir alt sıraya iner. İnme
noktası, önceki kaydırılmış kutuların alt kenarlarıyla belirlenir ve kutular farklı
yüksekliklerdeyse dizilim düzensizleşir. Kaydırmayla ızgara benzeri bir düzen kurmanın
kırılganlığının bir başka kaynağı budur.

#### Kaydırma Sınırının Biçimi

Satır kutularını kısaltan sınır, öntanımlı olarak kaydırılmış kutunun dış boşluk
dikdörtgenidir — biçimi ne olursa olsun bir dikdörtgen. Bu sınır `shape-outside`
özelliğiyle değiştirilebilir:

```css
.konum-gorseli {
  float: inline-start;
  shape-outside: circle(50%);
  shape-margin: 12px;
}
```

`shape-outside` yalnızca akan metnin sınırını değiştirir; kutunun kendisini kırpmaz.
Görselin dairesel görünmesi isteniyorsa `clip-path` ayrıca yazılır. İki özellik farklı
işler görür: biri komşuların nereden geçeceğini, diğeri kutunun neresinin çizileceğini
belirler.

#### Kaydırma Ne İçin Kullanılır

Kaydırma, sütunlu sayfa düzenleri kurmak için uzun süre kullanılmış bir araçtır. Bu kullanım
kaydırmanın tanımına uymaz ve çıktının ilk bloğu sebebini gösterir: kaydırma bir kutuyu bir
sütuna yerleştirmez, yalnızca satır kutularını kısaltır. Sütun yüksekliklerinin
eşitlenmesi, kutuların hizalanması ve aralıklandırılması ayrıca çözülmesi gereken sorunlar
olarak kalır.

Kaydırmanın kendi tanımına uyan tek kullanımı **metnin bir kutunun çevresinden akmasıdır**.
Bir sonraki kursta ele alınacak düzen sistemleri sütun kurma işini üstlenir ve bu işi
kaydırmadan daha az bildirimle, daha öngörülebilir biçimde yapar. Kaydırma o kurslarda da
yerinde kalır, çünkü hiçbir düzen modeli metni bir kutunun çevresinden akıtmaz.

```css
/* istasyon.css — 12. adım: konum görseli ve çevresinden akan metin */
.konum-bolumu { display: flow-root; }

.konum-gorseli {
  float: inline-start;
  inline-size: 200px;
  margin-inline-end: 16px;
  margin-block-end: 8px;
}

.konum-bolumu .kaynak-notu { clear: both; }
```

`display: flow-root` bildirimi, bölümün görseli içermesini sağlar. Görsele verilen sona
doğru dış boşluk, metnin görsele yapışmasını önler — kaydırılmış kutunun dış boşluk kenarı
satır kutularını kısaltan sınırdır, kenarlık kenarı değil.

Son kural kaynak notunu görselin altına indirir; not metnin sağ yanında değil, bölümün
sonunda okunmalıdır.

#### Özet

- Kaydırılmış kutu normal akıştan çıkar ama kendisiyle aynı dikey aralıktaki satır
  kutularını kısaltır; metnin çevresinden akması budur.
- Kaydırma satır kutularını etkiler, blok kutuları etkilemez; bu yüzden sütun düzeni kurma
  aracı olarak kırılgandır.
- Kaydırılmış kutu kapsayanının yüksekliğine katılmaz; kapsayan çöker ve arka planı,
  kenarlığı kaydırılmış içeriği kapsamaz.
- `clear` bir kutuyu kaydırılmışların altına indirir; kapsayanın son çocuğu olarak
  kullanıldığında yüksekliği dolaylı olarak düzeltir.
- `display: flow-root` kutuyu bağımsız akış kökü yapar: kaydırılmışları yüksekliğe katar,
  dışarıdaki kaydırılmışları içeri almaz ve dış boşluk çökmesini sınırında durdurur.

#### Sonraki Adım

Kaydırma bir kutuyu akıştan çıkardı ama nereye gideceğini seçtirmedi; kutu yalnızca bir
kenara itildi. Bir ögeyi belgede istenen bir noktaya yerleştirmek — bir rozeti bir kartın
köşesine, bir başlığı kaydırmada sabit tutmak — başka bir mekanizma ister. Sonraki ders
konumlandırmanın beş değerini ve her birinin hangi kutuya göre ölçüldüğünü tanımlar.

### Konumlandırma

Source: https://academia.sh/tr/kurslar/css-temelleri/konumlandirma

Statik, göreli, mutlak, sabit ve yapışkan konumlandırmanın kutuyu nereye koyduğu; kapsayan blok kavramı ve ofset özelliklerinin neye göre ölçüldüğü.

Kaydırma bir kutuyu akıştan çıkardı ama nereye gideceğini seçtirmedi; kutu yalnızca bir
kenara itildi. İstasyon sayfasında bunun yetmediği yerler var: bir durum rozetinin kartın
köşesinde durması, ölçüm çizelgesinin sütun başlıklarının kaydırma sırasında görünür
kalması.

Bu ders `position` özelliğinin beş değerini tanımlar. Her değerin iki sorusu vardır: kutu
akışta kalıyor mu ve ofset değerleri **hangi kutuya göre** ölçülüyor?

#### Beş Değer, İki Soru

```js
// konum.mjs — bes konumlandirma degerinin kutuyu nereye koydugunu hesaplar
const KAPSAYAN = { ad: "main",  x: 40,  y: 100, en: 640, boy: 800 };
const KART     = { ad: "kart",  x: 80,  y: 200, en: 400, boy: 240 }; // akistaki yeri
const GORUNTU  = { ad: "goruntu alani", x: 0, y: 0, en: 1024, boy: 768 };

function yerlestir({ konum, akistakiYer, kapsayanBlok, ofset, kaydirmaY = 0 }) {
  const { ust = null, sol = null, en = 120, boy = 40 } = ofset;
  switch (konum) {
    case "static":
      return { x: akistakiYer.x, y: akistakiYer.y, en: akistakiYer.en, boy: akistakiYer.boy, akista: true };
    case "relative":
      return { x: akistakiYer.x + (sol ?? 0), y: akistakiYer.y + (ust ?? 0),
               en: akistakiYer.en, boy: akistakiYer.boy, akista: true };
    case "absolute":
      return { x: kapsayanBlok.x + (sol ?? 0), y: kapsayanBlok.y + (ust ?? 0), en, boy, akista: false };
    case "fixed":
      return { x: GORUNTU.x + (sol ?? 0), y: GORUNTU.y + (ust ?? 0), en, boy, akista: false };
    case "sticky": {
      const esik = ust ?? 0;
      const akmisY = akistakiYer.y - kaydirmaY;
      return { x: akistakiYer.x, y: Math.max(esik, akmisY),
               en: akistakiYer.en, boy: akistakiYer.boy, akista: true, yapisti: akmisY < esik };
    }
  }
}

const rozet = { x: 0, y: 0, en: 120, boy: 40 };
console.log("kapsayan blok: main  x=40 y=100 640x800");
console.log("akistaki yer : rozet x=0  y=0   120x40\n");

for (const konum of ["static", "relative", "absolute", "fixed"]) {
  const r = yerlestir({ konum, akistakiYer: rozet, kapsayanBlok: KART, ofset: { ust: 16, sol: 12 } });
  console.log(`${konum.padEnd(9)} -> x=${String(r.x).padStart(4)} y=${String(r.y).padStart(4)}  ${r.en}x${r.boy}  akista=${r.akista}`);
}
console.log("  (absolute icin kapsayan blok = kart: x=80 y=200)");

console.log("\n--- sticky: baslik, esik ust=0, kaydirma ilerledikce ---");
const baslik = { x: 40, y: 300, en: 640, boy: 48 };
for (const kaydirmaY of [0, 200, 300, 400, 520]) {
  const r = yerlestir({ konum: "sticky", akistakiYer: baslik, kapsayanBlok: KAPSAYAN, ofset: { ust: 0 }, kaydirmaY });
  console.log(`kaydirma=${String(kaydirmaY).padStart(3)} -> ekranda y=${String(r.y).padStart(3)}  yapisti=${r.yapisti}`);
}
```

```
kapsayan blok: main  x=40 y=100 640x800
akistaki yer : rozet x=0  y=0   120x40

static    -> x=   0 y=   0  120x40  akista=true
relative  -> x=  12 y=  16  120x40  akista=true
absolute  -> x=  92 y= 216  120x40  akista=false
fixed     -> x=  12 y=  16  120x40  akista=false
  (absolute icin kapsayan blok = kart: x=80 y=200)

--- sticky: baslik, esik ust=0, kaydirma ilerledikce ---
kaydirma=  0 -> ekranda y=300  yapisti=false
kaydirma=200 -> ekranda y=100  yapisti=false
kaydirma=300 -> ekranda y=  0  yapisti=false
kaydirma=400 -> ekranda y=  0  yapisti=true
kaydirma=520 -> ekranda y=  0  yapisti=true
```

#### Değerlerin Okunması

**`static`** — başlangıç değeridir. Kutu normal akıştaki yerinde durur ve ofset özellikleri
(`top`, `right`, `bottom`, `left`; mantıksal karşılıkları `inset-block-start` ve
akrabaları) **yok sayılır**. Çıktıda ofset verildiği hâlde konum değişmedi.

**`relative`** — kutu akıştaki yerinden ofset kadar kaydırılır ama **akıştaki yerini korur**.
Çıktıda kutu 12 ve 16 birim kaydı; komşuları bu kaymadan etkilenmez ve kutunun eski yeri boş
kalır. Bu, göreli konumlandırmanın hem gücü hem sınırıdır: görsel bir düzeltme yapar, yerleşimi
yeniden düzenlemez.

**`absolute`** — kutu akıştan **çıkarılır** ve ofsetler kapsayan bloğa göre ölçülür.
Çıktıda kart $x=80$, $y=200$ noktasındaydı; rozet $92, 216$ noktasına yerleşti. Kutu
akıştan çıktığı için genişliği de artık kapsayanı doldurmaz — içeriğine göre daralır.

**`fixed`** — kutu akıştan çıkarılır ve ofsetler **görüntü alanına** göre ölçülür; ataları
arasında `transform`, `filter` ya da `perspective` değeri `none` dışında olan biri varsa kapsayan
blok görüntü alanı değil o atadır. Çıktıda kart nerede olursa olsun kutu $12, 16$ noktasına
yerleşti. Sayfa kaydırıldığında kutu ekranda sabit kalır.

**`sticky`** — ikisi arasındadır ve çıktının ikinci bloğunda görülüyor.

#### Kapsayan Blok Nasıl Belirlenir

Mutlak konumlandırmanın en çok yanlış anlaşılan yanı, "kapsayan blok"un ne olduğudur.
Kural şudur: kutu, **en yakın konumlandırılmış atasına** göre yerleşir. Konumlandırılmış
demek, `position` değeri `static` **dışında** bir şey olan demektir.

Hiçbir atası konumlandırılmamışsa kapsayan blok, belgenin ilk kutusudur; kutu sayfanın
tamamına göre yerleşir — çoğu zaman istenmeyen sonuç budur.

Bu, standart bir eşleşmeyi doğurur:

```css
.kart          { position: relative; }
.kart .rozet   { position: absolute; inset-block-start: 8px; inset-inline-end: 8px; }
```

Karta `position: relative` verilir ama hiçbir ofset yazılmaz. Amaç kartı kaydırmak değil,
onu bir **kapsayan blok** hâline getirmektir. Rozet artık kartın köşesine yerleşir.

Ofsetlerin kısayolu `inset`'tir: `inset: 0` dört ofseti de sıfırlar ve mutlak
konumlandırılmış bir kutuyu kapsayan bloğuna tam olarak yayar.

Karşıt iki ofset birlikte verildiğinde — `inset-inline-start: 0` ve `inset-inline-end: 0`
— kutunun genişliği ikisi arasındaki mesafeye zorlanır. Bu, `width` yazmadan genişlik
vermenin yoludur.

#### Yapışkan Konum Bir Eşik Davranışıdır

`position: sticky` kutuyu akışta bırakır. Kutu normal yerinde durur; ta ki kaydırma
sırasında bildirilen eşiğe ulaşana kadar. O noktadan sonra kutu eşikte **yapışır**.

Çıktının ikinci bloğu bunu adım adım gösteriyor. Başlık akışta $y = 300$ noktasındaydı ve
eşik `inset-block-start: 0` olarak verildi. Kaydırma 200 birim ilerlediğinde başlık ekranda
100 noktasına çıktı — hâlâ akışla birlikte hareket ediyor. Kaydırma 300'e ulaştığında
başlık eşiğe değdi; 400 ve 520'de ekranda 0 noktasında kaldı, yani yapıştı.

İki koşul gerekir ve ikisi de sıkça atlanır.

**Bir eşik bildirilmelidir.** `position: sticky` tek başına hiçbir şey yapmaz; en az bir
ofset özelliği yazılmalıdır.

**Yapışma kapsayan kutunun sınırları içindedir.** Kutu, kapsayanının dışına çıkmaz; kapsayan
kaydırılıp geçildiğinde kutu da onunla birlikte gider. Bu, bir bölüm başlığının yalnızca
kendi bölümü ekrandayken yapışmasını sağlar — istenen davranış genellikle budur.

Bir de engel vardır: yapışkan kutu, kaydırılabilir bir atası varsa ona göre yapışır. Aradaki
atalardan birinde `overflow: hidden` bulunması yapışmayı sessizce durdurur. Yapışmayan bir
kutuda ilk bakılacak yer budur.

#### Akıştan Çıkmanın Bedeli

Mutlak ve sabit konumlandırma kutuyu akıştan çıkarır. Bunun bedeli, kutunun **kapsayanının
yüksekliğine katılmamasıdır** — kaydırmada gördüğümüz sorunun aynısı.

Sonuç, mutlak konumlandırılmış içeriğin altındaki içerikle çakışabilmesidir. Bu yüzden
mutlak konumlandırma, ölçüsü içerikle birlikte değişen bir şey için değil, ölçüsü sabit ya
da çakışması sorun olmayan bir şey için kullanılır: rozet, kapatma düğmesi, dekoratif
işaret.

Sayfanın ana düzenini konumlandırmayla kurmak, akışın verdiği bütün otomatik davranışları
elle yeniden yazmak demektir. Genel kural şudur: **düzen akışla kurulur, konumlandırma
akışın veremediği tek tek istisnalar içindir.**

```css
/* istasyon.css — 13. adım: konumlandırma */
.kart { position: relative; }

.kart .durum-rozeti {
  position: absolute;
  inset-block-start: 8px;
  inset-inline-end: 8px;
}

.olcum-cizelgesi thead th {
  position: sticky;
  inset-block-start: 0;
  background-color: #f5f7f8;
}

.atlama-baglantisi {
  position: absolute;
  inset-block-start: -100px;
}

.atlama-baglantisi:focus {
  position: fixed;
  inset-block-start: 8px;
  inset-inline-start: 8px;
}
```

Çizelge başlığındaki arka plan rengi zorunludur: yapışan başlık, altından geçen satırların
üstünde çizilir ve saydam kalırsa iki metin üst üste okunur.

Son iki kural bir erişilebilirlik kalıbıdır. İçeriğe atlama bağlantısı görüş alanının
dışında durur, klavye odağı ona geldiğinde sabit konuma geçip görünür olur. Kural
`display: none` ile yazılamazdı — kutu üretmeyen bir öge odaklanamaz.

#### Özet

- `static` kutuyu akıştaki yerinde bırakır ve ofsetleri yok sayar; `relative` kutuyu
  ofset kadar kaydırır ama akıştaki yerini korur.
- `absolute` ve `fixed` kutuyu akıştan çıkarır; birincisi en yakın konumlandırılmış ataya,
  ikincisi görüntü alanına göre ölçer — `transform`, `filter` ya da `perspective` taşıyan bir
ata varsa `fixed` için de kapsayan blok o atadır.
- Mutlak konumlandırma için kapsayan blok, `position` değeri `static` dışında olan en yakın
  atadır; ofsetsiz `position: relative` yazmanın işi budur.
- `sticky` kutuyu akışta bırakır ve bildirilen eşiğe ulaşınca orada tutar; en az bir ofset
  bildirilmelidir ve yapışma kapsayan kutunun sınırlarıyla çevrilidir.
- Akıştan çıkmış kutu kapsayanının yüksekliğine katılmaz; bu yüzden konumlandırma ana düzen
  için değil, akışın veremediği istisnalar için kullanılır.

#### Sonraki Adım

Konumlandırma kutuları üst üste bindirebiliyor: rozet kartın üzerinde, yapışan başlık
satırların üzerinde duruyor. Peki iki kutu aynı noktada çakışırsa hangisi üstte çizilir?
`z-index` yazmak her zaman işe yaramaz — bazen daha yüksek bir değer daha altta kalır.
Sonraki ders çizim sırasını ve bu davranışın sebebi olan yığılma bağlamını tanımlar.

### Yığılma Bağlamı

Source: https://academia.sh/tr/kurslar/css-temelleri/yigilma-baglami

Kutuların çizim sırası; z-index değerlerinin yalnızca kardeşler arasında karşılaştırılması ve yığılma bağlamını kuran bildirimler.

Konumlandırma kutuları üst üste bindirdi: rozet kartın üzerinde, yapışan başlık satırların
üzerinde duruyor. Bir soru açıkta kaldı — iki kutu aynı noktada çakışırsa hangisi üstte
çizilir?

Yanıt `z-index` yazmakla bitmiyor. Bu özelliğin sık karşılaşılan bir davranışı vardır:
bazen daha yüksek bir değer daha altta kalır. Sebep bir hata değil, **yığılma bağlamı
(stacking context)** adı verilen bir yapıdır. Bu ders o yapıyı tanımlar.

#### Varsayılan Çizim Sırası

`z-index` hiç yazılmadığında bile bir sıra vardır. Aynı yığılma bağlamındaki kutular
kabaca şu sırayla çizilir — önce çizilen altta kalır:

1. Bağlamı kuran kutunun arka planı ve kenarlığı.
2. Negatif `z-index` değerli konumlandırılmış kutular.
3. Akıştaki blok kutuların arka planları.
4. Kaydırılmış kutular.
5. Satır içi içerik ve metin.
6. `z-index: auto` ya da `0` olan konumlandırılmış kutular.
7. Pozitif `z-index` değerli konumlandırılmış kutular.

Bu listeden iki gözlem çıkar. Birincisi, **konumlandırılmış bir kutu akıştaki kutuların
üstünde çizilir**; `z-index` yazmaya gerek yoktur. Bir rozetin kartın üzerinde görünmesi
için `position: absolute` yeter.

İkincisi, aynı düzeydeki kutular arasında **belge sırası** belirleyicidir: sonra gelen üstte
çizilir.

#### z-index Yalnızca Kardeşler Arasında Karşılaştırılır

`z-index` bir kutuya küresel bir yükseklik vermez. Değer, yalnızca kutunun **kendi yığılma
bağlamı** içindeki kardeşleriyle karşılaştırılır.

Bir kutu yığılma bağlamı kurduğunda, içindeki bütün kutular o bağlamın içine kapanır.
Dışarıdaki kutularla karşılaştırılan şey artık çocukların değerleri değil, **bağlamı kuran
kutunun** değeridir.

```js
// yigilma.mjs — yigilma baglamlarini agac olarak cozer ve cizim sirasini uretir
const agac = {
  ad: "kok", zIndex: "auto", baglamKurar: true, cocuk: [
    { ad: "A", zIndex: 1, baglamKurar: true, cocuk: [
      { ad: "A1", zIndex: 999, baglamKurar: true, cocuk: [] },
      { ad: "A2", zIndex: 5,   baglamKurar: true, cocuk: [] },
    ]},
    { ad: "B", zIndex: 2, baglamKurar: true, cocuk: [
      { ad: "B1", zIndex: 1, baglamKurar: true, cocuk: [] },
    ]},
    { ad: "C", zIndex: "auto", baglamKurar: false, cocuk: [] },
  ],
};

// bir baglam icindeki cocuklar z-index'e gore, esitse belge sirasina gore siralanir
function ciz(dugum, derinlik = 0, onEk = "") {
  const yol = onEk ? `${onEk} > ${dugum.ad}` : dugum.ad;
  const z = dugum.zIndex === "auto" ? 0 : dugum.zIndex;
  console.log(`${"  ".repeat(derinlik)}${dugum.ad.padEnd(4)} z=${String(dugum.zIndex).padEnd(4)} yol=${yol}`);
  const sirali = [...dugum.cocuk]
    .map((c, i) => ({ c, i }))
    .sort((x, y) => {
      const zx = x.c.zIndex === "auto" ? 0 : x.c.zIndex;
      const zy = y.c.zIndex === "auto" ? 0 : y.c.zIndex;
      return zx - zy || x.i - y.i;
    });
  for (const { c } of sirali) ciz(c, derinlik + 1, yol);
  return z;
}

console.log("cizim sirasi (ustteki alttan uste dogru, ic ice):");
ciz(agac);

console.log("\n--- A1 ile B karsilastirmasi ---");
console.log("A1.z = 999, B.z = 2");
console.log("A1 kok baglaminda degil; A (z=1) baglaminin icinde.");
console.log("Kok baglaminda karsilastirilan degerler: A=1, B=2  -> B ustte.");
console.log("Sonuc: A1 (999) tum B agacinin ALTINDA kalir.");
```

```
cizim sirasi (ustteki alttan uste dogru, ic ice):
kok  z=auto yol=kok
  C    z=auto yol=kok > C
  A    z=1    yol=kok > A
    A2   z=5    yol=kok > A > A2
    A1   z=999  yol=kok > A > A1
  B    z=2    yol=kok > B
    B1   z=1    yol=kok > B > B1

--- A1 ile B karsilastirmasi ---
A1.z = 999, B.z = 2
A1 kok baglaminda degil; A (z=1) baglaminin icinde.
Kok baglaminda karsilastirilan degerler: A=1, B=2  -> B ustte.
Sonuc: A1 (999) tum B agacinin ALTINDA kalir.
```

Son blok, bu dersin çözdüğü sorunun tam ifadesidir. `A1` kutusunun `z-index` değeri 999 —
sayfadaki en yüksek değer. Buna karşın 2 değerli `B` kutusunun **altında** kalıyor.

Sebep, `A1` kutusunun kök bağlamda bulunmamasıdır. Kök bağlamda karşılaştırılan iki değer
vardır: `A` için 1 ve `B` için 2. `B` kazanır ve bütün alt ağacıyla birlikte `A`'nın
üstünde çizilir. `A1` kutusunun 999 değeri yalnızca kardeşi `A2` ile karşılaştırılır.

Uygulanabilir sonuç şudur: bir kutu beklendiği kadar yukarı çıkmıyorsa çözüm değeri
büyütmek değildir. Değeri milyona çıkarmak da işe yaramaz. Çözüm, **atalar zincirinde
hangi kutunun bağlam kurduğunu bulmak** ve karşılaştırmayı o düzeyde yapmaktır.

#### Yığılma Bağlamını Ne Kurar

Bir kutu şu koşullardan biri sağlandığında yığılma bağlamı kurar:

- Belgenin kök ögesi (her zaman bir bağlam kurar).
- `position` değeri `static` dışında ve `z-index` değeri `auto` dışında olan kutular.
- `position: fixed` ya da `position: sticky` olan kutular (`z-index` yazılmasa bile).
- `opacity` değeri 1'den küçük olan kutular.
- `transform`, `filter`, `perspective`, `clip-path`, `mask` gibi özelliklerin `none`
  dışında bir değer aldığı kutular.
- `isolation: isolate` bildirilen kutular.
- Esnek kutu ya da ızgara düzeninin `z-index` değeri `auto` dışında olan çocukları.

Bu listenin ortası dikkat çekicidir. `opacity: 0.99` yazmak — görsel olarak fark
edilmeyecek bir değişiklik — bir yığılma bağlamı kurar ve o kutunun içindeki bütün
`z-index` hesabını dışarıdan yalıtır. Aynı şey bir kutuya hareket vermek için yazılan
`transform` bildirimi için de geçerlidir.

Yığılmayla ilgisi olmayan bir bildirimin yığılmayı değiştirmesi şaşırtıcı görünür, ama
gerekçesi vardır: bu özelliklerin hepsi kutuyu ve içeriğini **bir bütün olarak** işlemeyi
gerektirir. Saydamlık, kutunun içindekilerin ayrı ayrı değil, birlikte harmanlanmasıyla
hesaplanır; bu da onları tek bir katman olarak ele almayı zorunlu kılar.

#### isolation: isolate

Listedeki son maddeden önceki `isolation: isolate` bildirimi, yığılma bağlamı kurmanın **yan
etkisiz** yoludur. Bir kutunun içindeki `z-index` değerlerinin dışarıya sızmasını istemiyorsa
yazılır ve başka hiçbir şeyi değiştirmez.

Bu, birden çok bileşenin bir arada bulunduğu sayfalarda kullanışlıdır: her bileşen kendi
bağlamını kurarsa, iç `z-index` değerleri küçük ve yerel tutulabilir. Bir bileşenin `10`
değeri başka bir bileşenin `10` değeriyle yarışmaz.

#### z-index Ölçeği

Bu dersin pratik sonucu bir yazım disiplinidir.

- `z-index` değerleri **küçük ve adlandırılmış** tutulur. Sayfada birkaç katman vardır —
  taban, yapışan başlık, açılır katman, üst katman — ve her birine 1, 10, 100 gibi seyrek
  değerler verilir. Aradaki boşluk sonradan katman eklemeye yer bırakır.
- Bir kutunun üstte kalması için önce `position` yeter mi diye bakılır; konumlandırılmış
  kutu akıştaki kutuların zaten üstündedir.
- Bileşenler `isolation: isolate` ile yalıtılır; böylece iç değerler dışarıya sızmaz.

```css
/* istasyon.css — 14. adım: katman ölçeği */
.kart { position: relative; isolation: isolate; }

.kart .durum-rozeti {
  position: absolute;
  z-index: 1;
  inset-block-start: 8px;
  inset-inline-end: 8px;
}

.olcum-cizelgesi thead th {
  position: sticky;
  z-index: 10;
  inset-block-start: 0;
  background-color: #f5f7f8;
}

.atlama-baglantisi:focus {
  position: fixed;
  z-index: 100;
  inset-block-start: 8px;
  inset-inline-start: 8px;
}
```

Karta yazılan `isolation: isolate`, rozetin `z-index: 1` değerinin kartın dışındaki hiçbir
şeyle yarışmamasını sağlar. Yapışan başlık 10, odaklanan atlama bağlantısı 100 katmanında
durur; üçü arasındaki sıra sayfanın tamamı için tanımlıdır ve okunabilir.

#### Özet

- Çizim sırası `z-index` yazılmasa da tanımlıdır; konumlandırılmış kutular akıştaki
  kutuların üstünde, aynı düzeydekiler ise belge sırasına göre çizilir.
- `z-index` küresel bir yükseklik vermez; yalnızca kutunun kendi yığılma bağlamı içindeki
  kardeşleriyle karşılaştırılır.
- Bir kutu bağlam kurduğunda içindeki bütün değerler o bağlama kapanır; dışarıyla
  karşılaştırılan şey bağlamı kuran kutunun değeridir.
- `opacity`, `transform`, `filter` gibi yığılmayla ilgisiz görünen bildirimler de bağlam
  kurar, çünkü kutunun içeriğini bir bütün olarak işlemeyi gerektirirler.
- `isolation: isolate` bağlam kurmanın yan etkisiz yoludur; bileşenlerin iç katman
  değerlerini yerel tutmak için kullanılır.

#### Sonraki Adım

Bu konu boyunca kutular ölçüldü, dizildi, üst üste bindirildi. Bir durum hâlâ ele
alınmadı: içerik kutusuna sığmadığında ne olur? Uzun bir istasyon adı dar bir hücreye,
geniş bir ölçüm çizelgesi dar bir ekrana sığmaz. Sonraki ders taşmanın ne olduğunu ve
kırpma, kaydırma, gizleme seçeneklerinin her birinin neyi kaybettirdiğini ele alır.

### Taşma Yönetimi

Source: https://academia.sh/tr/kurslar/css-temelleri/tasma-yonetimi

İçeriğin kutuya sığmadığı durumun ele alınması; overflow değerlerinin karşılaştırılması, metin taşmasının ayrı mekanizmaları ve kırpmanın erişilebilirlik bedeli.

Bu konu boyunca kutular ölçüldü, dizildi ve üst üste bindirildi. Bir durum ele alınmadı:
içerik kutusuna **sığmadığında** ne olur?

Durum kaçınılmazdır. Uzun bir istasyon adı dar bir hücreye, dokuz sütunlu bir ölçüm
çizelgesi dar bir ekrana, sabit yükseklik verilmiş bir karta uzun bir açıklama sığmaz. Bu
ders taşmanın ne olduğunu, seçeneklerin her birinin neyi kaybettirdiğini ve metin
taşmasının neden ayrı bir mesele olduğunu ele alır.

#### Taşmanın Varsayılan Davranışı Görünürlüktür

Bir kutunun içeriği kutuya sığmadığında, varsayılan davranış içeriğin **dışarı taşıp
görünmesidir**. Kırpılmaz, gizlenmez, kutuyu büyütmez.

Bu seçim bilinçlidir: kaybolan bir içerik, düzeni bozan bir içerikten daha kötüdür. Taşan
metin görünür kalır ve okunabilir; sorun görülür ve düzeltilebilir.

```js
// tasma.mjs — icerigin kutuya sigip sigmadigini ve overflow degerinin sonucunu hesaplar
function tasmaDurumu({ kutuEn, kutuBoy, icerikEn, icerikBoy, overflow }) {
  const yatay = Math.max(0, icerikEn - kutuEn);
  const dikey = Math.max(0, icerikBoy - kutuBoy);
  const sonuc = {
    "visible": { gorunur: true,  kaydirilabilir: false, kirpilir: false },
    "hidden":  { gorunur: false, kaydirilabilir: false, kirpilir: true  },
    "clip":    { gorunur: false, kaydirilabilir: false, kirpilir: true  },
    "scroll":  { gorunur: false, kaydirilabilir: true,  kirpilir: true  },
    "auto":    { gorunur: false, kaydirilabilir: yatay > 0 || dikey > 0, kirpilir: true },
  }[overflow];
  return { yatay, dikey, ...sonuc };
}

const kutu = { kutuEn: 320, kutuBoy: 120 };
const icerik = { icerikEn: 480, icerikBoy: 200 };
console.log(`kutu ${kutu.kutuEn}x${kutu.kutuBoy}, icerik ${icerik.icerikEn}x${icerik.icerikBoy}`);
for (const o of ["visible", "hidden", "clip", "scroll", "auto"]) {
  const r = tasmaDurumu({ ...kutu, ...icerik, overflow: o });
  console.log(`${o.padEnd(8)} tasma=${r.yatay}x${r.dikey}  disaridan gorunur=${String(r.gorunur).padEnd(5)} kaydirilabilir=${String(r.kaydirilabilir).padEnd(5)} kirpilir=${r.kirpilir}`);
}

console.log("\n--- sigan icerik ---");
for (const o of ["scroll", "auto"]) {
  const r = tasmaDurumu({ ...kutu, icerikEn: 300, icerikBoy: 100, overflow: o });
  console.log(`${o.padEnd(8)} tasma=${r.yatay}x${r.dikey}  kaydirilabilir=${r.kaydirilabilir}`);
}

console.log("\n--- uzun bir sozcugun satira sigmasi ---");
const sozcuk = "Kuzey-Yamac-Otomatik-Olcum-Istasyonu";
const KARAKTER_EN = 8;   // ornek bir yazi tipinde karakter genisligi
const kutuEn = 160;
const sigar = Math.floor(kutuEn / KARAKTER_EN);
console.log(`sozcuk ${sozcuk.length} karakter x ${KARAKTER_EN}px = ${sozcuk.length * KARAKTER_EN}px`);
console.log(`kutu ${kutuEn}px -> satira ${sigar} karakter sigar, tasma ${sozcuk.length * KARAKTER_EN - kutuEn}px`);
const parcalar = sozcuk.match(new RegExp(`.{1,${sigar}}`, "g"));
console.log("overflow-wrap: anywhere ile bolunme:");
for (const p of parcalar) console.log(`  |${p.padEnd(sigar)}|`);
```

```
kutu 320x120, icerik 480x200
visible  tasma=160x80  disaridan gorunur=true  kaydirilabilir=false kirpilir=false
hidden   tasma=160x80  disaridan gorunur=false kaydirilabilir=false kirpilir=true
clip     tasma=160x80  disaridan gorunur=false kaydirilabilir=false kirpilir=true
scroll   tasma=160x80  disaridan gorunur=false kaydirilabilir=true  kirpilir=true
auto     tasma=160x80  disaridan gorunur=false kaydirilabilir=true  kirpilir=true

--- sigan icerik ---
scroll   tasma=0x0  kaydirilabilir=true
auto     tasma=0x0  kaydirilabilir=false

--- uzun bir sozcugun satira sigmasi ---
sozcuk 36 karakter x 8px = 288px
kutu 160px -> satira 20 karakter sigar, tasma 128px
overflow-wrap: anywhere ile bolunme:
  |Kuzey-Yamac-Otomatik|
  |-Olcum-Istasyonu    |
```

#### Beş Değerin Karşılaştırması

`visible` — başlangıç değeridir. İçerik taşar ve görünür kalır. Kutunun kendisi büyümez;
komşuları taşmadan haberdar değildir ve üstüne çizilebilir.

`hidden` — taşan kısım kırpılır ve **erişilemez** olur. Kaydırma çubuğu gösterilmez ama
kutu programatik olarak kaydırılabilir bir kutudur; odaklanan bir öge görünür alana
getirilebilir.

`clip` — `hidden` gibi kırpar ama kutuyu kaydırılabilir yapmaz. Kırpma kesindir.

`scroll` — taşan kısım kırpılır ve kaydırma çubuğu **her zaman** gösterilir.

`auto` — kırpar ve kaydırma çubuğunu **yalnızca gerektiğinde** gösterir.

Çıktının ikinci bloğu bu son iki değerin farkını veriyor: içerik kutuya sığdığında `scroll`
hâlâ kaydırılabilir bir kutu üretiyor, `auto` üretmiyor. Bazı ortamlarda kaydırma çubuğu
yer kaplar; `scroll` yazmak bu durumda içerik sığsa bile bir şerit ayırır ve kutu daralır.
Kaydırma gereğinde ortaya çıksın isteniyorsa `auto` yazılır.

#### Kırpmanın Bedeli

`overflow: hidden` yazmak sık başvurulan bir çözümdür ve iki farklı amaçla kullanılır.
İkisini ayırmak gerekir.

Birinci kullanım **kasıtlı kırpmadır**: bir görselin kutuya oturması, bir dekoratif şeklin
taşmaması. Burada kırpılan şey bilgi taşımaz.

İkinci kullanım **taşma sorununu görünmez kılmaktır**: içerik sığmıyor ve gizlenerek
sorun ortadan kaldırılmış gibi yapılıyor. Bu, sorunu çözmez; kullanıcıdan saklar. Kırpılan
metin ekran okuyucuya okunmaya devam edebilir ama görsel olarak ulaşılamaz; klavyeyle
gezen bir kullanıcı odaklanabilir ama göremez.

Ayrım şu soruyla yapılır: **kırpılan şeyin kaybolması kabul edilebilir mi?** Yanıt hayırsa
`hidden` değil, `auto` yazılır ya da kutunun ölçüsü esnetilir.

Bir yan etki daha vardır ve önceki derslerde iki kez karşımıza çıktı: `overflow` değerinin
`visible` ve `clip` dışında olması kutuyu **bağımsız akış kökü** yapar. Kaydırılmış çocuklar
yüksekliğe katılır, dış boşluk çökmesi kutunun sınırında durur. Bu, `overflow: hidden`
yazmanın bazen ilgisiz görünen bir yerleşim değişikliği yaratmasının sebebidir.

#### İki Eksen Ayrı Ayrı

`overflow` bir kısayoldur; `overflow-x` ve `overflow-y` açılımlarını ayarlar. Mantıksal
karşılıkları `overflow-inline` ve `overflow-block`'tur.

İki eksene farklı değer verilebilir ama bir kısıt vardır: bir eksen `visible` iken diğeri
`hidden`, `scroll` ya da `auto` alırsa, `visible` olan eksen `auto` gibi işlenir; `clip` bu
kuralın dışındadır. İki eksen bağımsız değildir; bir yönde kırpan bir kutu, diğer yönde taşan
içeriği görünür bırakamaz.

Yatay kaydırma özel olarak ele alınmalıdır. Geniş bir ölçüm çizelgesini dar bir ekranda
göstermenin yolu, çizelgeyi yatay kaydırılabilir bir kutuya almaktır. Böyle bir kutu
klavyeyle de erişilebilir olmalıdır; kaydırılabilir bir bölgeye `tabindex="0"` verilmesi ve
erişilebilir bir adla adlandırılması bunun karşılığıdır.

#### Metin Taşması Ayrı Bir Mekanizmadır

Çıktının üçüncü bloğu farklı bir soruna işaret ediyor. Otuz altı karakterlik bir sözcük 160
birimlik bir kutuya sığmıyor ve 128 birim taşıyor. Bu, `overflow` ile çözülecek bir sorun
değildir — sözcüğün **bölünmesi** gerekir.

Satır bölme kuralları ayrı özelliklerle yönetilir:

`overflow-wrap: anywhere` — sığmayan bir sözcüğü herhangi bir noktadan böler. Çıktının son
satırlarında bölünme bu kurala göre gösterildi. `break-word` değeri benzer iş görür ama
kutunun en küçük içerik ölçüsü hesabına katılmaz.

`word-break: break-all` — sözcükleri sığmasalar da sığsalar da böler; sözcük bütünlüğünü
gözetmez.

`hyphens: auto` — belgenin diline göre heceleme yapar ve bölünme noktasına tire koyar. `lang`
özniteliğinin bildirilmiş olmasını gerektirir; Web'in Temelleri ve HTML kursunda kök ögeye
yazılan `lang="tr"` bildiriminin bir karşılığı da budur.

`white-space: nowrap` — tersini yapar: satır bölünmesini büsbütün engeller. Bu, taşmayı
önlemez, garantiler.

`text-overflow: ellipsis` — taşan metnin sonuna üç nokta koyar. Yalnız çalışmaz; kutunun
kırpıyor ve tek satır olması gerekir:

```css
.istasyon-adi {
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
}
```

Üç bildirim birlikte yazılır. Kırpılan metnin kaybolduğunu unutmamak gerekir: tam ad bir
`title` özniteliğinde ya da başka bir yerde erişilebilir kalmalıdır.

```css
/* istasyon.css — 15. adım: taşma */
.cizelge-sarmalayici {
  overflow-x: auto;
  overscroll-behavior-x: contain;
}

.olcum-cizelgesi { min-width: 40rem; }

.kart .aciklama {
  overflow-wrap: anywhere;
  hyphens: auto;
}

.konum-gorseli img { overflow: clip; }
```

`.cizelge-sarmalayici` çizelgeyi dar ekranda yatay kaydırılabilir yapar; `min-width`
bildirimi çizelgenin sıkışıp okunmaz hâle gelmesini önler. İkisi birlikte, çizelgeyi
küçültmek yerine kaydırmayı seçmiş olur.

`overscroll-behavior-x: contain`, kutunun kaydırması bittiğinde sayfanın kaymasını
engeller — kaydırma zincirinin o kutuda durmasını sağlar.

#### Özet

- Taşmanın varsayılan davranışı görünürlüktür: içerik kutunun dışına çıkar ve okunabilir
  kalır. Bu, kaybolan içeriğin bozulan düzenden kötü olduğu kabulüne dayanır.
- `hidden` ve `clip` kırpar, `scroll` her zaman kaydırma çubuğu gösterir, `auto` yalnızca
  gerektiğinde gösterir; içerik sığdığında `scroll` yine kaydırılabilir bir kutu üretir.
- `overflow` değerinin `visible` ve `clip` dışında olması kutuyu bağımsız akış kökü yapar; kaydırılmış
  çocukları içerir ve dış boşluk çökmesini sınırında durdurur.
- İki eksen bağımsız değildir: bir eksen `hidden`, `scroll` ya da `auto` alırken diğeri
  `visible` kalamaz, `auto` gibi işlenir; `clip` bu kuralın dışındadır.
- Sığmayan bir sözcük `overflow` ile değil, satır bölme özellikleriyle çözülür;
  `text-overflow: ellipsis` yalnız çalışmaz, kırpma ve tek satır bildirimleriyle birlikte
  yazılır.

#### Sonraki Adım

Bu konu kutuların ölçüsünü, dizilişini ve sınırlarını kurdu. Ortaya çıkan sayfa yapılı ama
renksiz: bütün kararlar geometriye ilişkindi. Sonraki konu görsel özellikleri ele alır ve
ilk soru rengin kendisidir — bir rengin nasıl yazıldığı, hangi uzayda tanımlandığı ve iki
renk arasındaki kontrastın nasıl bir sayıya dönüştüğü.

## Görsel Özellikler

Renk gösterimleri ve kontrast hesabı, tipografi ölçekleri ve yazı tipi yükleme davranışı, arka planlar, geçişler, gölgeler ve birimlerle hesaplama fonksiyonları.

### Renk Gösterimleri

Source: https://academia.sh/tr/kurslar/css-temelleri/renk-gosterimleri

Adlandırılmış, onaltılık ve fonksiyonel renk yazımları; RGB ile HSL arasındaki dönüşüm ve renk uzayı seçiminin ölçek üretmedeki sonucu.

Önceki konu kutuların ölçüsünü, dizilişini ve sınırlarını kurdu. Ortaya çıkan sayfa yapılı
ama renksiz: bütün kararlar geometriye ilişkindi. Bu konu görsel özellikleri ele alır ve
ilk soru rengin kendisidir.

Bir rengin CSS'te dört ayrı yazımı vardır ve aralarında geçiş hesapla yapılır. Yazımın
seçilmesi bir üslup tercihi değildir: bazı yazımlar bir rengi **değiştirmeyi** ötekilerden
daha uygulanabilir kılar ve bir tasarımın renk ölçeği bu yüzden belirli bir gösterimde
kurulur.

#### Dört Yazım

**Adlandırılmış renk (named color)** sabit bir sözlükten gelen bir addır: `red`, `darkslategray`,
`transparent`. Sözlük tarih içinde birkaç kez genişledi (CSS2'de `orange`, Color 4'te
`rebeccapurple`) ama bugün kapalı sayılır; adlar bir renk sistemi oluşturmaz, tarihsel bir
listedir. Tasarımda kullanılmaz.

**Onaltılık gösterim (hexadecimal notation)** kırmızı, yeşil ve mavi bileşenlerini
onaltılık tabanda yazar: `#143a52`. Bilgisayarlar Nasıl Çalışır kursunda tanımlanan
onaltılık taban burada karşılığını bulur — her bileşen bir bayt, iki onaltılık basamak.

**Fonksiyonel gösterim (functional notation)** bileşenleri açıkça yazar: `rgb(20 58 82)`,
`hsl(203 61% 20%)`, `oklch(30% 0.06 240)`. Fonksiyon adı, sayıların hangi **renk uzayında**
okunacağını söyler.

**Anahtar sözcükler** bağlamdan renk alır: `currentcolor` ögenin `color` değerini,
`transparent` ise tam saydam siyahı verir.

#### RGB ile HSL Arasındaki Dönüşüm

RGB ve HSL aynı renk kümesini tanımlar; farkları koordinat sistemidir. RGB üç ışık
bileşenini, HSL ise ton, doygunluk ve açıklık eksenlerini kullanır. Dönüşüm iki yönlü ve
kayıpsızdır.

```js
// renk.mjs — onaltilik, rgb ve hsl gosterimleri arasinda cevrim yapar
function hexToRgb(hex) {
  let h = hex.replace("#", "");
  if (h.length === 3) h = [...h].map((c) => c + c).join("");
  if (h.length === 4) h = [...h].map((c) => c + c).join("");
  const r = parseInt(h.slice(0, 2), 16);
  const g = parseInt(h.slice(2, 4), 16);
  const b = parseInt(h.slice(4, 6), 16);
  const a = h.length === 8 ? parseInt(h.slice(6, 8), 16) / 255 : 1;
  return { r, g, b, a };
}

function rgbToHsl({ r, g, b }) {
  const rn = r / 255, gn = g / 255, bn = b / 255;
  const enB = Math.max(rn, gn, bn), enK = Math.min(rn, gn, bn);
  const fark = enB - enK;
  const l = (enB + enK) / 2;
  let h = 0, s = 0;
  if (fark !== 0) {
    s = fark / (1 - Math.abs(2 * l - 1));
    if (enB === rn) h = 60 * (((gn - bn) / fark) % 6);
    else if (enB === gn) h = 60 * ((bn - rn) / fark + 2);
    else h = 60 * ((rn - gn) / fark + 4);
    if (h < 0) h += 360;
  }
  return { h: Math.round(h), s: +(s * 100).toFixed(1), l: +(l * 100).toFixed(1) };
}

const renkler = ["#143a52", "#8a1c1c", "#f5f7f8", "#fff", "#1c2733", "#8a5b00", "#d5dbe0"];
console.log("onaltilik   rgb()                  hsl()");
for (const hex of renkler) {
  const c = hexToRgb(hex);
  const h = rgbToHsl(c);
  console.log(
    `${hex.padEnd(11)} rgb(${c.r} ${c.g} ${c.b})`.padEnd(34) +
    `hsl(${h.h} ${h.s}% ${h.l}%)`
  );
}

console.log("\n--- kisa yazimlarin acilimi ---");
for (const hex of ["#fff", "#0a3", "#143a52cc"]) {
  const c = hexToRgb(hex);
  console.log(`${hex.padEnd(11)} -> rgb(${c.r} ${c.g} ${c.b}) alfa=${c.a.toFixed(3)}`);
}

console.log("\n--- ayni tonun acikligi degistirilerek olcek uretimi ---");
const taban = rgbToHsl(hexToRgb("#143a52"));
for (const l of [12, 24, 36, 50, 72, 90, 96]) {
  console.log(`hsl(${taban.h} ${taban.s}% ${l}%)`);
}
```

```
onaltilik   rgb()                  hsl()
#143a52     rgb(20 58 82)         hsl(203 60.8% 20%)
#8a1c1c     rgb(138 28 28)        hsl(0 66.3% 32.5%)
#f5f7f8     rgb(245 247 248)      hsl(200 17.6% 96.7%)
#fff        rgb(255 255 255)      hsl(0 0% 100%)
#1c2733     rgb(28 39 51)         hsl(211 29.1% 15.5%)
#8a5b00     rgb(138 91 0)         hsl(40 100% 27.1%)
#d5dbe0     rgb(213 219 224)      hsl(207 15.1% 85.7%)

--- kisa yazimlarin acilimi ---
#fff        -> rgb(255 255 255) alfa=1.000
#0a3        -> rgb(0 170 51) alfa=1.000
#143a52cc   -> rgb(20 58 82) alfa=0.800

--- ayni tonun acikligi degistirilerek olcek uretimi ---
hsl(203 60.8% 12%)
hsl(203 60.8% 24%)
hsl(203 60.8% 36%)
hsl(203 60.8% 50%)
hsl(203 60.8% 72%)
hsl(203 60.8% 90%)
hsl(203 60.8% 96%)
```

#### Onaltılık Yazımın Kısaltmaları

Çıktının ikinci bloğu üç kısaltmayı açıyor.

Üç basamaklı yazımda her basamak **ikiye katlanır**: `#fff` yazımı `#ffffff` demektir,
`#0a3` yazımı `#00aa33`. Bu, üç basamakla yazılabilen renklerin sayısının 4096 olduğu
anlamına gelir — tam kümenin küçük bir alt kümesi. `#0a3` bir yaklaşım değil, tam olarak
`#00aa33`'tür.

Sekiz basamaklı yazımda son iki basamak **alfa kanalını** taşır. `#143a52cc` yazımında
`cc` onaltılık değeri 204'tür ve 255'e bölündüğünde 0.8 verir. Alfa kanalı bir sonraki
derste ele alınacak.

#### Renk Uzayı Seçimi Bir Ölçek Kararıdır

Çıktının ilk bloğunda `#143a52` rengi `hsl(203 60.8% 20%)` olarak yazılıyor. İki gösterim
aynı rengi verir; farkları, değiştirmek istendiğinde ortaya çıkar.

Bu rengin daha açık bir tonunu üretmek gerekirse, onaltılık gösterimde üç bileşenin de
ayrı ayrı hesaplanması gerekir ve ton kayabilir. HSL gösteriminde yalnızca üçüncü sayı
değiştirilir; ton ve doygunluk sabit kalır. Çıktının üçüncü bloğu bunu gösteriyor: tek bir
tondan yedi basamaklı bir ölçek, yalnızca açıklık değeri değiştirilerek üretildi.

Bir tasarımın renk ölçeği bu yüzden ton eksenli bir gösterimde kurulur. HSL'in bilinen bir
sınırı vardır: eşit açıklık farkları **algılanan** eşit farklara karşılık gelmez. Aynı
açıklık değerinde sarı, maviden belirgin biçimde daha parlak görünür. `oklch()` gibi
algısal olarak daha düzgün uzaylar bu sorunu azaltmak üzere tanımlanmıştır ve aynı üç
eksene sahiptir: açıklık, renklilik, ton.

Bir gösterimin gerçekleştirimlerce tanınıp tanınmadığı sınanabilir:

```css
.uyari { background-color: #8a5b00; }

@supports (color: oklch(50% 0.1 40)) {
  .uyari { background-color: oklch(45% 0.12 75); }
}
```

Blok, koşul sağlanmazsa yok sayılır ve ilk bildirim yerinde kalır. Önceki derslerde
gördüğümüz "geçersiz bildirim atılır" kuralı da aynı sonucu verirdi; `@supports` yazmak
niyeti açık kılar.

#### Renk Değişkenleri

Bir renk ölçeği bir kez tanımlanıp her yerde adıyla çağrılmalıdır. Bunun aracı özel
özelliklerdir:

```css
:root {
  --ton: 203;
  --marka-koyu:  hsl(var(--ton) 61% 20%);
  --marka-orta:  hsl(var(--ton) 61% 36%);
  --marka-acik:  hsl(var(--ton) 61% 90%);
  --zemin:       hsl(var(--ton) 18% 97%);
  --metin:       hsl(211 29% 16%);
  --uyari:       hsl(0 66% 33%);
}
```

Özel özellikler iki tire (`--`) önekiyle tanımlanır ve `var()` ile okunur. Kalıtılan
özelliklerdir: kök ögede tanımlanan bir değer bütün alt ağaçta okunabilir. Bu, bir
bileşenin içinde değeri yeniden tanımlayarak o alt ağaç için değiştirmeyi de mümkün kılar.

Buradaki yazımın bir yan sonucu var: `--ton` tek bir yerde tutulduğu için bütün ölçek tek
bir sayının değiştirilmesiyle kaydırılabilir.

Bir uyarı gerekir: özel özelliğin değeri ayrıştırma sırasında doğrulanmaz. `--marka-koyu`
değerine geçersiz bir şey yazılırsa hata, değerin kullanıldığı yerde ortaya çıkar ve o
bildirim düşer.

#### İstasyon Sayfasının Renk Ölçeği

```css
/* istasyon.css — 16. adım: renk ölçeği */
:root {
  --ton: 203;
  --marka-koyu: hsl(var(--ton) 61% 20%);
  --marka-acik: hsl(var(--ton) 61% 90%);
  --zemin:      hsl(var(--ton) 18% 97%);
  --cizgi:      hsl(207 15% 86%);
  --metin:      hsl(211 29% 16%);
  --metin-soluk: hsl(211 12% 45%);
  --uyari:      hsl(0 66% 33%);
  --dikkat:     hsl(40 100% 27%);
}

body { color: var(--metin); }
.kunye { background-color: var(--zemin); border-block-end-color: var(--marka-koyu); }
.olcum-cizelgesi th, .olcum-cizelgesi td { border-block-end-color: var(--cizgi); }
.eksik { color: var(--uyari); }
.kaynak-notu { color: var(--metin-soluk); }
```

Ölçekteki adlar rengi değil **işlevi** adlandırıyor: `--uyari`, `--cizgi`, `--metin-soluk`.
Bu, seçicilerdeki adlandırma ilkesinin renklere uygulanmış hâlidir. `--kirmizi` adlı bir
değişken, uyarı rengi turuncuya çevrildiğinde yalan söylemeye başlar.

`currentcolor` anahtar sözcüğü bu ölçekle birlikte kullanışlıdır: bir kenarlığa
`border-color: currentcolor` yazmak, kenarlığı metin rengine bağlar ve `.eksik` sınıfı
metni kırmızıya çevirdiğinde kenarlık da onunla birlikte değişir. Tek bir bildirim iki
özelliği eşzamanlı tutar.

#### Özet

- Bir renk dört biçimde yazılır: adlandırılmış sözlükten, onaltılık gösterimle, fonksiyonel
  gösterimle ve bağlamdan renk alan anahtar sözcüklerle.
- Onaltılık yazımda üç basamaklı biçim her basamağı ikiye katlar; sekiz basamaklı biçimde
  son iki basamak alfa kanalını taşır.
- RGB ve HSL aynı renk kümesini farklı koordinatlarla tanımlar; dönüşüm iki yönlüdür ve
  gösterim seçimi rengi değiştirmenin ne kadar uygulanabilir olduğunu belirler.
- Bir renk ölçeği ton eksenli bir gösterimde kurulur: tek bir tonun açıklığı değiştirilerek
  tutarlı bir basamak dizisi üretilir.
- Renk değişkenleri rengi değil işlevi adlandırır; kalıtılan özel özellikler olarak
  tanımlanır ve bir alt ağaçta yeniden tanımlanarak yerel olarak değiştirilebilir.

#### Sonraki Adım

Bu derste renkler tam donuk kabul edildi; sekiz basamaklı yazımda görülen alfa kanalı
açıklanmadan geçildi. Saydam bir renk üst üste bindiğinde ortaya çıkan renk nedir ve o
rengin metinle kontrastı nasıl hesaplanır? Sonraki ders alfa harmanlamasını ve WCAG
kontrast oranını sayıyla ele alır.

### Saydamlık ve Karışım

Source: https://academia.sh/tr/kurslar/css-temelleri/saydamlik-ve-karisim

Alfa kanalının harmanlama hesabı, opacity ile alfa arasındaki fark ve harmanlanmış rengin kontrast oranının WCAG ölçütlerine göre değerlendirilmesi.

Önceki derste renkler tam donuk kabul edildi ve sekiz basamaklı onaltılık yazımda görülen
alfa kanalı açıklanmadan geçildi. Saydam bir renk bir zeminin üzerine bindiğinde ortaya
çıkan renk nedir?

Bu sorunun yanıtı bir hesaptır ve hesabın sonucu okunabilirliği doğrudan etkiler.
Saydamlaştırılmış bir metin rengi, zeminle karışarak beklenenden soluk bir renge dönüşür ve
kontrast oranı düşer. Bu ders harmanlama hesabını ve kontrast oranını sayıyla ele alır.

#### Alfa Harmanlaması

**Alfa kanalı (alpha channel)** bir rengin ne kadar donuk olduğunu bildirir: 1 tam donuk,
0 tam saydam. Saydam bir renk zeminin üzerine bindiğinde her bileşen için ağırlıklı
ortalama alınır:

$$C_{\text{sonuç}} = \alpha \cdot C_{\text{üst}} + (1 - \alpha) \cdot C_{\text{alt}}$$

Kontrast oranı ise WCAG'de tanımlıdır. Önce her rengin **bağıl parlaklığı (relative
luminance)** hesaplanır — kanallar doğrusallaştırılır ve göz duyarlılığına göre ağırlıklı
toplanır — sonra iki parlaklık şu bağıntıya sokulur:

$$\text{oran} = \frac{L_{\text{açık}} + 0{,}05}{L_{\text{koyu}} + 0{,}05}$$

Sonuç 1:1 (aynı renk) ile 21:1 (siyah–beyaz) arasında bir sayıdır.

```js
// harman.mjs — alfa harmanlamasi ve WCAG kontrast orani
const hexToRgb = (hex) => {
  let h = hex.replace("#", "");
  if (h.length === 3) h = [...h].map((c) => c + c).join("");
  return { r: parseInt(h.slice(0,2),16), g: parseInt(h.slice(2,4),16), b: parseInt(h.slice(4,6),16) };
};

// ustteki renk alfa ile alttakinin uzerine harmanlanir: C = a*Cu + (1-a)*Ca
function harmanla(ust, alfa, alt) {
  const u = hexToRgb(ust), a = hexToRgb(alt);
  const k = (x, y) => Math.round(alfa * x + (1 - alfa) * y);
  return { r: k(u.r, a.r), g: k(u.g, a.g), b: k(u.b, a.b) };
}

const hex = ({ r, g, b }) =>
  "#" + [r, g, b].map((v) => v.toString(16).padStart(2, "0")).join("");

// WCAG 2 bagil parlaklik
function bagilParlaklik({ r, g, b }) {
  const kanal = (v) => {
    const s = v / 255;
    return s <= 0.04045 ? s / 12.92 : Math.pow((s + 0.055) / 1.055, 2.4);
  };
  return 0.2126 * kanal(r) + 0.7152 * kanal(g) + 0.0722 * kanal(b);
}

// kontrast orani: (L1 + 0.05) / (L2 + 0.05), L1 >= L2
function kontrast(a, b) {
  const la = bagilParlaklik(a), lb = bagilParlaklik(b);
  const [ust, alt] = la > lb ? [la, lb] : [lb, la];
  return (ust + 0.05) / (alt + 0.05);
}

console.log("--- alfa harmanlamasi (zemin #ffffff) ---");
for (const alfa of [1, 0.8, 0.5, 0.2, 0]) {
  const s = harmanla("#143a52", alfa, "#ffffff");
  console.log(`alfa=${alfa.toFixed(2)} -> rgb(${s.r} ${s.g} ${s.b}) = ${hex(s)}`);
}

console.log("\n--- ayni renk koyu zeminde (#1c2733) ---");
for (const alfa of [1, 0.8, 0.5, 0.2]) {
  const s = harmanla("#143a52", alfa, "#1c2733");
  console.log(`alfa=${alfa.toFixed(2)} -> ${hex(s)}`);
}

console.log("\n--- kontrast oranlari ---");
const ciftler = [
  ["#1c2733", "#ffffff"], ["#1c2733", "#f5f7f8"], ["#143a52", "#ffffff"],
  ["#8a1c1c", "#ffffff"], ["#8a5b00", "#ffffff"], ["#6b767f", "#ffffff"],
  ["#d5dbe0", "#ffffff"], ["#ffffff", "#143a52"],
];
for (const [on, arka] of ciftler) {
  const o = kontrast(hexToRgb(on), hexToRgb(arka));
  const normal = o >= 7 ? "AAA" : o >= 4.5 ? "AA" : "gecmez";
  const buyuk  = o >= 4.5 ? "AAA" : o >= 3 ? "AA" : "gecmez";
  console.log(`${on} / ${arka} -> ${o.toFixed(2)}:1   normal metin ${normal.padEnd(6)} buyuk metin ${buyuk}`);
}

console.log("\n--- saydam metnin kontrasti ---");
for (const alfa of [1, 0.7, 0.5, 0.35]) {
  const s = harmanla("#1c2733", alfa, "#ffffff");
  const o = kontrast(s, hexToRgb("#ffffff"));
  console.log(`alfa=${alfa.toFixed(2)} -> ${hex(s)}  kontrast ${o.toFixed(2)}:1  ${o >= 4.5 ? "AA gecer" : "AA gecmez"}`);
}
```

```
--- alfa harmanlamasi (zemin #ffffff) ---
alfa=1.00 -> rgb(20 58 82) = #143a52
alfa=0.80 -> rgb(67 97 117) = #436175
alfa=0.50 -> rgb(138 157 169) = #8a9da9
alfa=0.20 -> rgb(208 216 220) = #d0d8dc
alfa=0.00 -> rgb(255 255 255) = #ffffff

--- ayni renk koyu zeminde (#1c2733) ---
alfa=1.00 -> #143a52
alfa=0.80 -> #16364c
alfa=0.50 -> #183143
alfa=0.20 -> #1a2b39

--- kontrast oranlari ---
#1c2733 / #ffffff -> 15.14:1   normal metin AAA    buyuk metin AAA
#1c2733 / #f5f7f8 -> 14.09:1   normal metin AAA    buyuk metin AAA
#143a52 / #ffffff -> 11.95:1   normal metin AAA    buyuk metin AAA
#8a1c1c / #ffffff -> 9.28:1   normal metin AAA    buyuk metin AAA
#8a5b00 / #ffffff -> 5.87:1   normal metin AA     buyuk metin AAA
#6b767f / #ffffff -> 4.64:1   normal metin AA     buyuk metin AAA
#d5dbe0 / #ffffff -> 1.40:1   normal metin gecmez buyuk metin gecmez
#ffffff / #143a52 -> 11.95:1   normal metin AAA    buyuk metin AAA

--- saydam metnin kontrasti ---
alfa=1.00 -> #1c2733  kontrast 15.14:1  AA gecer
alfa=0.70 -> #606870  kontrast 5.66:1  AA gecer
alfa=0.50 -> #8e9399  kontrast 3.10:1  AA gecmez
alfa=0.35 -> #b0b3b8  kontrast 2.10:1  AA gecmez
```

#### Harmanlama Zemine Bağlıdır

Çıktının ilk iki bloğu aynı rengi iki farklı zemin üzerinde harmanlıyor. `#143a52` rengi
0.5 alfayla beyaz zeminde `#8a9da9`'a, koyu zeminde `#183143`'e dönüştü. İki sonuç
birbirine benzemez.

Sonuç şudur: **saydam bir renk sabit bir renk değildir.** Bir bileşene saydam bir arka plan
verildiğinde, bileşenin görünümü nereye konduğuna bağlı olur. Bu bazen istenen davranıştır
— bir gölge ya da örtü katmanı zeminle uyum sağlamalıdır. Bazen değildir: bir uyarı
rozetinin rengi bulunduğu yere göre değişmemelidir.

Çıktının son satırı bir sınır durumunu gösteriyor: alfa sıfır olduğunda sonuç tamamen zemin
rengidir. `transparent` anahtar sözcüğü tam olarak bunu ifade eder.

#### opacity Alfadan Farklıdır

İki mekanizma benzer görünür ama farklı şeyler yapar.

**Renk alfası** yalnızca o rengin uygulandığı özelliği etkiler.
`background-color: rgb(20 58 82 / 0.5)` yazmak yalnızca arka planı saydamlaştırır; metin ve
kenarlık donuk kalır.

**`opacity`** ögenin **tamamını** — arka planı, metni, kenarlığı, bütün çocuklarını —
tek bir katman olarak saydamlaştırır. Alt ögelere ayrı ayrı uygulanmaz; öge önce kendi
içinde çizilir, sonra bütün olarak harmanlanır.

Bunun iki sonucu vardır. Birincisi, `opacity: 0.5` verilmiş bir kutunun içindeki metnin
saydamlığı iptal edilemez; çocukta `opacity: 1` yazmak işe yaramaz. İkincisi, yığılma
bağlamı dersinde gördüğümüz gibi 1'den küçük her `opacity` değeri bir yığılma bağlamı
kurar — çünkü bütün olarak harmanlama tam da bunu gerektirir.

Uygulanabilir kural: bir rengi saydamlaştırmak isteniyorsa renk alfası yazılır; bir ögenin
tamamını soldurmak isteniyorsa `opacity` yazılır.

#### Kontrast Oranını Okumak

Çıktının üçüncü bloğu WCAG ölçütlerine göre değerlendirilmiş oranları veriyor. Ölçütler
şunlardır:

| Ölçüt | Normal metin | Büyük metin |
|---|---|---|
| AA | 4,5:1 | 3:1 |
| AAA | 7:1 | 4,5:1 |

Büyük metin, kalın olmayan 24 piksel ya da kalın 18,66 piksel ve üzeri boyuttaki metindir.
Metin dışındaki görsel bileşenler — form alanı kenarlıkları, simgeler, odak göstergeleri —
için ölçüt 3:1'dir.

Çizelgedeki satırlar üç tür karar veriyor.

`#6b767f` rengi beyaz üzerinde 4,64:1 veriyor ve AA ölçütünü **kıl payı** geçiyor. Soluk
metin renkleri bu sınırda dolaşır; bir tonluk bir değişiklik ölçütü düşürebilir.

`#d5dbe0` rengi 1,40:1 ile hiçbir ölçütü geçmiyor. Bu renk çizgi rengi olarak seçilmişti ve
metin rengi olarak kullanılamaz. Kenarlık için bile 3:1 ölçütünün altında kalır; ayırıcı
çizgi olarak dekoratif kabul edilebilir ama bir form alanının sınırını göstermek için
yetersizdir.

Son satır simetriyi gösteriyor: `#ffffff / #143a52` ve `#143a52 / #ffffff` aynı oranı
veriyor. Kontrast oranı yönsüzdür; hangisinin ön hangisinin arka plan olduğuna bakmaz.

#### Saydamlık Kontrastı Düşürür

Çıktının son bloğu bu dersin en pratik sonucudur. Tam donuk `#1c2733` metni beyaz zeminde
15,14:1 veriyor. Aynı renk 0,5 alfayla yazıldığında oran 3,10:1'e düşüyor ve AA ölçütünü
geçmiyor.

Bir metni "soluklaştırmak" için alfa kullanmak bu yüzden tehlikelidir: yapılan şey bir görsel
ayardan çok, bir okunabilirlik kaybıdır ve kayıp hesaplanmadıkça görünmez. Doğru yol,
soluk tonu ölçekten seçmek ve seçilen tonun kontrast oranını hesaplamaktır.

WCAG 2'deki bu hesabın bilinen bir sınırı vardır: bağıl parlaklık, algılanan kontrastı her
renk çiftinde aynı doğrulukla yansıtmaz — özellikle koyu zeminlerde ve doygun renklerde
sapma gösterir. Ölçüt yine de ölçülebilir ve sınanabilir bir taban sağlar; hesabın
geçilmesi gereken bir alt sınır olarak okunur, iyi tasarımın tanımı olarak değil.

```css
/* istasyon.css — 17. adım: saydamlık ve kontrast */
.kaynak-notu { color: hsl(211 12% 45%); }        /* donuk ton, alfa değil */

.gorsel-orgusu { background-color: rgb(20 58 82 / 0.08); }

.katman-ortusu {
  position: fixed;
  inset: 0;
  background-color: rgb(28 39 51 / 0.6);
}

.devre-disi { opacity: 0.55; pointer-events: none; }
```

`.kaynak-notu` soluk metin için alfa yerine ölçekten seçilmiş donuk bir ton kullanıyor.
`.gorsel-orgusu` ve `.katman-ortusu` ise alfayı doğru yerde kullanıyor: ikisi de zeminle
karışması **istenen** katmanlar.

`.devre-disi` sınıfı `opacity` kullanıyor çünkü ögenin tamamının — metni, kenarlığı, arka
planı — birlikte solması isteniyor. `pointer-events: none` görsel durumun etkileşimle
tutarlı olmasını sağlar; ancak devre dışı bırakma bilgisinin belgede `disabled` ya da
`aria-disabled` ile bildirilmesi gerekir. Saydamlık tek başına hiçbir yardımcı teknolojiye
bir şey söylemez.

#### Özet

- Alfa harmanlaması ağırlıklı ortalamadır: sonuç renk, üst rengin alfayla, alt rengin
  kalan payla çarpılıp toplanmasıyla bulunur.
- Saydam bir renk sabit bir renk değildir; sonucu zemine bağlıdır ve aynı bildirim iki
  farklı zeminde iki farklı renk üretir.
- Renk alfası yalnızca o bildirimi etkiler; `opacity` ögeyi ve bütün çocuklarını tek bir
  katman olarak saydamlaştırır ve bir yığılma bağlamı kurar.
- Kontrast oranı bağıl parlaklıklardan hesaplanır, 1:1 ile 21:1 arasındadır ve yönsüzdür;
  AA ölçütü normal metin için 4,5:1, büyük metin için 3:1'dir.
- Bir metni alfayla soluklaştırmak kontrast oranını düşürür; soluk ton, hesaplanmış bir
  donuk renk olarak ölçekten seçilir.

#### Sonraki Adım

Renk kararları verildi ama metnin kendisine henüz dokunulmadı: hangi yazı tipiyle, hangi
boyutta, hangi satır aralığıyla yazılacağı belirlenmedi. Sonraki ders tipografiyi ele alır
ve yazı tipi ailesinin nasıl seçildiğini, boyut ölçeğinin nasıl kurulduğunu ve satır
yüksekliğinin neden birimsiz yazıldığını hesapla gösterir.

### Tipografi

Source: https://academia.sh/tr/kurslar/css-temelleri/tipografi

Yazı tipi ailesi listesinin çözülmesi, ağırlık ve stil eksenleri, modüler boyut ölçeği ve satır yüksekliğinin birimsiz yazılmasının hesaplanabilir gerekçesi.

Renk kararları verildi ama metnin kendisine dokunulmadı. Ölçüm istasyonu sayfasında bir
başlık, bir künye paragrafı, bir ölçüm çizelgesi ve bir açıklama notu var; hepsi aynı yazı
tipiyle, aynı boyutta ve aynı satır aralığıyla duruyor.

Bu ders tipografinin üç ekseni üzerinde durur: hangi yazı tipiyle, hangi boyutta, hangi
satır aralığıyla. Üçünün de hesaplanabilir bir yanı vardır ve kararlar tek tek değil,
**ölçek** olarak verilir.

#### Yazı Tipi Ailesi Bir Listedir

`font-family` bir ad değil, virgülle ayrılmış bir **öncelik listesi** alır. Liste soldan
sağa denenir ve bulunan ilk aile kullanılır.

```css
body { font-family: "Inter", system-ui, sans-serif; }
```

Çözümleme karakter düzeyinde yapılır: listedeki ilk aile bir karakteri içermiyorsa, o
karakter için sonraki aileye bakılır. Bir belgede aynı satırdaki iki karakter iki farklı
aileden gelebilir. Türkçe metinlerde bu, noktalı harflerin listedeki farklı bir aileden
alınmasına ve satırın karışık görünmesine yol açabilir.

Listenin sonunda her zaman bir **genel aile (generic family)** bulunur: `serif`,
`sans-serif`, `monospace`, `cursive`, `system-ui`, `ui-monospace`. Bunlar belirli bir yazı
tipini değil, bir sınıfı adlandırır; hangi dosyanın kullanılacağını ortam belirler. Genel
aile yazılmadığında ve listedeki hiçbir ad bulunamadığında tarayıcının varsayılan yazı tipi
kullanılır — bu, kontrolün büsbütün bırakılması demektir.

Ad içinde boşluk varsa tırnak içine alınır. Genel aileler anahtar sözcüktür ve tırnaklanmaz;
tırnaklanırsa bir aile adı gibi aranır ve bulunamaz.

#### Ağırlık ve Stil Ayrı Eksenlerdir

`font-weight` 1 ile 1000 arasında bir sayı — yaygın ağırlıklar 100'ün katlarıdır — ya da `normal`
(400) ile `bold` (700) anahtar sözcüklerini alır. Bir ailede bildirilen ağırlığa karşılık gelen
bir dosya yoksa gerçekleştirim en yakınını seçer; bulunamayan bir ağırlığı hesapla üretmesi de
mümkündür ve sonuç, gerçek bir kalın kesimden farklı görünür.

`font-style` üç değer alır: `normal`, `italic`, `oblique`. Son ikisi arasında bir ayrım
vardır — `italic` ailenin ayrı çizilmiş eğik kesimini ister, `oblique` düz kesimin
eğilmesini kabul eder. Ailede eğik kesim yoksa `italic` de eğilerek üretilir.

Bu iki eksenin belgeyle ilişkisi ayrıca kurulmalıdır. Web'in Temelleri ve HTML kursunda
`strong` ve `em` ögelerinin anlam bildirdiği, `b` ve `i` ögelerinin ise yalnızca görünüm
bildirdiği kurulmuştu. Bir metni kalın göstermek için `strong` yazmak, o metne "önemli"
anlamı yükler. Yalnızca görsel bir vurgu isteniyorsa karar biçemde verilir.

#### Satır Yüksekliği Birimsiz Yazılır

`line-height` bir sayı, bir uzunluk ya da bir yüzde alabilir. Üçü de aynı ilk sonucu
verir ama **kalıtım** davranışları ayrılır.

```js
// tipografi.mjs — birimli ve birimsiz line-height farki; modular olcek
console.log("--- line-height: birimli deger kalitilir, hesaplanmis olarak ---");
const agac = [
  { ad: "body",  fontSize: 16 },
  { ad: "main",  fontSize: 16 },
  { ad: "h1",    fontSize: 32 },
  { ad: "small", fontSize: 12 },
];

for (const bicim of ["1.5", "24px", "150%"]) {
  console.log(`\nbody { line-height: ${bicim} }`);
  const govde = agac[0];
  for (const o of agac) {
    let satir;
    if (bicim.endsWith("px"))      satir = parseFloat(bicim);            // hesaplanmis: sabit px
    else if (bicim.endsWith("%"))  satir = govde.fontSize * parseFloat(bicim) / 100; // body'de cozulur
    else                           satir = o.fontSize * parseFloat(bicim);  // her ogede yeniden
    console.log(`  ${o.ad.padEnd(6)} font-size=${String(o.fontSize).padStart(2)}px -> line-height=${satir}px  (oran ${(satir / o.fontSize).toFixed(2)})`);
  }
}

console.log("\n--- modular olcek: taban 16px, oran 1.25 ---");
const taban = 16, oran = 1.25;
const adimlar = [-2, -1, 0, 1, 2, 3, 4];
for (const n of adimlar) {
  const px = taban * Math.pow(oran, n);
  console.log(`adim ${String(n).padStart(2)}: ${px.toFixed(2)}px = ${(px / taban).toFixed(4)}rem`);
}

console.log("\n--- satir uzunlugu ---");
const ORT_KARAKTER = 0.5; // ortalama karakter genisligi, em cinsinden (ornek deger)
for (const fs of [16, 18, 20]) {
  for (const en of [480, 640, 800]) {
    const karakter = en / (fs * ORT_KARAKTER);
    const uygun = karakter >= 45 && karakter <= 75 ? "uygun" : "uygun degil";
    console.log(`font-size=${fs}px, kutu=${en}px -> ~${karakter.toFixed(0)} karakter/satir  ${uygun}`);
  }
}
```

```
--- line-height: birimli deger kalitilir, hesaplanmis olarak ---

body { line-height: 1.5 }
  body   font-size=16px -> line-height=24px  (oran 1.50)
  main   font-size=16px -> line-height=24px  (oran 1.50)
  h1     font-size=32px -> line-height=48px  (oran 1.50)
  small  font-size=12px -> line-height=18px  (oran 1.50)

body { line-height: 24px }
  body   font-size=16px -> line-height=24px  (oran 1.50)
  main   font-size=16px -> line-height=24px  (oran 1.50)
  h1     font-size=32px -> line-height=24px  (oran 0.75)
  small  font-size=12px -> line-height=24px  (oran 2.00)

body { line-height: 150% }
  body   font-size=16px -> line-height=24px  (oran 1.50)
  main   font-size=16px -> line-height=24px  (oran 1.50)
  h1     font-size=32px -> line-height=24px  (oran 0.75)
  small  font-size=12px -> line-height=24px  (oran 2.00)

--- modular olcek: taban 16px, oran 1.25 ---
adim -2: 10.24px = 0.6400rem
adim -1: 12.80px = 0.8000rem
adim  0: 16.00px = 1.0000rem
adim  1: 20.00px = 1.2500rem
adim  2: 25.00px = 1.5625rem
adim  3: 31.25px = 1.9531rem
adim  4: 39.06px = 2.4414rem

--- satir uzunlugu ---
font-size=16px, kutu=480px -> ~60 karakter/satir  uygun
font-size=16px, kutu=640px -> ~80 karakter/satir  uygun degil
font-size=16px, kutu=800px -> ~100 karakter/satir  uygun degil
font-size=18px, kutu=480px -> ~53 karakter/satir  uygun
font-size=18px, kutu=640px -> ~71 karakter/satir  uygun
font-size=18px, kutu=800px -> ~89 karakter/satir  uygun degil
font-size=20px, kutu=480px -> ~48 karakter/satir  uygun
font-size=20px, kutu=640px -> ~64 karakter/satir  uygun
font-size=20px, kutu=800px -> ~80 karakter/satir  uygun degil
```

Üç bloğun karşılaştırılması kuralı veriyor.

Birimsiz `1.5` yazıldığında **oran kalıtılır**: her öge kendi yazı boyutunu 1,5 ile çarpar.
32 piksellik başlık 48, 12 piksellik küçük metin 18 birimlik satır yüksekliği aldı; oran her
yerde 1,50 kaldı.

`24px` ve `150%` yazıldığında ise **hesaplanmış değer kalıtılır**. Kalıtım ve varsayılan
değerler dersinde kurulan kural burada sonucunu veriyor: `body` ögesinde `150%` çözülür ve
24 pikselde sabitlenir; alt ögeler o sabiti devralır. 32 piksellik başlığın satır yüksekliği
24 birim oldu — oran 0,75, yani satır yazıdan **kısa**. Başlığın satırları üst üste biner.

Uygulanabilir kural şudur: `line-height` birimsiz yazılır. Birimli yazım yalnızca tek bir
ögede, kalıtımın istenmediği durumda kullanılır.

#### Modüler Ölçek

Yazı boyutları tek tek seçilmez; bir tabandan ve bir orandan türetilir. **Modüler ölçek
(modular scale)**, ardışık boyutların sabit bir oranla çarpılmasıyla üretilen dizidir:

$$b_n = b_0 \cdot r^n$$

Çıktının ikinci bloğu 16 piksel taban ve 1,25 oranla yedi basamak üretiyor. Sonuçlar `rem`
cinsine çevrilmiş hâlde de veriliyor; bu, ölçeğin biçem dosyasına nasıl yazılacağını
doğrudan gösterir:

```css
:root {
  --olcek-1: 0.64rem;
  --olcek-2: 0.8rem;
  --olcek-3: 1rem;
  --olcek-4: 1.25rem;
  --olcek-5: 1.5625rem;
  --olcek-6: 1.9531rem;
  --olcek-7: 2.4414rem;
}
```

Ölçeğin değeri, tutarlılığı zorunlu kılmasındadır: bir başlığa "biraz daha büyük" bir boyut
verilemez; ölçekteki bir sonraki basamak seçilir. Oran büyüdükçe basamaklar arasındaki fark
açılır ve hiyerarşi belirginleşir; küçüldükçe ölçek sıkışır ve düzeyler birbirine yaklaşır.

#### Satır Uzunluğu

Çıktının üçüncü bloğu, boyut ile kutu genişliği arasındaki ilişkiyi hesaplıyor. Okunabilir
satır uzunluğu için yerleşik ölçüt satır başına 45–75 karakterdir; hesap bu aralığa göre
değerlendiriliyor.

Tablo bir ödünleşimi gösteriyor. 640 birimlik bir kutuda 16 piksellik metin satır başına
yaklaşık 80 karakter veriyor — aralığın dışında. Aynı kutuda 18 piksellik metin 71 karakter
veriyor ve aralığa giriyor. Yazı boyutunu büyütmek satırı **kısaltır**, çünkü aynı genişliğe
daha az karakter sığar.

Bu, kutu boyutlandırma dersinde yazılan `max-width: 70ch` bildiriminin gerekçesidir: `ch`
birimi karakter genişliğine bağlı olduğu için, yazı boyutu değiştiğinde kutu genişliği de
onunla birlikte değişir ve satır uzunluğu aralıkta kalır. Hesaptaki 0,5 em ortalama karakter
genişliği örnek bir değerdir; gerçek oran yazı tipine göre değişir.

```css
/* istasyon.css — 18. adım: tipografi ölçeği */
:root {
  --yazi-govde: 1rem;
  --yazi-kucuk: 0.8rem;
  --yazi-h3: 1.25rem;
  --yazi-h2: 1.5625rem;
  --yazi-h1: 2.4414rem;
}

body {
  font-family: system-ui, sans-serif;
  font-size: var(--yazi-govde);
  line-height: 1.5;
}

h1 { font-size: var(--yazi-h1); line-height: 1.15; }
h2 { font-size: var(--yazi-h2); line-height: 1.2; }
h3 { font-size: var(--yazi-h3); line-height: 1.3; }

.kaynak-notu { font-size: var(--yazi-kucuk); }

.olcum-cizelgesi td.deger {
  font-variant-numeric: tabular-nums;
  font-feature-settings: "tnum";
}
```

Başlıklara gövde metninden **küçük** satır yüksekliği oranı verilmesi bir tipografi
kuralıdır: büyük punto satırlar zaten uzaktır ve aynı oran korunursa aralık gereğinden açık
görünür.

Son kural ölçüm çizelgesine özgüdür. `tabular-nums` rakamların eşit genişlikte
çizilmesini ister; böylece alt alta gelen sayıların basamakları hizalanır. Yazı tipi bu
özelliği taşımıyorsa bildirim etkisiz kalır ve rakamlar orantılı genişlikte kalır — sayısal
bir çizelgede okumayı güçleştiren bir durumdur.

#### Özet

- `font-family` bir öncelik listesidir ve karakter düzeyinde çözülür; listenin sonunda genel
  bir aile bulunmazsa kontrol büsbütün ortama bırakılmış olur.
- `font-weight` ve `font-style` ayrı eksenlerdir; ailede karşılığı olmayan bir ağırlık ya da
  eğim hesapla üretilir ve gerçek kesimden farklı görünür.
- `line-height` birimsiz yazıldığında oran kalıtılır ve her öge kendi boyutuyla çarpar;
  birimli ya da yüzdeli yazımda hesaplanmış değer kalıtılır ve büyük punto ögelerde satırlar
  üst üste biner.
- Yazı boyutları tek tek seçilmez; bir taban ve bir orandan modüler ölçek olarak türetilir
  ve bir hiyerarşi düzeyi ölçekteki bir basamağa karşılık gelir.
- Satır uzunluğu 45–75 karakter aralığında tutulur; yazı boyutunu büyütmek aynı kutuda
  satırı kısaltır, bu yüzden boyut ve kutu genişliği birlikte kararlaştırılır.

#### Sonraki Adım

Bu ders yazı tipini bir adla çağırdı ama adın karşılığı olan dosyanın nereden geldiğini
sormadı. Bir yazı tipi belgeye nasıl bağlanır, dosya inerken metin ne olur ve bu bekleme
sayfanın okunmasını nasıl etkiler? Sonraki ders yazı tipi yüklemesini ve yükleme sırasındaki
görüntüleme davranışını ele alır.

### Yazı Tipi Yükleme

Source: https://academia.sh/tr/kurslar/css-temelleri/yazi-tipi-yukleme

Yazı tipi dosyasının belgeye bağlanması, alt küme aralıklarıyla koşullu indirme ve yükleme süresince görüntüleme davranışının seçilmesi.

Önceki ders yazı tipini bir adla çağırdı: `font-family: "Inter", system-ui, sans-serif`.
Adın karşılığı olan dosyanın nereden geldiği sorulmadı. Sistemde kurulu olmayan bir yazı
tipi belgeye nasıl bağlanır ve dosya inerken metin ne olur?

Bu ders yazı tipi yüklemesini ele alır. Konu tipografiden çok yükleme sırasına ilişkindir
ve Web'in Temelleri ve HTML kursundaki kaynak yükleme sırası dersinin bir uzantısıdır: yazı
tipi de bir alt kaynaktır ve ne zaman geldiği sayfanın okunmasını etkiler.

#### Yazı Tipi Tanımı

`@font-face` kuralı bir aile adını bir dosyaya bağlar. Bir kural değildir — seçicisi
yoktur, hiçbir ögeye uygulanmaz. Yaptığı iş, `font-family` listelerinde kullanılabilecek
bir ad tanımlamaktır.

```css
@font-face {
  font-family: "Istasyon Sans";
  src: url("/yazi/istasyon-sans.woff2") format("woff2");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}
```

`font-weight` ve `font-style` burada bir bildirim değil, bir **tanımdır**: bu dosyanın
hangi ağırlığı ve stili sağladığını söyler. Aynı aile adına farklı ağırlıklar için ayrı
`@font-face` blokları yazılır ve tarayıcı, `font-weight: 700` istendiğinde o ağırlığı
sağlayan dosyayı seçer.

Bir ağırlık için dosya bildirilmediğinde tarayıcı en yakınını kullanır ve gerekirse
hesapla kalınlaştırır. Önceki derste anılan bu davranış, `@font-face` bloklarının eksik
yazıldığında ortaya çıkar.

`src` birden çok kaynak alabilir; ilk çözülebilen kullanılır. `format()` bildirimi, dosya
indirilmeden biçimin tanınıp tanınmadığının anlaşılmasını sağlar — tanınmayan biçimdeki
kaynak atlanır, indirilmez.

#### Alt Küme Aralıkları Koşullu İndirme Sağlar

`unicode-range` tanımı, bir dosyanın hangi kod noktalarını sağladığını bildirir. Bunun
sonucu belirleyicidir: **belgede o aralıktan hiç karakter yoksa dosya indirilmez.**

Aşağıdaki program dört alt küme dosyası tanımlar ve üç metin için hangilerinin
indirileceğini hesaplar.

```js
// yazitipi.mjs — unicode-range ile alt kume secimi ve font-display zaman cizelgesi
const dilimler = [
  { ad: "latin.woff2",     araliklar: [[0x0000, 0x00ff], [0x0131, 0x0131], [0x2000, 0x206f]] },
  { ad: "latin-ext.woff2", araliklar: [[0x0100, 0x024f], [0x1e00, 0x1eff]] },
  { ad: "greek.woff2",     araliklar: [[0x0370, 0x03ff]] },
  { ad: "cyrillic.woff2",  araliklar: [[0x0400, 0x04ff]] },
];

function gerekenDilimler(metin) {
  const gerekli = new Map();
  for (const ch of metin) {
    const kod = ch.codePointAt(0);
    for (const d of dilimler) {
      if (d.araliklar.some(([a, b]) => kod >= a && kod <= b)) {
        if (!gerekli.has(d.ad)) gerekli.set(d.ad, new Set());
        gerekli.get(d.ad).add(ch);
        break;
      }
    }
  }
  return gerekli;
}

const metinler = [
  "Kuzey Yamac Olcum Istasyonu",
  "Kuzey Yamaç Ölçüm İstasyonu",
  "Sıcaklık: -3,4 °C — bağıl nem %78",
];

for (const m of metinler) {
  const g = gerekenDilimler(m);
  console.log(`metin: ${m}`);
  for (const [ad, karakterler] of g) {
    console.log(`  ${ad.padEnd(17)} indirilir  (${karakterler.size} ayri karakter)`);
  }
  const inmeyen = dilimler.filter((d) => !g.has(d.ad)).map((d) => d.ad);
  console.log(`  indirilmeyen: ${inmeyen.join(", ") || "(yok)"}`);
  console.log("");
}

console.log("--- font-display: engelleme ve degistirme sureleri ---");
const DAVRANIS = {
  "auto":     { engelleme: "gerceklestirime bagli", degistirme: "gerceklestirime bagli" },
  "block":    { engelleme: "kisa (~3s)",            degistirme: "sonsuz" },
  "swap":     { engelleme: "cok kisa (~100ms)",     degistirme: "sonsuz" },
  "fallback": { engelleme: "cok kisa (~100ms)",     degistirme: "kisa (~3s)" },
  "optional": { engelleme: "cok kisa (~100ms)",     degistirme: "yok" },
};
for (const [ad, d] of Object.entries(DAVRANIS)) {
  console.log(`${ad.padEnd(9)} engelleme=${d.engelleme.padEnd(22)} degistirme=${d.degistirme}`);
}

console.log("\n--- engelleme suresinde metin gorunur mu ---");
for (const [ad] of Object.entries(DAVRANIS)) {
  const gorunur = ad === "block" || ad === "auto" ? "hayir (gorunmez metin)" : "evet (yedek yazi tipi)";
  console.log(`${ad.padEnd(9)} -> ${gorunur}`);
}
```

```
metin: Kuzey Yamac Olcum Istasyonu
  latin.woff2       indirilir  (17 ayri karakter)
  indirilmeyen: latin-ext.woff2, greek.woff2, cyrillic.woff2

metin: Kuzey Yamaç Ölçüm İstasyonu
  latin.woff2       indirilir  (17 ayri karakter)
  latin-ext.woff2   indirilir  (1 ayri karakter)
  indirilmeyen: greek.woff2, cyrillic.woff2

metin: Sıcaklık: -3,4 °C — bağıl nem %78
  latin.woff2       indirilir  (22 ayri karakter)
  latin-ext.woff2   indirilir  (1 ayri karakter)
  indirilmeyen: greek.woff2, cyrillic.woff2

--- font-display: engelleme ve degistirme sureleri ---
auto      engelleme=gerceklestirime bagli  degistirme=gerceklestirime bagli
block     engelleme=kisa (~3s)             degistirme=sonsuz
swap      engelleme=cok kisa (~100ms)      degistirme=sonsuz
fallback  engelleme=cok kisa (~100ms)      degistirme=kisa (~3s)
optional  engelleme=cok kisa (~100ms)      degistirme=yok

--- engelleme suresinde metin gorunur mu ---
auto      -> hayir (gorunmez metin)
block     -> hayir (gorunmez metin)
swap      -> evet (yedek yazi tipi)
fallback  -> evet (yedek yazi tipi)
optional  -> evet (yedek yazi tipi)
```

İlk metin yalnızca ASCII karakterler taşıyor ve tek bir dosya indiriliyor. İkinci metin
Türkçe yazılışıyla ve bir dosya daha ekleniyor — üstelik **tek bir karakter** yüzünden.

O karakter büyük noktalı İ'dir (U+0130) ve genişletilmiş Latin dilimine düşer. Küçük
noktasız ı (U+0131) ise temel dilimin içinde tanımlıdır. İki harf, aynı alfabenin
parçası olmalarına karşın iki ayrı dosyadan gelir. Üçüncü metinde aynı durum ğ (U+011F)
için oluşuyor.

Bu, Türkçe metinlerde iki dosyanın birden indirileceği anlamına gelir; bir başlık yalnızca
İ harfi taşıdığı için ikinci dosyayı bekler. Alt küme dilimleri kendi metnine göre
üretildiğinde bu maliyet ortadan kalkar.

`unicode-range` kod noktalarıyla çalışır ve Bilgisayarlar Nasıl Çalışır kursunda tanımlanan
karakter kümesi–kod noktası ayrımı burada doğrudan işler: aralıklar karakterlerin
görünüşüne göre değil, kod noktası numaralarına göre bölünmüştür.

#### Görüntüleme Davranışı

Yazı tipi dosyası inerken metnin ne olacağı `font-display` ile seçilir. Karar iki süreyle
tanımlanır:

**Engelleme süresi (block period)** — dosya beklenirken metin **çizilmez**. Bu sürede metin
görünmezdir.

**Değiştirme süresi (swap period)** — metin yedek yazı tipiyle çizilir; dosya bu süre
içinde gelirse yazı tipi değiştirilir.

Çıktının ikinci ve üçüncü blokları beş değeri bu iki süreye göre ayırıyor. Süre değerleri
belirtimde **öneri** olarak verilir; kesin sayılar gerçekleştirime bağlıdır.

`block` — uzun bir engelleme süresi tanır. Sonuç, metnin bir süre görünmemesidir. Simge
yazı tipleri gibi yedeği anlamsız olan durumlar dışında istenmeyen bir davranıştır.

`swap` — engelleme süresi çok kısadır; metin hemen yedek yazı tipiyle görünür ve dosya
geldiğinde değişir. Metin hiçbir zaman görünmez olmaz, ama değişim anında düzen kayar.

`fallback` — kısa bir engelleme ve kısa bir değiştirme süresi tanır. Dosya geç gelirse
yedek yazı tipi kalıcı olur ve düzen bir daha kaymaz.

`optional` — değiştirme süresi yoktur. Dosya çok hızlı gelmezse o sayfa yüklemesinde hiç
kullanılmaz; önbelleğe alınıp sonraki ziyarette kullanılır.

Seçim bir ödünleşimdir: `swap` metnin okunabilirliğini önceler ve düzen kaymasını kabul
eder; `optional` düzen kararlılığını önceler ve tasarlanan yazı tipinden vazgeçmeyi kabul
eder.

#### Düzen Kaymasını Azaltmak

Yedek yazı tipi ile asıl yazı tipinin ölçüleri farklıysa, değişim anında metin kutusu
büyür ya da küçülür ve altındaki her şey kayar. Bu kayma iki yolla azaltılır.

Birincisi, yedek yazı tipini ölçü olarak asıl yazı tipine yaklaştırmaktır.
`size-adjust`, `ascent-override`, `descent-override` ve `line-gap-override` tanımları,
yerel bir yazı tipinden yedek olarak kullanılacak bir aile üretmeyi sağlar:

```css
@font-face {
  font-family: "Istasyon Sans Yedek";
  src: local("Arial");
  size-adjust: 97%;
  ascent-override: 90%;
}
```

İkincisi, dosyanın belge ayrıştırılırken erken istenmesini sağlamaktır:

```html
<link rel="preload" href="/yazi/istasyon-sans.woff2" as="font" type="font/woff2" crossorigin>
```

Bu bağlantı bir biçem bağlantısı değildir; yalnızca kaynağın erken istenmesini bildirir.
Yazı tipi istekleri, kaynak aynı kökten gelse bile çapraz köken kurallarına tabidir; bu
yüzden `crossorigin` yazılmalıdır, yoksa kaynak iki kez indirilir.

#### Yerel Barındırma

Yazı tipi dosyasını kendi sunucusundan sunmak ile üçüncü taraf bir sunucudan çağırmak
arasındaki fark bir başarım ayrıntısı değildir.

Üçüncü taraf bir kaynak, ayrı bir bağlantı kurulmasını gerektirir: ad çözümlemesi, bağlantı
kurulumu ve güvenli el sıkışma yeniden yapılır. İnternet Nasıl Çalışır kursunda tanımlanan
bu adımların her biri gecikme ekler.

İkinci bir sonuç gizlilikle ilgilidir: her istek, ziyaretçinin adresini ve hangi sayfayı
görüntülediğini üçüncü tarafa bildirir. Yerel barındırma bu bildirimi ortadan kaldırır.

```css
/* istasyon.css — 19. adım: yazı tipi tanımı */
@font-face {
  font-family: "Istasyon Sans";
  src: url("/yazi/istasyon-sans-latin.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+2000-206F;
  font-weight: 400;
  font-display: swap;
}

@font-face {
  font-family: "Istasyon Sans";
  src: url("/yazi/istasyon-sans-latin-ext.woff2") format("woff2");
  unicode-range: U+0100-024F, U+1E00-1EFF;
  font-weight: 400;
  font-display: swap;
}

body { font-family: "Istasyon Sans", system-ui, sans-serif; }
```

İki tanım aynı aile adını paylaşır ve aralıklarıyla ayrılır. Sayfada Türkçe metin
bulunduğu için ikisi de indirilir; İngilizce bir sürümde yalnızca birincisi inerdi.

#### Özet

- `@font-face` bir kural değil bir tanımdır: bir aile adını bir dosyaya bağlar ve o
  dosyanın hangi ağırlık ile stili sağladığını bildirir.
- `unicode-range` dosyanın kapsadığı kod noktalarını bildirir; belgede o aralıktan karakter
  yoksa dosya hiç indirilmez.
- Türkçe metinlerde büyük noktalı İ ve ğ gibi harfler genişletilmiş Latin dilimine düşer;
  tek bir karakter ikinci bir dosyanın indirilmesine yol açabilir.
- `font-display` iki süreyi ayarlar: engelleme süresinde metin çizilmez, değiştirme
  süresinde yedek yazı tipiyle çizilir ve dosya gelirse değiştirilir.
- Yerel barındırma ayrı bir bağlantı kurulumunu ortadan kaldırır ve ziyaretçi bilgisinin
  üçüncü tarafa bildirilmesini engeller.

#### Sonraki Adım

Yazı tipi geldi ve boyut ölçeği kuruldu. Metnin kendisi üzerinde yapılacak kararlar kaldı:
satırların nasıl hizalanacağı, boşlukların nasıl işleneceği, harflerin büyük–küçük
dönüşümü ve altı çizili metnin nasıl biçimlendirileceği. Sonraki ders bu kararları ele alır
ve boşluk işlemenin kurallarını çalıştırılabilir bir örnekle gösterir.

### Metin Biçimlendirme

Source: https://academia.sh/tr/kurslar/css-temelleri/metin-bicimlendirme

Boşluk işleme kuralları, hizalama ve girinti, büyük–küçük harf dönüşümünün dile bağlılığı ve alt çizgi süslemesinin denetimi.

Yazı tipi geldi ve boyut ölçeği kuruldu. Metnin kendisi üzerinde verilecek kararlar kaldı:
kaynak metindeki boşluklara ne olur, satırlar nasıl hizalanır, harfler nasıl dönüştürülür,
bağlantıların altı nasıl çizilir.

Bu kararların ilki en çok şaşırtan olanıdır. Web'in Temelleri ve HTML kursunda yazılan
belgede metin girintili yazılmıştı ve girintiler ekranda görünmedi. Bu ders bunun sebebini
kuralla ve hesapla gösterir.

#### Boşluk İşleme

Varsayılan davranışta, kaynak metindeki **ardışık boşluk karakterleri tek bir boşluğa
indirgenir** ve satır sonları da birer boşluk karakteri sayılır. Bu, HTML belgesinin
biçimlendirilebilir olmasını sağlar: girintileme, satır bölme ve hizalama kaynak metinde
serbestçe yapılır, çıktıyı etkilemez.

Kural `white-space` özelliğiyle değiştirilir. Aşağıdaki program beş değeri aynı kaynak metin
üzerinde uygular.

```js
// bosluk.mjs — white-space degerlerinin bosluk isleme kurallarini uygular
const KAYNAK = "Kuzey   Yamac\n  Istasyonu\tolcum   verisi";

const DAVRANIS = {
  "normal":   { bosluklariBirlestir: true,  satirSonlariniKoru: false, sarma: true  },
  "nowrap":   { bosluklariBirlestir: true,  satirSonlariniKoru: false, sarma: false },
  "pre":      { bosluklariBirlestir: false, satirSonlariniKoru: true,  sarma: false },
  "pre-wrap": { bosluklariBirlestir: false, satirSonlariniKoru: true,  sarma: true  },
  "pre-line": { bosluklariBirlestir: true,  satirSonlariniKoru: true,  sarma: true  },
};

function isle(metin, kural) {
  let s = metin;
  if (kural.satirSonlariniKoru) {
    if (kural.bosluklariBirlestir) s = s.split("\n").map((p) => p.replace(/[ \t]+/g, " ").trim()).join("\n");
  } else {
    s = s.replace(/\s+/g, " ").trim();
  }
  return s;
}

console.log("kaynak (kacis dizileriyle):");
console.log(JSON.stringify(KAYNAK));
console.log("");
for (const [ad, k] of Object.entries(DAVRANIS)) {
  const sonuc = isle(KAYNAK, k);
  console.log(`${ad.padEnd(9)} sarma=${String(k.sarma).padEnd(5)} -> ${JSON.stringify(sonuc)}`);
}

console.log("\n--- text-transform: Turkce ozel durumu ---");
const ornek = "istasyon ILIK ışık";
console.log(`kaynak            : ${ornek}`);
console.log(`uppercase (en)    : ${ornek.toLocaleUpperCase("en-US")}`);
console.log(`uppercase (tr)    : ${ornek.toLocaleUpperCase("tr-TR")}`);
console.log(`lowercase (en)    : ${ornek.toLocaleLowerCase("en-US")}`);
console.log(`lowercase (tr)    : ${ornek.toLocaleLowerCase("tr-TR")}`);

console.log("\n--- text-indent ve hizalama etkisi (60 karakterlik kutu) ---");
const KUTU = 60;
const satir = "Istasyon deniz seviyesinden 1840 metre yukseklikte";
for (const [ad, hizala] of [["start", (s) => s.padEnd(KUTU)], ["end", (s) => s.padStart(KUTU)], ["center", (s) => s.padStart((KUTU + s.length) / 2).padEnd(KUTU)]]) {
  console.log(`${ad.padEnd(7)}|${hizala(satir)}|`);
}
```

```
kaynak (kacis dizileriyle):
"Kuzey   Yamac\n  Istasyonu\tolcum   verisi"

normal    sarma=true  -> "Kuzey Yamac Istasyonu olcum verisi"
nowrap    sarma=false -> "Kuzey Yamac Istasyonu olcum verisi"
pre       sarma=false -> "Kuzey   Yamac\n  Istasyonu\tolcum   verisi"
pre-wrap  sarma=true  -> "Kuzey   Yamac\n  Istasyonu\tolcum   verisi"
pre-line  sarma=true  -> "Kuzey Yamac\nIstasyonu olcum verisi"

--- text-transform: Turkce ozel durumu ---
kaynak            : istasyon ILIK ışık
uppercase (en)    : ISTASYON ILIK IŞIK
uppercase (tr)    : İSTASYON ILIK IŞIK
lowercase (en)    : istasyon ilik ışık
lowercase (tr)    : istasyon ılık ışık

--- text-indent ve hizalama etkisi (60 karakterlik kutu) ---
start  |Istasyon deniz seviyesinden 1840 metre yukseklikte          |
end    |          Istasyon deniz seviyesinden 1840 metre yukseklikte|
center |     Istasyon deniz seviyesinden 1840 metre yukseklikte     |
```

Çıktı iki ekseni ayırıyor: boşlukların birleştirilip birleştirilmediği ve satır
bölünmesine izin verilip verilmediği.

`normal` — ardışık boşluklar tek boşluğa iner, satır sonları boşluk sayılır, satır kutuya
göre bölünür. Kaynak metindeki üç boşluk ve sekme karakteri tek bir boşluğa dönüştü.

`nowrap` — aynı boşluk işlemesini yapar ama satır bölünmesine izin vermez. Metin kutudan
taşar; taşma yönetimi dersinde gördüğümüz durum ortaya çıkar.

`pre` — hiçbir şey değiştirmez: boşluklar ve satır sonları olduğu gibi korunur, otomatik
satır bölünmesi yapılmaz. `pre` ögesinin tarayıcı varsayılan biçemi budur.

`pre-wrap` — boşlukları ve satır sonlarını korur ama kutuya sığmayan satırları böler. Uzun
satırlar taşırmadan gösterilmek istendiğinde `pre` yerine bu değer yazılır.

`pre-line` — boşlukları birleştirir ama **satır sonlarını korur**. Bir metin alanından
gelen kullanıcı metnini paragraf yapısıyla göstermek için kullanılır.

#### Harf Dönüşümü Dile Bağlıdır

Çıktının ikinci bloğu, `text-transform` özelliğinin neden yalnızca görsel bir dönüşüm
sayılamayacağını gösteriyor.

`istasyon` sözcüğü İngilizce kurallarla büyütüldüğünde `ISTASYON`, Türkçe kurallarla
büyütüldüğünde `İSTASYON` oluyor. `ILIK` sözcüğü İngilizce kurallarla küçültüldüğünde
`ilik`, Türkçe kurallarla `ılık` oluyor — iki farklı sözcük.

Sebep, Türkçe alfabesinde `i` ve `ı` harflerinin ayrı harfler olması ve büyük
karşılıklarının `İ` ve `I` olmasıdır. Bilgisayarlar Nasıl Çalışır kursundaki kod noktası
ayrımı burada anlam farkına dönüşür.

Bu, `text-transform` yazmanın belgedeki `lang` bildirimine bağlı olduğunu gösterir. Kök
ögede `lang="tr"` bildirilmemiş bir belgede büyük harfe çevirme yanlış sonuç verebilir.

İkinci bir uyarı: dönüşüm yalnızca çizimde yapılır. Belgedeki metin değişmez; kopyalanan
metin kaynaktaki hâliyle gelir ve ekran okuyucu kaynaktaki hâlini okur. Bir başlığın büyük
harfle yazılması gerekiyorsa bu bir görsel karardır ve biçemde verilir; metnin kendisi
kaynakta olağan yazımıyla durmalıdır.

#### Hizalama

`text-align` beş temel değer alır: `start`, `end`, `center`, `justify` ve fiziksel
karşılıkları `left` ile `right`. Mantıksal değerler yazma yönüne uyar ve bu kursta
kullanılan biçim odur.

Çıktının üçüncü bloğu üç hizalamayı sabit genişlikli bir kutuda gösteriyor. Karar üç
ölçütle verilir.

**Ortalama** kısa metinlerde işler; uzun paragraflarda her satırın başlangıç noktası
değiştiği için gözün satır başını bulması zorlaşır. Başlık ve kısa açıklama için uygun,
gövde metni için değil.

**İki yana yaslama** (`justify`) sözcük aralıklarını genişleterek satırları aynı uzunlukta yapar;
paragrafın son satırı bunun dışındadır ve varsayılan olarak başa yaslı kalır. Dar sütunlarda
aralıklar aşırı genişler ve metnin içinde dikey beyaz şeritler oluşur. `hyphens: auto` ile
birlikte kullanıldığında sonuç daha dengelidir, ama dar sütunlarda yine de sola yaslama tercih
edilir.

**Girinti** (`text-indent`) paragraf başlarını içeri alır. Paragraflar arasında dikey
boşluk varsa girinti gereksizdir; ikisi birlikte kullanıldığında paragraf ayrımı iki kez
işaretlenmiş olur. Basılı metin geleneğinde girinti, boşluğun yerine geçer.

#### Metin Süslemesi

`text-decoration` bir kısayoldur ve dört açılımı ayarlar: `-line` (çizginin yeri), `-color`,
`-style`, `-thickness`.

Bağlantıların altının çizili olması tarayıcı varsayılanından gelir ve bir görsel süs
değildir: metin içindeki bir bağlantıyı yalnızca renkle işaretlemek, renk ayrımını
algılamayan bir okuyucu için bağlantıyı görünmez kılar. Alt çizginin kaldırılması, yerine
başka bir görsel ayırt edici konmadıkça yapılmamalıdır.

Alt çizginin okunabilirliği `text-underline-offset` ve `text-decoration-skip-ink` ile
ayarlanır; ikincisi çizginin harflerin alta inen kısımlarını kesmemesini sağlar.

```css
/* istasyon.css — 20. adım: metin biçimlendirme */
.olcum-cizelgesi th {
  text-transform: uppercase;
  letter-spacing: 0.04em;
  font-size: var(--yazi-kucuk);
}

.olcum-ham-veri {
  white-space: pre-wrap;
  font-family: ui-monospace, monospace;
  tab-size: 2;
}

.gozlem-notu { white-space: pre-line; }

main a {
  text-decoration-line: underline;
  text-underline-offset: 0.15em;
  text-decoration-thickness: 1px;
  text-decoration-skip-ink: auto;
}

main a:hover { text-decoration-thickness: 2px; }
```

`.olcum-ham-veri` sınıfı, istasyondan gelen ham ölçüm kaydını gösterir: satır sonları ve
hizalama boşlukları anlam taşır, bu yüzden korunur; `pre` yerine `pre-wrap` yazılması uzun
satırların taşmasını değil bölünmesini sağlar.

Başlık hücrelerine verilen harf aralığı, büyük harfe çevrilmiş metinlerde yerleşik bir
düzeltmedir: büyük harfler küçük harflere göre daha sıkışık görünür ve aralık açılmadığında
okunması zorlaşır.

#### Özet

- Varsayılan boşluk işlemesinde ardışık boşluklar tek boşluğa indirgenir ve satır sonları
  boşluk sayılır; kaynak metnin girintilenebilir olması buna dayanır.
- `white-space` iki ekseni birden ayarlar: boşlukların birleştirilip birleştirilmediği ve
  satır bölünmesine izin verilip verilmediği. `pre-wrap` boşlukları korur ve satırı böler,
  `pre-line` boşlukları birleştirir ve satır sonlarını korur.
- `text-transform` dile bağlıdır; Türkçede `i` ve `ı` harflerinin ayrı büyük karşılıkları
  olduğu için `lang` bildirimi olmadan yanlış dönüşüm üretilebilir.
- Dönüşüm yalnızca çizimde yapılır; belgedeki metin değişmez ve kopyalandığında kaynaktaki
  hâliyle gelir.
- Bağlantıların alt çizgisi bir süs değil, renkten bağımsız bir ayırt edicidir; kaldırılacaksa
  yerine başka bir görsel işaret konur.

#### Sonraki Adım

Metin kararları tamamlandı. Kutuların arkasına henüz bakılmadı: bir kutunun arka planı
yalnızca bir renk değil, konumlandırılabilir ve yinelenebilir bir görsel de olabilir.
Sonraki ders arka plan katmanlarını, konumlandırma ve yineleme kurallarını ve arka planın
hangi bölgeye kadar uzandığını ele alır.

### Arka Planlar

Source: https://academia.sh/tr/kurslar/css-temelleri/arka-planlar

Arka plan katmanları, yüzdeli konumlandırmanın hesabı, yineleme kipleri ile cover ve contain ölçeklemesinin sayısal karşılığı.

Metin kararları tamamlandı. Kutuların arkasına henüz bakılmadı. Kutu modeli dersinde arka
planın kenarlık kutusuna kadar uzandığı ve iç boşluğu kapsadığı kurulmuştu; arka planın
kendisinin ne olabileceği söylenmedi.

Bir arka plan yalnızca bir renk değildir. Konumlandırılabilir, yinelenebilir,
ölçeklenebilir görsellerden oluşan bir **katman yığını** olabilir. Bu ders o katmanların
kurallarını ve hesaplarını ele alır.

#### Arka Plan Bir Katman Yığınıdır

`background` bir kısayoldur ve sekiz açılımı ayarlar: `-color`, `-image`, `-position`,
`-size`, `-repeat`, `-origin`, `-clip`, `-attachment`.

`background-image` virgülle ayrılmış birden çok değer alabilir. Her değer bir **katman**
kurar ve katmanlar **soldan sağa üstten alta** sıralanır: listenin ilk üyesi en üstte
çizilir.

`background-color` bir katman değildir; her zaman en altta, tek bir renk olarak çizilir.
Bu yüzden virgülle çoğaltılamaz.

Diğer açılımlar da virgülle çoğaltılır ve katmanlarla sırayla eşleşir. Katman sayısı
`background-image` listesinin uzunluğuyla belirlenir; kısa kalan listeler baştan
tekrarlanır.

#### Yüzdeli Konum Bir Hizalamadır

`background-position` yüzde aldığında bu yüzde bir uzaklık değil, bir **hizalama**dır:
görselin yüzde $p$ noktası, alanın yüzde $p$ noktasına denk getirilir. Sonuç:

$$x = (A_{\text{en}} - G_{\text{en}}) \cdot p$$

```js
// arkaplan.mjs — background-position yuzdelerinin ve yinelemenin hesabi
// yuzde konumlandirma: gorselin %p noktasi, alanin %p noktasina denk gelir
function konum(alanEn, gorselEn, deger) {
  if (typeof deger === "number") return deger;                 // px
  const p = parseFloat(deger) / 100;
  return (alanEn - gorselEn) * p;
}

const ALAN = { en: 400, boy: 200 };
const GORSEL = { en: 120, boy: 60 };

console.log(`alan ${ALAN.en}x${ALAN.boy}, gorsel ${GORSEL.en}x${GORSEL.boy}`);
for (const [x, y] of [["0%","0%"], ["50%","50%"], ["100%","100%"], ["25%","75%"], [16, 16]]) {
  const cx = konum(ALAN.en, GORSEL.en, x);
  const cy = konum(ALAN.boy, GORSEL.boy, y);
  console.log(`position: ${String(x).padEnd(5)} ${String(y).padEnd(5)} -> sol ust kose (${cx}, ${cy})`);
}

console.log("\n--- repeat: dosemenin baslangic noktalari ---");
function dosemeler(alan, gorsel, baslangic, yineleme) {
  if (yineleme === "no-repeat") return [baslangic];
  const noktalar = [];
  let x = baslangic;
  while (x > -gorsel) x -= gorsel;   // sola dogru geri sar
  x += gorsel;
  while (x < alan) { noktalar.push(x); x += gorsel; }
  return noktalar;
}
for (const y of ["repeat", "no-repeat"]) {
  const n = dosemeler(ALAN.en, GORSEL.en, konum(ALAN.en, GORSEL.en, "50%"), y);
  console.log(`${y.padEnd(10)} -> ${n.length} dosem, x = ${n.join(", ")}`);
}

console.log("\n--- repeat: yatay kalan bosluk ---");
for (const alanEn of [400, 360, 480]) {
  const tam = Math.floor(alanEn / GORSEL.en);
  const kalan = alanEn - tam * GORSEL.en;
  const roundOlcek = alanEn / Math.round(alanEn / GORSEL.en);
  console.log(`alan=${alanEn}: repeat -> ${tam} tam + ${kalan}px kirpik | round -> ${Math.round(alanEn / GORSEL.en)} adet, her biri ${roundOlcek.toFixed(2)}px | space -> ${tam} adet, aralarinda ${tam > 1 ? (kalan / (tam - 1)).toFixed(2) : 0}px`);
}

console.log("\n--- background-size: cover ve contain ---");
function olcekle({ alanEn, alanBoy, gorselEn, gorselBoy }, kip) {
  const ox = alanEn / gorselEn, oy = alanBoy / gorselBoy;
  const o = kip === "cover" ? Math.max(ox, oy) : Math.min(ox, oy);
  return { en: +(gorselEn * o).toFixed(1), boy: +(gorselBoy * o).toFixed(1), olcek: +o.toFixed(3) };
}
const g = { alanEn: 400, alanBoy: 200, gorselEn: 800, gorselBoy: 600 };
for (const kip of ["cover", "contain"]) {
  const r = olcekle(g, kip);
  console.log(`${kip.padEnd(8)} olcek=${r.olcek} -> ${r.en}x${r.boy}  (alan ${g.alanEn}x${g.alanBoy})`);
}
```

```
alan 400x200, gorsel 120x60
position: 0%    0%    -> sol ust kose (0, 0)
position: 50%   50%   -> sol ust kose (140, 70)
position: 100%  100%  -> sol ust kose (280, 140)
position: 25%   75%   -> sol ust kose (70, 105)
position: 16    16    -> sol ust kose (16, 16)

--- repeat: dosemenin baslangic noktalari ---
repeat     -> 5 dosem, x = -100, 20, 140, 260, 380
no-repeat  -> 1 dosem, x = 140

--- repeat: yatay kalan bosluk ---
alan=400: repeat -> 3 tam + 40px kirpik | round -> 3 adet, her biri 133.33px | space -> 3 adet, aralarinda 20.00px
alan=360: repeat -> 3 tam + 0px kirpik | round -> 3 adet, her biri 120.00px | space -> 3 adet, aralarinda 0.00px
alan=480: repeat -> 4 tam + 0px kirpik | round -> 4 adet, her biri 120.00px | space -> 4 adet, aralarinda 0.00px

--- background-size: cover ve contain ---
cover    olcek=0.5 -> 400x300  (alan 400x200)
contain  olcek=0.333 -> 266.7x200  (alan 400x200)
```

İlk blokta `50% 50%` değerinin 140 ve 70 vermesi hesabın doğrudan sonucudur: alan ile
görsel arasındaki farkın yarısı. Sonuç, görselin ortalanmasıdır. `100% 100%` sağ alt köşeye
yaslar, `0% 0%` sol üst köşeye.

Piksel değerlerinin farkı son satırda görülüyor: `16px 16px` her zaman kenardan 16 birim
uzaklıktır, alan ve görsel ölçülerinden bağımsız. Yüzde bir hizalama, uzunluk bir uzaklık
bildirir.

`background-position` dört değerli bir yazımı da kabul eder ve hangi kenardan ölçüleceğini
seçtirir: `right 16px bottom 16px` yazımı sağ alt köşeden 16 birim içeri koyar.

#### Yineleme Kipleri

İkinci blok, `repeat` kipinde döşemenin görünen alanın **dışından** başladığını gösteriyor:
ilk döşem $-100$ noktasında. Yineleme, konumlandırılan döşemin iki yönde de sonsuza
uzatılması olarak tanımlıdır; görünen alanın dışına düşenler kırpılır.

Üçüncü blok üç kipin farkını sayıyla veriyor. 400 birimlik bir alanda 120 birimlik görsel:

- `repeat` — 3 tam döşem yerleşir, 40 birimlik bir kalan kırpık olarak görünür.
- `round` — döşem sayısı yuvarlanır ve görsel **ölçeklenir**: 3 adet, her biri 133,33 birim.
  Kırpık kalmaz, ama görselin oranı bozulur.
- `space` — döşem sayısı aşağı yuvarlanır ve kalan alan **aralara dağıtılır**: 3 adet,
  aralarında 20'şer birim. Ölçek korunur, aralık açılır.

Alan görselin tam katı olduğunda (360 ve 480 satırları) üç kip de aynı sonucu verir.

`repeat-x` ve `repeat-y` yalnızca bir eksende yineler; `no-repeat` hiç yinelemez.

#### cover ve contain

Son blok iki ölçekleme anahtar sözcüğünü karşılaştırıyor. 400×200 alanda 800×600 bir görsel:

`cover` — iki eksendeki ölçek oranından **büyüğünü** seçer. Sonuç 400×300: görsel alanı tam
doldurur, taşan kısım kırpılır.

`contain` — **küçüğünü** seçer. Sonuç 266,7×200: görselin tamamı görünür, alanın bir kısmı
boş kalır.

Karar şu soruyla verilir: kırpılması mı, boşluk kalması mı kabul edilebilir? Dekoratif bir
görselde `cover`, tamamı görülmesi gereken bir görselde `contain` yazılır.

`cover` kullanılırken kırpmanın nereden yapılacağı `background-position` ile
yönlendirilir; görselin önemli kısmı kenardaysa ortalama kırpma onu keser.

#### Arka Planın Sınırları

Üç özellik arka planın hangi bölgeye ilişkin olduğunu belirler ve kutu modeli dersindeki
bölge adları burada değer olarak geçer.

`background-origin` — konumlandırmanın **hangi kutuya göre** yapılacağını seçer. Başlangıç
değeri `padding-box`'tır.

`background-clip` — arka planın **nereye kadar çizileceğini** seçer. Başlangıç değeri
`border-box`'tır; bu yüzden kesikli bir kenarlığın boşluklarından arka plan görünür.
`content-box` yazıldığında arka plan iç boşluğa girmez.

`background-attachment` — arka planın kutuyla birlikte mi kaydırılacağını (`scroll`, `local`)
yoksa görüntü alanına mı sabitleneceğini (`fixed`) seçer.

#### Görsel Arka Planlar ve Erişilebilirlik

Bir arka plan görseli belgede yoktur: alternatif metni yoktur, yardımcı teknolojiye
bildirilmez, arama motoru tarafından içerik olarak okunmaz. Bu, sözde elemanlarla üretilen
içeriğin sınırıyla aynı sınırdır.

Sonuç bir kuraldır: **anlam taşıyan hiçbir görsel arka plan olarak konmaz.** Konum haritası
belgede `img` ögesiyle ve alternatif metniyle durmalıdır. Arka plan, dekoratif dokular ve
üzerine metin yazılacak zeminler içindir.

Zemin görselinin üzerine yazılan metnin okunabilirliği ayrıca güvenceye alınmalıdır. Görsel
inmediğinde ya da inmesi geciktiğinde metin arka plan **rengiyle** karşı karşıya kalır; o
renk ile metin rengi arasındaki kontrast, önceki derste hesaplanan ölçütü geçmelidir.

```css
/* istasyon.css — 21. adım: arka plan katmanları */
.kunye {
  background-color: var(--zemin);
  background-image: url("/gorsel/topografya.svg");
  background-position: right -40px top 50%;
  background-repeat: no-repeat;
  background-size: 320px auto;
}

.olcum-cizelgesi tbody tr:nth-child(odd) {
  background-color: hsl(var(--ton) 18% 97%);
  background-clip: padding-box;
}

.uyari-kutusu {
  background-color: hsl(0 66% 97%);
  background-image: url("/gorsel/uyari.svg");
  background-position: 12px center;
  background-repeat: no-repeat;
  background-size: 20px 20px;
  padding-inline-start: 44px;
}
```

Künyedeki desen dekoratiftir ve `background-color` ile birlikte yazılmıştır: görsel
inmediğinde zemin rengi kalır ve başlık metninin kontrastı korunur.

`.uyari-kutusu` sınıfındaki simge de dekoratiftir; uyarının kendisi kutunun içindeki metinde
yazılıdır. Simge yalnızca metnin görsel işaretidir ve `padding-inline-start` ile ona yer
açılmıştır — simgenin metnin üzerine binmemesi bu bildirime bağlıdır.

#### Özet

- `background-image` virgülle çoğaltılarak katman yığını kurar; listenin ilk üyesi en üstte
  çizilir ve `background-color` her zaman en altta, tek renk olarak kalır.
- Yüzdeli arka plan konumu bir uzaklık değil hizalamadır: görselin yüzde $p$ noktası alanın
  yüzde $p$ noktasına denk getirilir; `50%` bu yüzden ortalar.
- Yineleme kipleri kalan boşluğu farklı ele alır: `repeat` kırpar, `round` görseli
  ölçekleyerek kırpığı kaldırır, `space` kalanı döşemler arasına dağıtır.
- `cover` iki ölçek oranından büyüğünü seçer ve alanı doldurup taşanı kırpar; `contain`
  küçüğünü seçer ve görselin tamamını gösterip boşluk bırakır.
- Arka plan görseli belgede yoktur ve yardımcı teknolojiye bildirilmez; anlam taşıyan
  görseller işaretlemede, arka planlar yalnızca dekoratif katman olarak yazılır.

#### Sonraki Adım

Arka plan katmanları bir dosyadan gelen görsellerle kuruldu. Bir katman dosyasız da
üretilebilir: renkler arasında hesaplanmış geçişlerle. Sonraki ders doğrusal ve dairesel
geçişleri, renk durak noktalarının hesabını ve geçişlerin neden görsel değil arka plan
görseli sayıldığını ele alır.

### Geçişler (gradient)

Source: https://academia.sh/tr/kurslar/css-temelleri/gecisler

Doğrusal ve dairesel renk geçişleri; durak noktalarının çözülme kuralı, ara renklerin hesaplanması ve sert geçişle çizgi üretimi.

Arka plan katmanları bir dosyadan gelen görsellerle kuruldu. Bir katman dosyasız da
üretilebilir: renkler arasında hesaplanmış geçişlerle.

**Geçiş (gradient)** bir renk değil, bir **görseldir**. Bu ayrım sonuçlar doğurur:
`background-color` değerine yazılamaz, `background-image` değerine yazılır; bir ölçüsü
yoktur ve konulduğu alana göre boyutlanır; `background-size`, `background-position` ve
`background-repeat` ona da uygulanır.

#### Durak Noktaları

Bir geçiş, **renk durak noktalarından (color stop)** oluşur. Her durak bir renk ve isteğe
bağlı bir konum taşır. Konumlar yazılmadığında bir kurala göre doldurulur: ilk durak
%0'a, son durak %100'e yerleşir, aradakiler **eşit aralıklarla** dağıtılır.

İki durak arasındaki her nokta, iki rengin doğrusal karışımıdır.

```js
// gecis.mjs — renk durak noktalarinin cozulmesi ve ara renklerin hesaplanmasi
const hexToRgb = (h) => ({ r: parseInt(h.slice(1,3),16), g: parseInt(h.slice(3,5),16), b: parseInt(h.slice(5,7),16) });
const hex = ({r,g,b}) => "#" + [r,g,b].map(v => Math.round(v).toString(16).padStart(2,"0")).join("");

// yazilmamis durak noktalari, komsu bilinen noktalar arasinda esit dagitilir
function duraklariCoz(duraklar) {
  const k = duraklar.map((d) => ({ ...d }));
  if (k[0].konum == null) k[0].konum = 0;
  if (k.at(-1).konum == null) k.at(-1).konum = 100;
  let i = 0;
  while (i < k.length) {
    if (k[i].konum != null) { i++; continue; }
    let j = i;
    while (k[j].konum == null) j++;
    const bas = k[i - 1].konum, son = k[j].konum;
    const adim = (son - bas) / (j - i + 1);
    for (let t = i; t < j; t++) k[t].konum = +(bas + adim * (t - i + 1)).toFixed(2);
    i = j;
  }
  // durak noktalari azalamaz: onceki degerin altina duseni yukselt
  for (let t = 1; t < k.length; t++) if (k[t].konum < k[t-1].konum) k[t].konum = k[t-1].konum;
  return k;
}

function araRenk(duraklar, yuzde) {
  const k = duraklariCoz(duraklar);
  if (yuzde <= k[0].konum) return k[0].renk;
  if (yuzde >= k.at(-1).konum) return k.at(-1).renk;
  for (let i = 1; i < k.length; i++) {
    if (yuzde <= k[i].konum) {
      const a = k[i-1], b = k[i];
      const t = b.konum === a.konum ? 0 : (yuzde - a.konum) / (b.konum - a.konum);
      const ca = hexToRgb(a.renk), cb = hexToRgb(b.renk);
      return hex({ r: ca.r + (cb.r-ca.r)*t, g: ca.g + (cb.g-ca.g)*t, b: ca.b + (cb.b-ca.b)*t });
    }
  }
}

const A = [{ renk: "#143a52" }, { renk: "#8a9da9" }, { renk: "#ffffff" }];
const B = [{ renk: "#143a52", konum: 0 }, { renk: "#8a9da9", konum: 20 }, { renk: "#ffffff", konum: 100 }];
const C = [{ renk: "#143a52", konum: 0 }, { renk: "#143a52", konum: 50 }, { renk: "#ffffff", konum: 50 }, { renk: "#ffffff", konum: 100 }];

for (const [ad, d] of [["esit dagitim", A], ["elle konumlandirilmis", B], ["sert gecis (cizgi)", C]]) {
  console.log(`--- ${ad} ---`);
  console.log("cozulen duraklar:", duraklariCoz(d).map((x) => `${x.renk}@${x.konum}%`).join(" "));
  const ornek = [0, 10, 20, 25, 49, 50, 51, 75, 100];
  console.log(ornek.map((p) => `${String(p).padStart(3)}%:${araRenk(d, p)}`).join("  "));
  console.log("");
}

console.log("--- aci yonu (linear-gradient) ---");
for (const [aci, yon] of [[0,"asagidan yukari"],[90,"soldan saga"],[180,"yukaridan asagi"],[270,"sagdan sola"]]) {
  const rad = (aci - 90) * Math.PI / 180;
  console.log(`${String(aci).padStart(3)}deg -> ${yon.padEnd(18)} birim vektor (${Math.cos(rad).toFixed(3)}, ${(-Math.sin(rad)).toFixed(3)})`);
}
```

```
--- esit dagitim ---
cozulen duraklar: #143a52@0% #8a9da9@50% #ffffff@100%
  0%:#143a52   10%:#2c4e63   20%:#436275   25%:#4f6c7e   49%:#889ba7   50%:#8a9da9   51%:#8c9fab   75%:#c5ced4  100%:#ffffff

--- elle konumlandirilmis ---
cozulen duraklar: #143a52@0% #8a9da9@20% #ffffff@100%
  0%:#143a52   10%:#4f6c7e   20%:#8a9da9   25%:#91a3ae   49%:#b4c1c8   50%:#b6c2c9   51%:#b7c3ca   75%:#dae0e4  100%:#ffffff

--- sert gecis (cizgi) ---
cozulen duraklar: #143a52@0% #143a52@50% #ffffff@50% #ffffff@100%
  0%:#143a52   10%:#143a52   20%:#143a52   25%:#143a52   49%:#143a52   50%:#143a52   51%:#ffffff   75%:#ffffff  100%:#ffffff

--- aci yonu (linear-gradient) ---
  0deg -> asagidan yukari    birim vektor (0.000, 1.000)
 90deg -> soldan saga        birim vektor (1.000, 0.000)
180deg -> yukaridan asagi    birim vektor (0.000, -1.000)
270deg -> sagdan sola        birim vektor (-1.000, -0.000)
```

İlk blok eşit dağıtımı gösteriyor: üç durak yazıldı, ortadaki %50'ye yerleşti. `%10`
noktasındaki renk `#2c4e63`, ilk iki durağın beşte bir karışımıdır.

İkinci blok aynı üç rengi farklı konumlarla kullanıyor. Orta durak %20'ye çekildiğinde
geçişin **hızı** değişiyor: ilk beşte birlik bölümde renk hızla açılıyor, kalan bölümde
yavaşlıyor. `%10` noktasında birinci blokta `#2c4e63` olan renk, burada `#4f6c7e` — çok
daha açık.

#### Sert Geçişle Çizgi Üretmek

Üçüncü blok bir hile gibi görünen ama tanımın doğrudan sonucu olan bir kalıbı gösteriyor.
İki durak **aynı konuma** yazıldığında aralarında karışacak mesafe kalmaz ve geçiş bir
kenara dönüşür: %49'da hâlâ koyu, %51'de tam beyaz.

Bu, geçişlerle çizgi, şerit ve dama deseni üretmenin yoludur:

```css
.olcum-cizelgesi {
  background-image: linear-gradient(
    to right,
    var(--cizgi) 0 1px,
    transparent 1px 100%
  );
  background-repeat: repeat-x;
  background-size: 120px 100%;
}
```

Bir durağa iki konum yazılması (`0 1px`) o rengin o aralık boyunca sabit kalmasını
bildirir; iki durak yazmanın kısayoludur.

#### Üç Geçiş Türü

**`linear-gradient()`** — renkler bir doğru boyunca değişir. Yön bir açıyla ya da bir
anahtar sözcükle verilir. Çıktının son bloğu açı ölçümünü veriyor: `0deg` yukarı doğru,
açı saat yönünde artıyor. `to top` yazımı `0deg`, `to right` yazımı `90deg` ile aynıdır.
Vektörlerdeki pozitif y yukarıyı gösterir.

**`radial-gradient()`** — renkler bir merkezden dışa doğru değişir. Şekil (`circle` ya da
`ellipse`), ölçü (`closest-side`, `farthest-corner` gibi) ve merkez ayrıca verilebilir.

**`conic-gradient()`** — renkler bir merkez çevresinde açıya göre değişir. Dilim
grafiklerinin ve renk çarklarının yazılabildiği tür budur.

Üçünün de `repeating-` önekli biçimleri vardır; durak dizisini sonuna kadar gidip baştan
başlatırlar.

#### Şerit Etkisi ve Karışım Uzayı

Geniş bir alanda iki yakın renk arasında yapılan geçişte, renk basamaklarının görünür
hâle gelmesi olağandır. Sekiz bitlik kanallarda ara değerler yuvarlanır ve büyük bir alanda
aynı değere yuvarlanan komşu bölgeler bir şerit gibi görünür.

Karışımın hangi renk uzayında yapılacağı bildirilebilir:

```css
.zemin {
  background-image: linear-gradient(in oklab, #143a52, #8a5b00);
}
```

Uzay bildirilmediğinde karışım sRGB uzayında yapılır. Algısal olarak düzgün bir uzayda
yapılan karışım, iki renk arasındaki ara tonların soluklaşmasını azaltır — özellikle
tamamlayıcı renkler arasındaki geçişlerde görülür bir farktır.

Bu yazımın tanınıp tanınmadığı sınanabilir ve tanınmadığında öncesindeki bildirim yerinde
kalır:

```css
.zemin { background-image: linear-gradient(#143a52, #8a5b00); }

@supports (background-image: linear-gradient(in oklab, red, blue)) {
  .zemin { background-image: linear-gradient(in oklab, #143a52, #8a5b00); }
}
```

#### Geçişler Bir Görseldir

Bu dersin başındaki ayrımın pratik sonuçları vardır.

Bir geçiş `background-image` katmanıdır; birden çok geçiş virgülle üst üste konabilir ve
saydam bölgeler alttaki katmanı gösterir.

Bir geçişin doğal ölçüsü yoktur; `background-size` verilmediğinde konulduğu alanı doldurur.
Verildiğinde ise `background-repeat` ile döşenebilir — yukarıdaki çizgi örneği buna dayanır.

Bir geçiş dosya indirmez. Aynı görsel etki bir görsel dosyasıyla da elde edilebilirdi;
geçiş, ek bir istek olmadan ve alan değiştiğinde yeniden ölçeklenerek üretir.

```css
/* istasyon.css — 22. adım: geçişler */
.kunye {
  background-image:
    linear-gradient(to bottom, hsl(var(--ton) 61% 96%), hsl(var(--ton) 18% 97%));
}

.olcum-grafigi {
  background-image:
    linear-gradient(to top, var(--cizgi) 0 1px, transparent 1px 100%);
  background-size: 100% 40px;
}

.yukleme-cubugu {
  background-image: linear-gradient(
    to right,
    var(--marka-koyu) 0 var(--oran, 0%),
    var(--cizgi) var(--oran, 0%) 100%
  );
}
```

`.olcum-grafigi` sınıfı ölçüm grafiğinin arkasına 40 birimde bir yatay ızgara çizgisi
koyar. Çizgiler belgede yoktur ve olmamalıdır: bir ızgara çizgisi bilgi taşımaz, okumaya
yardım eden bir görsel yardımcıdır.

`.yukleme-cubugu` özel bir özelliği durak konumu olarak kullanıyor. `--oran` değeri
değiştiğinde çubuğun dolu kısmı değişir; geçişin kendisi yeniden yazılmaz. Değerin belgeden
gelmesi durumunda dolgu oranının ayrıca erişilebilir biçimde — örneğin `progress` ögesiyle
ya da uygun ARIA öznitelikleriyle — bildirilmesi gerekir. Görsel çubuk tek başına bir
duruma karşılık gelmez.

#### Özet

- Geçiş bir renk değil bir arka plan görselidir; `background-image` değerine yazılır,
  doğal ölçüsü yoktur ve konulduğu alana göre boyutlanır.
- Yazılmamış durak konumları doldurulur: ilk durak %0'a, son durak %100'e, aradakiler
  komşu bilinen noktalar arasına eşit aralıklarla dağıtılır.
- Durak konumlarını değiştirmek geçişin hızını değiştirir; iki durak aynı konuma yazıldığında
  karışacak mesafe kalmaz ve keskin bir kenar oluşur.
- Bir durağa iki konum yazmak o rengin aralık boyunca sabit kalmasını bildirir; çizgi ve
  şerit desenleri bu yazımla, dosya indirilmeden üretilir.
- Karışımın yapılacağı renk uzayı bildirilebilir; bildirilmediğinde sRGB kullanılır ve
  uzak renkler arasındaki ara tonlar soluklaşabilir.

#### Sonraki Adım

Kutuların içi ve arkası tamamlandı. Kenarları eksik: kenarlığın çizim biçimi, köşelerin
yuvarlatılması ve kutunun dışına düşen gölgeler henüz ele alınmadı. Sonraki ders bu üç
konuyu birleştirir ve köşe yarıçapının kenarlık kalınlığıyla nasıl hesaplandığını gösterir.

### Kenarlık, Gölge ve Şekil

Source: https://academia.sh/tr/kurslar/css-temelleri/kenarlik-golge-ve-sekil

Kenarlık bileşenleri, köşe yarıçapının ölçeklenme kuralı, iç yarıçap hesabı ve gölge katmanlarının kapladığı alan.

Kutuların içi ve arkası tamamlandı. Kenarları eksik kaldı: kenarlığın nasıl çizildiği,
köşelerin nasıl yuvarlatıldığı ve kutunun dışına düşen gölgelerin nereye kadar uzandığı.

Bu üç konu bir arada ele alınır, çünkü aynı sınırı paylaşırlar: kenarlık kutusu. Kenarlık
o sınırda çizilir, köşe yarıçapı o sınırı yuvarlatır, gölge o sınırın dışına düşer.

#### Kenarlığın Üç Bileşeni

`border` bir kısayoldur ve üç açılımı ayarlar: `-width`, `-style`, `-color`.

`border-style` yazılmadığında başlangıç değeri `none`'dır ve **kenarlık çizilmez**. Bir
kenarlığın görünmemesinin en sık sebebi budur: kalınlık ve renk yazılmış ama stil
yazılmamıştır.

Stil değerleri arasında `solid`, `dashed`, `dotted`, `double`, `groove`, `ridge`, `inset`,
`outset` ve `hidden` bulunur. Bunlardan `groove`, `ridge`, `inset` ve `outset` kenarlığa
hesaplanmış açık–koyu tonlar vererek kabartma etkisi üretir; renk denetimi ölçek dışına çıktığı
için tasarımlı arayüzlerde kullanılmaz. `hidden` ise kenarlığı tümüyle gizler.

`border-color` yazılmadığında `currentcolor` kullanılır — yani ögenin metin rengi. Renk
gösterimleri dersinde anılan bu davranış, kenarlığı metin rengine bağlı tutmanın
varsayılan olduğu anlamına gelir.

Dört kenar ayrı ayrı ayarlanabilir ve mantıksal adlarla yazılır: `border-block-end`,
`border-inline-start` ve akrabaları.

#### Köşe Yarıçapı Kutuya Sığmalıdır

`border-radius` köşeleri yuvarlatır. Değer, köşedeki yayın yarıçapıdır ve dört köşe için
ayrı ayrı verilebilir.

Bir kısıt vardır: bir kenardaki iki köşenin yarıçapları toplamı o kenarın uzunluğunu
aşamaz. Aşarsa bütün yarıçaplar **aynı çarpanla** küçültülür — yalnızca taşan köşe değil,
hepsi. Böylece köşeler arasındaki oran korunur.

```js
// kenarlik.mjs — kose yaricapi olceklemesi, ic yaricap ve golge kutusu hesabi
// kural: yaricap toplami kenardan buyukse hepsi ayni f carpaniyla kucultulur
function yaricapOlcek(kutu, r) {
  const { en, boy } = kutu;
  const f = Math.min(
    en  / (r.solUst + r.sagUst),
    en  / (r.solAlt + r.sagAlt),
    boy / (r.solUst + r.solAlt),
    boy / (r.sagUst + r.sagAlt),
    1
  );
  return Object.fromEntries(Object.entries(r).map(([k, v]) => [k, +(v * f).toFixed(2)]));
}

const durumlar = [
  { kutu: { en: 200, boy: 80 },  r: { solUst: 12, sagUst: 12, sagAlt: 12, solAlt: 12 } },
  { kutu: { en: 200, boy: 80 },  r: { solUst: 60, sagUst: 60, sagAlt: 60, solAlt: 60 } },
  { kutu: { en: 100, boy: 40 },  r: { solUst: 999, sagUst: 999, sagAlt: 999, solAlt: 999 } },
];
for (const d of durumlar) {
  const s = yaricapOlcek(d.kutu, d.r);
  console.log(`kutu ${d.kutu.en}x${d.kutu.boy}, istenen ${d.r.solUst} -> uygulanan ${s.solUst}`);
}

console.log("\n--- ic yaricap: kenarlik kalinligi kadar kucuk ---");
for (const [dis, kalinlik] of [[12, 1], [12, 4], [12, 16], [8, 8]]) {
  const ic = Math.max(0, dis - kalinlik);
  console.log(`dis yaricap=${String(dis).padStart(2)} kenarlik=${String(kalinlik).padStart(2)} -> ic yaricap=${ic}${ic === 0 ? "  (koseli ic kenar)" : ""}`);
}

console.log("\n--- box-shadow: golgenin kapladigi dikdortgen ---");
function golgeKutusu({ en, boy }, { x, y, bulanik, yayilma }) {
  // golge kutusu: yayilma kadar buyur, ofset kadar kay, bulanikligin yarisi kadar tas
  const tasma = bulanik / 2;
  return {
    sol:  x - yayilma - tasma,
    ust:  y - yayilma - tasma,
    sag:  en + x + yayilma + tasma,
    alt:  boy + y + yayilma + tasma,
  };
}
const kutu = { en: 200, boy: 80 };
for (const g of [
  { x: 0, y: 2,  bulanik: 4,  yayilma: 0 },
  { x: 0, y: 8,  bulanik: 24, yayilma: -4 },
  { x: 0, y: 0,  bulanik: 0,  yayilma: 3 },
]) {
  const k = golgeKutusu(kutu, g);
  console.log(`box-shadow: ${g.x}px ${g.y}px ${g.bulanik}px ${g.yayilma}px -> golge (${k.sol}, ${k.ust}) .. (${k.sag}, ${k.alt})`);
}
console.log(`kutunun kendisi: (0, 0) .. (${kutu.en}, ${kutu.boy})`);
```

```
kutu 200x80, istenen 12 -> uygulanan 12
kutu 200x80, istenen 60 -> uygulanan 40
kutu 100x40, istenen 999 -> uygulanan 20

--- ic yaricap: kenarlik kalinligi kadar kucuk ---
dis yaricap=12 kenarlik= 1 -> ic yaricap=11
dis yaricap=12 kenarlik= 4 -> ic yaricap=8
dis yaricap=12 kenarlik=16 -> ic yaricap=0  (koseli ic kenar)
dis yaricap= 8 kenarlik= 8 -> ic yaricap=0  (koseli ic kenar)

--- box-shadow: golgenin kapladigi dikdortgen ---
box-shadow: 0px 2px 4px 0px -> golge (-2, 0) .. (202, 84)
box-shadow: 0px 8px 24px -4px -> golge (-8, 0) .. (208, 96)
box-shadow: 0px 0px 0px 3px -> golge (-3, -3) .. (203, 83)
kutunun kendisi: (0, 0) .. (200, 80)
```

İlk blok üç durumu veriyor. 12 birimlik yarıçap 200×80 kutuya sığdı ve olduğu gibi
uygulandı. 60 birimlik yarıçap dikey kenara sığmadı — iki köşe toplamı 120, kenar 80 — ve
hepsi 40'a düşürüldü.

Üçüncü satır bir kalıbı açıklıyor. `border-radius: 999px` yazımı, kutuyu tam yuvarlak uçlu
yapmanın standart yoludur: değer her zaman kutunun yarısına inecek kadar büyük seçilir ve
kutunun ölçüsü ne olursa olsun sonuç bir hap biçimi olur. Örnekte 999 istendi, 20
uygulandı — 40 birimlik yüksekliğin yarısı.

#### İç Yarıçap

İkinci blok, iç içe geçmiş yuvarlak köşelerin neden bazen yanlış göründüğünü açıklıyor.

Kenarlığın **iç** kenarındaki yarıçap, dış yarıçaptan kenarlık kalınlığı kadar küçüktür.
Kalınlık yarıçaptan büyükse iç yarıçap sıfırlanır ve iç kenar köşeli olur — dıştan yuvarlak,
içten köşeli bir kenarlık ortaya çıkar.

Aynı hesap iç içe kutular için de geçerlidir. Bir kartın içine, iç boşluk kadar içeride
duran ikinci bir yuvarlak kutu konursa, iç kutunun yarıçapı dış yarıçaptan iç boşluk kadar
küçük olmalıdır; aksi hâlde iki yay birbirine koşut olmaz:

```css
.kart       { border-radius: 12px; padding: 8px; }
.kart .alan { border-radius: 4px; }   /* 12 - 8 */
```

#### Gölge Katmanları

`box-shadow` dört uzunluk alır: yatay ofset, dikey ofset, bulanıklık yarıçapı ve yayılma.
Son ikisi isteğe bağlıdır.

Üçüncü blok, bir gölgenin kapladığı dikdörtgeni hesaplıyor. Bulanıklık yarıçapı $b$ olan
bir gölge, kutunun kenarından yaklaşık $b/2$ kadar dışarı taşar; yayılma değeri ise gölge
dikdörtgenini doğrudan büyütür ya da küçültür.

İkinci satırdaki negatif yayılma bir kalıptır: geniş bir bulanıklıkla yumuşak bir gölge
istenirken gölgenin kutunun yanlarından taşması istenmiyorsa yayılma negatif verilir. Sonuç,
gölgenin ağırlıklı olarak alta düşmesidir.

Üçüncü satır bulanıklıksız ve ofsetsiz bir gölge gösteriyor: yalnızca yayılma. Bu, kutunun
çevresine keskin bir halka çizer ve yer kaplamayan bir kenarlık gibi davranır. `outline`
ile farkı, gölgenin köşe yarıçapını izlemesidir.

Virgülle ayrılarak birden çok gölge yazılabilir; ilk yazılan en üstte çizilir. Katmanlı
gölgeler, tek bir gölgeden daha yumuşak bir derinlik verir.

`inset` anahtar sözcüğü gölgeyi kutunun içine çevirir.

`box-shadow` ile `filter: drop-shadow()` arasında bir ayrım vardır: birincisi kenarlık
kutusunun dikdörtgenini (ve yarıçapını) izler, ikincisi ögenin **görünen biçimini** izler
ve saydam bölgeleri atlar. Saydamlığı olan bir simge için ikincisi doğru araçtır.

#### Şekil Vermek

`clip-path` kutunun çizilecek bölgesini bir geometrik şekle sınırlar:

```css
.simge { clip-path: circle(50%); }
.etiket { clip-path: polygon(0 0, 100% 0, calc(100% - 12px) 100%, 0 100%); }
```

Kırpma yerleşimi etkilemez: kutu yine dikdörtgen ölçüsünde yer kaplar, yalnızca çizimi
kırpılır. Kaydırılmış bir kutunun **çevresinden akan metnin** sınırını değiştirmek için
`shape-outside` gerekir; iki özellik birlikte yazıldığında biçim ve akış sınırı örtüşür.

Kırpma, kutunun dışına düşen her şeyi keser — odak çerçevesi de dâhil. Kırpılmış bir
denetimin odak göstergesi görünmez kalabilir; bu durumda gösterge kutunun içine alınmalıdır.

```css
/* istasyon.css — 23. adım: kenarlık, köşe ve gölge */
.kart {
  border: 1px solid var(--cizgi);
  border-radius: 12px;
  padding: 16px;
  box-shadow:
    0 1px 2px rgb(28 39 51 / 0.06),
    0 8px 24px -4px rgb(28 39 51 / 0.08);
}

.kart .alan { border-radius: 4px; }

.durum-rozeti {
  border-radius: 999px;
  border: 1px solid currentcolor;
  padding-block: 2px;
  padding-inline: 8px;
}

.olcum-cizelgesi { border-collapse: collapse; }

a:focus-visible {
  outline: 3px solid var(--marka-koyu);
  outline-offset: 2px;
  border-radius: 2px;
}
```

Kartta iki gölge katmanı var: yakın ve keskin olan kenarı belirginleştirir, uzak ve yumuşak
olan derinlik verir. İkincisinde negatif yayılma, gölgenin yanlara taşmasını sınırlar.

Rozetin kenarlık rengi `currentcolor` — metin rengi neyse kenarlık da o olur. Uyarı rozeti
kırmızı metin taşıdığında kenarlığı da kırmızı olur; ayrı bir kural yazılmaz.

`border-collapse: collapse` bildirimi tablo hücrelerinin komşu kenarlıklarını tek bir çizgi
hâline getirir. Bu kip, tablo köşelerine yarıçap uygulanmasını da engeller; yuvarlak köşeli
bir çizelge isteniyorsa çizelgenin bir sarmalayıcıya alınması ve yarıçapın orada verilmesi
gerekir.

#### Özet

- `border-style` yazılmadığında kenarlık çizilmez; kalınlık ve renk yazılmış bir kenarlığın
  görünmemesinin en sık sebebi budur.
- Bir kenardaki iki köşenin yarıçapları toplamı kenarı aşarsa bütün yarıçaplar aynı çarpanla
  küçültülür; `border-radius: 999px` yazımı bu kurala dayanarak hap biçimi üretir.
- İç köşe yarıçapı dış yarıçaptan kenarlık kalınlığı kadar küçüktür ve fark sıfırın altına
  inince iç kenar köşeli olur; iç içe kutularda yarıçap iç boşluk kadar azaltılır.
- `box-shadow` gölgesi bulanıklığın yarısı kadar taşar, yayılma değeri gölge dikdörtgenini
  doğrudan büyütür ya da küçültür; negatif yayılma taşmayı sınırlar.
- `clip-path` yalnızca çizimi kırpar, yerleşimi ve kapladığı yeri değiştirmez; odak
  göstergesi de kırpılabileceği için gösterge kutunun içine alınmalıdır.

#### Sonraki Adım

Bu konu boyunca uzunluklar hep piksel ve `rem` olarak yazıldı, `ch` birimi tanımsız
bırakıldı. Bir uzunluk kaç ayrı biçimde yazılabilir ve `em` ile `rem` arasındaki fark
neden zincirleme bir hesap doğurur? Sonraki ders CSS birimlerini tanımlar ve dönüşümleri
hesapla gösterir.

### CSS Birimleri

Source: https://academia.sh/tr/kurslar/css-temelleri/css-birimleri

Mutlak, yazı tipine göreli ve görüntü alanına göreli birimler; em zincirinin hesabı, rem sabitliği ve yüzdelerin hangi ölçüye başvurduğu.

Bu konu boyunca uzunluklar piksel ve `rem` olarak yazıldı, `ch` birimi tanımsız bırakıldı.
Bir uzunluk birkaç ayrı biçimde yazılabilir ve seçim yalnızca bir sayı biçimi değildir:
bazı birimler kullanıcı ayarlarını izler, bazıları izlemez.

Bu ders birimleri üç aileye ayırır ve aralarındaki dönüşümleri hesapla gösterir.

#### Üç Aile

**Mutlak birimler** — `px`, `cm`, `mm`, `in`, `pt`, `pc`, `q`. Aralarındaki oranlar sabittir
ve tanım gereği `1in = 96px` kabul edilir. `px` bir aygıt pikseline karşılık gelmez; bir
başvuru birimidir ve aygıt piksel oranı ne olursa olsun aynı fiziksel büyüklüğe yakın durur.

**Yazı tipine göreli birimler** — `em`, `rem`, `ch`, `ex`, `cap`, `lh`, `rlh`. Bunların
değeri hesaplandıkları bağlamdaki yazı ölçülerine bağlıdır.

**Görüntü alanına göreli birimler** — `vw`, `vh`, `vmin`, `vmax` ve bunların dinamik
biçimleri. Görüntü alanının yüzde biri olarak tanımlıdır.

```js
// birim.mjs — em zinciri, rem sabitligi ve yuzde referanslari
console.log("--- em zinciri: her kusakta yeniden carpilir ---");
const zincir = [
  { ad: "html",    fontSize: "16px" },
  { ad: "body",    fontSize: "1em"  },
  { ad: "section", fontSize: "1.25em" },
  { ad: "div",     fontSize: "1.25em" },
  { ad: "p",       fontSize: "1.25em" },
  { ad: "span",    fontSize: "1.25em" },
];
let ustPx = 16;
for (const o of zincir) {
  let px;
  if (o.fontSize.endsWith("px")) px = parseFloat(o.fontSize);
  else px = ustPx * parseFloat(o.fontSize);
  console.log(`${o.ad.padEnd(8)} font-size: ${o.fontSize.padEnd(7)} -> ${px.toFixed(3)}px  (ust ${ustPx.toFixed(3)}px)`);
  ustPx = px;
}

console.log("\n--- ayni zincir rem ile ---");
const KOK = 16;
ustPx = 16;
for (const o of zincir) {
  const px = o.fontSize.endsWith("px") ? parseFloat(o.fontSize) : KOK * parseFloat(o.fontSize);
  console.log(`${o.ad.padEnd(8)} font-size: ${o.fontSize.replace("em","rem").padEnd(8)} -> ${px.toFixed(3)}px`);
}

console.log("\n--- ayni ogede em: font-size dis, padding ic degeri kullanir ---");
const ustFont = 16, kendiFont = 1.5 * ustFont;
console.log(`ust font-size = ${ustFont}px`);
console.log(`font-size: 1.5em -> ${kendiFont}px   (ust deger uzerinden)`);
console.log(`padding:   1.5em -> ${1.5 * kendiFont}px   (kendi font-size'i uzerinden)`);
console.log(`line-height: 1.5em -> ${1.5 * kendiFont}px`);

console.log("\n--- yuzde neye gore cozulur ---");
const KAPSAYAN = { en: 640, boy: 400, fontSize: 16 };
const REF = {
  "width":       ["kapsayan en", KAPSAYAN.en],
  "padding-top": ["kapsayan EN", KAPSAYAN.en],
  "margin-left": ["kapsayan en", KAPSAYAN.en],
  "height":      ["kapsayan boy", KAPSAYAN.boy],
  "font-size":   ["ust font-size", KAPSAYAN.fontSize],
  "line-height": ["kendi font-size", 16],
};
for (const [oz, [aciklama, taban]] of Object.entries(REF)) {
  console.log(`${oz.padEnd(12)} %50 -> ${(taban * 0.5).toFixed(1)}px   (referans: ${aciklama} = ${taban})`);
}

console.log("\n--- mutlak birim donusumleri (1in = 96px) ---");
const PX = { px: 1, in: 96, cm: 96/2.54, mm: 96/25.4, q: 96/101.6, pt: 96/72, pc: 16 };
for (const [b, v] of Object.entries(PX)) console.log(`1${b.padEnd(3)} = ${v.toFixed(4)}px`);

console.log("\n--- ch ve ex (ornek bir yazi tipinde) ---");
const fs = 16, chOran = 0.5, exOran = 0.52;
console.log(`font-size=${fs}px, 1ch=${fs*chOran}px, 1ex=${(fs*exOran).toFixed(2)}px`);
console.log(`max-width: 70ch -> ${70 * fs * chOran}px`);
```

```
--- em zinciri: her kusakta yeniden carpilir ---
html     font-size: 16px    -> 16.000px  (ust 16.000px)
body     font-size: 1em     -> 16.000px  (ust 16.000px)
section  font-size: 1.25em  -> 20.000px  (ust 16.000px)
div      font-size: 1.25em  -> 25.000px  (ust 20.000px)
p        font-size: 1.25em  -> 31.250px  (ust 25.000px)
span     font-size: 1.25em  -> 39.063px  (ust 31.250px)

--- ayni zincir rem ile ---
html     font-size: 16px     -> 16.000px
body     font-size: 1rem     -> 16.000px
section  font-size: 1.25rem  -> 20.000px
div      font-size: 1.25rem  -> 20.000px
p        font-size: 1.25rem  -> 20.000px
span     font-size: 1.25rem  -> 20.000px

--- ayni ogede em: font-size dis, padding ic degeri kullanir ---
ust font-size = 16px
font-size: 1.5em -> 24px   (ust deger uzerinden)
padding:   1.5em -> 36px   (kendi font-size'i uzerinden)
line-height: 1.5em -> 36px

--- yuzde neye gore cozulur ---
width        %50 -> 320.0px   (referans: kapsayan en = 640)
padding-top  %50 -> 320.0px   (referans: kapsayan EN = 640)
margin-left  %50 -> 320.0px   (referans: kapsayan en = 640)
height       %50 -> 200.0px   (referans: kapsayan boy = 400)
font-size    %50 -> 8.0px   (referans: ust font-size = 16)
line-height  %50 -> 8.0px   (referans: kendi font-size = 16)

--- mutlak birim donusumleri (1in = 96px) ---
1px  = 1.0000px
1in  = 96.0000px
1cm  = 37.7953px
1mm  = 3.7795px
1q   = 0.9449px
1pt  = 1.3333px
1pc  = 16.0000px

--- ch ve ex (ornek bir yazi tipinde) ---
font-size=16px, 1ch=8px, 1ex=8.32px
max-width: 70ch -> 560px
```

#### em Zinciri

İlk iki blok bu dersin merkezindeki farkı gösteriyor. Aynı ağaçta, her kuşakta
`font-size: 1.25` bildirimi yazıldı. `em` ile yazıldığında değer her kuşakta **bir öncekinin
üzerine** çarpıldı ve beşinci kuşakta 39 pikseli aştı. `rem` ile yazıldığında her kuşak kök
ögenin boyutunu kullandı ve değer 20 pikselde sabit kaldı.

`em` — ögenin **kendi hesaplanmış** `font-size` değerine bağlıdır. `font-size` özelliğinde
kullanıldığında, ögenin kendi değeri henüz hesaplanmadığı için **üst ögenin** değeri
kullanılır. Zincirleme büyüme buradan doğar.

`rem` — her zaman **kök ögenin** `font-size` değerine bağlıdır. Zincir yoktur.

Üçüncü blok aynı öge içindeki ayrımı gösteriyor. `font-size: 1.5em` üst ögenin 16 pikseli
üzerinden 24 piksel verdi. Aynı ögede `padding: 1.5em` ise ögenin **kendi** 24 pikseli
üzerinden 36 piksel verdi. İki bildirim aynı sayıyı taşıyor ama farklı tabanlara başvuruyor.

`em` biriminin bu davranışı bir kusur değil, bileşen ölçeklemesinin aracıdır: bir düğmenin
iç boşluğu `em` ile yazıldığında, düğmenin yazı boyutu değiştiğinde iç boşluk da onunla
birlikte ölçeklenir. `rem` ile yazılsaydı sabit kalırdı.

#### Yüzdelerin Referansı Özelliğe Göre Değişir

Dördüncü blok, sık yapılan bir varsayımı yıkıyor. Bir yüzde her zaman aynı ölçüye
başvurmaz.

`width`, `margin` ve — dikkat — **dikey iç boşluk da dâhil olmak üzere** bütün `padding`
değerleri kapsayan bloğun **satır içi ölçüsüne** başvurur. `padding-top: 50%` yazımı
kapsayanın yüksekliğinin değil, genişliğinin yarısını verir.

Bu tuhaf görünen kural bir kalıbın temelidir: bir kutuya en–boy oranı vermek. `padding-top`
genişliğe bağlı olduğu için, yüksekliği genişliğe oranlı bir kutu bu yolla kurulabilir.
`aspect-ratio` özelliği aynı işi doğrudan yapar ve bugün tercih edilen yol odur.

`height` kapsayanın yüksekliğine, `font-size` üst ögenin yazı boyutuna, `line-height`
ögenin **kendi** yazı boyutuna başvurur.

#### Piksel Yazmanın Bedeli

Beşinci blok mutlak birimlerin sabit oranlarını veriyor. Bu oranlar hesaplanabilir ama
kararın asıl boyutu başka yerdedir.

Kullanıcı, tarayıcısında varsayılan yazı boyutunu değiştirebilir. Bu ayar kök ögenin
`font-size` değerini etkiler. Sonuç şudur:

- `rem` ve `em` ile yazılan ölçüler bu ayarla **birlikte** büyür.
- `px` ile yazılan ölçüler **değişmez**.

Yazı boyutu piksel olarak yazılmış bir sayfa, kullanıcının okuma ayarını görmezden gelir.
Yakınlaştırma yine işler, ama yakınlaştırma bütün sayfayı büyütür — kullanıcının istediği,
genellikle yalnızca metnin büyümesidir.

Uygulanabilir kural: **yazı boyutu, aralık ve kutu ölçüleri `rem` ya da `em` ile yazılır.**
`px` yalnızca gerçekten sabit kalması gereken ölçüler için kullanılır: ince kenarlıklar,
bir piksellik ayırıcılar, gölge ofsetleri.

Kök ögenin `font-size` değerine sabit bir piksel yazmak aynı sorunu geri getirir; bu değer
ya hiç yazılmaz ya da yüzdeyle yazılır.

#### Yazı Tipine Bağlı Diğer Birimler

Son blok `ch` birimini tanımlıyor: bir yazı tipinde "0" karakterinin genişliği. Kutu
boyutlandırma dersinde yazılan `max-width: 70ch` bildirimi burada karşılığını buluyor —
örnekteki oranla 560 piksel.

`ex` küçük harf yüksekliğine, `cap` büyük harf yüksekliğine, `lh` ögenin satır yüksekliğine
karşılık gelir. Bunların değeri seçilen yazı tipine bağlıdır; örnekteki 0,5 ve 0,52
oranları örnek değerlerdir.

`lh` birimi aralık ölçeği kurmakta kullanışlıdır: `margin-block-end: 1lh` yazmak, boşluğun
tam bir satır yüksekliği kadar olmasını sağlar ve metnin taban çizgisi düzeni korunur.

#### Görüntü Alanı Birimleri

`1vw` görüntü alanı genişliğinin yüzde biri, `1vh` yüksekliğinin yüzde biridir. `vmin` ve
`vmax` ikisinden küçüğünü ve büyüğünü kullanır.

Yükseklik birimlerinde bir ayrım vardır. Bazı ortamlarda tarayıcı arayüzü kaydırmayla
gizlenip görünür ve görüntü alanının yüksekliği değişir. Bunun için üç aile tanımlıdır:
`svh` en küçük, `lvh` en büyük, `dvh` ise o anki yüksekliği kullanır. `100vh` yazmak arayüz
görünürken taşan bir kutu üretebilir; `100svh` bu durumu önler.

Yazı boyutunu doğrudan `vw` ile yazmak sakıncalıdır: kullanıcı yakınlaştırma yaptığında
görüntü alanı genişliği yakınlaştırmayla ölçüldüğü için metin büyümez. Ölçeklenen tipografi
için görüntü alanı birimi tek başına değil, bir alt sınırla birlikte kullanılır — bunun
aracı bir sonraki derste ele alınacak.

```css
/* istasyon.css — 24. adım: birim seçimi */
:root { font-size: 100%; }          /* kullanıcı ayarını koru */

main { max-width: 70ch; padding-inline: 1rem; }

h1 { font-size: 2.4414rem; margin-block-end: 0.5em; }

.durum-rozeti { padding-block: 0.25em; padding-inline: 0.75em; }

.olcum-cizelgesi th,
.olcum-cizelgesi td {
  padding-block: 0.5rem;
  padding-inline: 0.75rem;
  border-block-end: 1px solid var(--cizgi);
}

.kart { box-shadow: 0 1px 2px rgb(28 39 51 / 0.06); }
```

Rozetin iç boşluğu `em` ile yazıldı: rozet küçük bir yazı boyutunda kullanıldığında iç
boşluk da küçülür. Hücre iç boşluğu `rem` ile yazıldı: çizelgenin ritmi hücre içeriğinin
boyutundan bağımsız kalmalıdır.

Başlığın alt boşluğu `em` ile yazıldı ve başlığın kendi boyutuna bağlandı; büyük başlıklar
büyük boşluk alır.

Kenarlık ve gölge ofseti `px` olarak kaldı: bunlar ölçeklendiğinde bulanıklaşan, sabit
kalması istenen ölçülerdir.

#### Özet

- `em`, ögenin kendi hesaplanmış yazı boyutuna bağlıdır; `font-size` özelliğinde
  kullanıldığında üst ögenin değerine başvurur ve her kuşakta yeniden çarpılarak zincirleme
  büyür.
- `rem` her zaman kök ögenin yazı boyutuna bağlıdır; zincir yoktur ve değer ağacın
  derinliğinden etkilenmez.
- Bir yüzdenin referansı özelliğe göre değişir: `width` ve **bütün** `padding` değerleri
  kapsayanın satır içi ölçüsüne, `height` kapsayanın blok ölçüsüne, `line-height` ögenin
  kendi yazı boyutuna başvurur.
- `px` ile yazılan ölçüler kullanıcının yazı boyutu ayarıyla büyümez; yazı, aralık ve kutu
  ölçüleri `rem` ya da `em` ile yazılır, `px` sabit kalması gereken ince ayrıntılara ayrılır.
- Görüntü alanı yükseklik birimlerinin üç ailesi vardır; `svh` en küçük, `lvh` en büyük,
  `dvh` o anki yüksekliği kullanır.

#### Sonraki Adım

Birimler tanımlandı ama hâlâ tek tek sabit değerler yazılıyor. Bir ölçünün iki değer
arasında kalması, iki ölçüden küçüğünün seçilmesi ya da bir değerin başka bir değerden
çıkarılması gerekebilir. Sonraki ders — kursun son dersi — bu hesapları yapan fonksiyonları
tanımlar ve kursun kurduğu biçem dosyasını toparlar.

### CSS Fonksiyonları

Source: https://academia.sh/tr/kurslar/css-temelleri/css-fonksiyonlari

calc ile birim karışımı, min ve max ile sınır koyma, clamp ile aralık kelepçeleme ve bu fonksiyonların ölçek üretiminde birleştirilmesi.

Birimler tanımlandı ama hâlâ tek tek sabit değerler yazılıyor. Biçem dosyasında ölçüler
elle hesaplanıp yazılmış durumda: `max-width: 70ch`, `padding-inline: 1rem`,
`font-size: 2.4414rem`. Bu değerlerden bazıları bir başkasına bağlıdır ve bağ dosyada
görünmez.

Bu ders hesabı biçem dosyasının içine taşıyan fonksiyonları tanımlar. Kursun son dersidir;
sonunda kurs boyunca kurulan biçem dosyası toparlanır.

#### Dört Fonksiyon

`calc()` — dört işlemle bir uzunluk hesaplar. Farklı birimler karıştırılabilir; çözüm,
değerlerin bilindiği aşamada yapılır.

`min()` ve `max()` — virgülle ayrılmış değerlerden en küçüğünü ya da en büyüğünü seçer.
İkiden çok değer alabilirler.

`clamp()` — üç değer alır: en küçük, tercih edilen, en büyük. Tercih edilen değeri iki sınır
arasına kelepçeler.

```js
// fonksiyon.mjs — calc, min, max ve clamp degerlerinin cozulmesi
const px = (v) => +v.toFixed(2);

// clamp(en_kucuk, tercih, en_buyuk) = max(en_kucuk, min(tercih, en_buyuk))
const clamp = (k, t, b) => Math.max(k, Math.min(t, b));

console.log("--- clamp: gorus alani genisligine gore yazi boyutu ---");
console.log("font-size: clamp(1rem, 0.75rem + 1.2vw, 1.5rem)   (kok 16px)");
for (const vw of [320, 480, 640, 800, 1024, 1440]) {
  const tercih = 0.75 * 16 + 1.2 * vw / 100;
  const sonuc = clamp(1 * 16, tercih, 1.5 * 16);
  const hangisi = sonuc === 16 ? "alt sinir" : sonuc === 24 ? "ust sinir" : "tercih";
  console.log(`gorus alani ${String(vw).padStart(4)}px -> tercih ${px(tercih)}px, uygulanan ${px(sonuc)}px  (${hangisi})`);
}

console.log("\n--- calc: farkli birimlerin karisimi ---");
const KAPSAYAN = 640, KOK = 16;
const ifadeler = [
  ["100% - 2rem",      KAPSAYAN - 2 * KOK],
  ["100% / 3",         KAPSAYAN / 3],
  ["100% / 3 - 16px",  KAPSAYAN / 3 - 16],
  ["50% + 20px",       KAPSAYAN * 0.5 + 20],
  ["2rem * 1.5",       2 * KOK * 1.5],
];
for (const [ifade, sonuc] of ifadeler) {
  console.log(`calc(${ifade})`.padEnd(24) + `-> ${px(sonuc)}px   (kapsayan ${KAPSAYAN}px, kok ${KOK}px)`);
}

console.log("\n--- min ve max: hangisi sinir koyar ---");
for (const kapsayan of [320, 640, 1200]) {
  const minSonuc = Math.min(kapsayan * 0.9, 600);
  const maxSonuc = Math.max(kapsayan * 0.9, 600);
  console.log(`kapsayan ${String(kapsayan).padStart(4)}px -> min(90%, 600px)=${px(minSonuc)}  max(90%, 600px)=${px(maxSonuc)}`);
}

console.log("\n--- clamp'in min/max ile esdegerligi ---");
for (const t of [10, 16, 20, 24, 30]) {
  const a = clamp(16, t, 24);
  const b = Math.max(16, Math.min(t, 24));
  console.log(`tercih=${String(t).padStart(2)} -> clamp=${a}  max(16,min(t,24))=${b}  esit=${a === b}`);
}

console.log("\n--- ic ice: aralik olcegi ---");
const TABAN = 16;
for (const n of [0, 1, 2, 3, 4]) {
  const deger = TABAN * Math.pow(1.5, n);
  console.log(`--aralik-${n}: ${px(deger / KOK)}rem = ${px(deger)}px`);
}
```

```
--- clamp: gorus alani genisligine gore yazi boyutu ---
font-size: clamp(1rem, 0.75rem + 1.2vw, 1.5rem)   (kok 16px)
gorus alani  320px -> tercih 15.84px, uygulanan 16px  (alt sinir)
gorus alani  480px -> tercih 17.76px, uygulanan 17.76px  (tercih)
gorus alani  640px -> tercih 19.68px, uygulanan 19.68px  (tercih)
gorus alani  800px -> tercih 21.6px, uygulanan 21.6px  (tercih)
gorus alani 1024px -> tercih 24.29px, uygulanan 24px  (ust sinir)
gorus alani 1440px -> tercih 29.28px, uygulanan 24px  (ust sinir)

--- calc: farkli birimlerin karisimi ---
calc(100% - 2rem)       -> 608px   (kapsayan 640px, kok 16px)
calc(100% / 3)          -> 213.33px   (kapsayan 640px, kok 16px)
calc(100% / 3 - 16px)   -> 197.33px   (kapsayan 640px, kok 16px)
calc(50% + 20px)        -> 340px   (kapsayan 640px, kok 16px)
calc(2rem * 1.5)        -> 48px   (kapsayan 640px, kok 16px)

--- min ve max: hangisi sinir koyar ---
kapsayan  320px -> min(90%, 600px)=288  max(90%, 600px)=600
kapsayan  640px -> min(90%, 600px)=576  max(90%, 600px)=600
kapsayan 1200px -> min(90%, 600px)=600  max(90%, 600px)=1080

--- clamp'in min/max ile esdegerligi ---
tercih=10 -> clamp=16  max(16,min(t,24))=16  esit=true
tercih=16 -> clamp=16  max(16,min(t,24))=16  esit=true
tercih=20 -> clamp=20  max(16,min(t,24))=20  esit=true
tercih=24 -> clamp=24  max(16,min(t,24))=24  esit=true
tercih=30 -> clamp=24  max(16,min(t,24))=24  esit=true

--- ic ice: aralik olcegi ---
--aralik-0: 1rem = 16px
--aralik-1: 1.5rem = 24px
--aralik-2: 2.25rem = 36px
--aralik-3: 3.38rem = 54px
--aralik-4: 5.06rem = 81px
```

#### clamp Üç Bölgeli Bir Fonksiyondur

İlk blok, `clamp()` fonksiyonunun üç bölgesini gösteriyor. 320 birimlik bir görüntü alanında
tercih edilen değer 15,84 piksel hesaplandı ama alt sınırın altında kaldığı için 16 piksel
uygulandı. 480'den 800'e kadar tercih edilen değer aralıkta kaldı ve doğrudan kullanıldı.
1024'ten sonra üst sınıra dayandı.

Dördüncü blok eşdeğerliği doğruluyor: `clamp(a, t, b)` her zaman `max(a, min(t, b))`
sonucunu verir. `clamp()` ayrı bir mekanizma değil, bu bileşimin adıdır.

Tercih edilen değerdeki `0.75rem + 1.2vw` yazımı bir kalıptır: sabit bir taban ile görüntü
alanına oranlı bir bileşenin toplamı. Sabit bileşen olmadan yalnızca `vw` yazılsaydı,
kullanıcı yakınlaştırma yaptığında metin büyümezdi — bir önceki derste anılan sorun. Sabit
bileşen bu bağı korur.

#### calc Birim Karıştırır

İkinci blok `calc()` fonksiyonunun asıl işini gösteriyor: aynı ifadede yüzde, `rem` ve `px`
karıştırılabiliyor. Bu, başka türlü yazılamayacak ilişkileri kurar.

`calc(100% - 2rem)` — kapsayanı doldur ama iki taban birim boşluk bırak.
`calc(100% / 3 - 16px)` — üçte bir genişlik al ve aradaki boşluğu düş.

Yazım kurallarında bir ayrıntı vardır: `+` ve `-` işleçlerinin **iki yanında da boşluk
zorunludur**. `calc(100%-2rem)` yazımı geçersizdir, çünkü `-2rem` negatif bir sayı olarak
okunur. `*` ve `/` için boşluk zorunlu değildir, ama tutarlılık için yazılır.

İkinci kısıt: çarpmada işlenenlerden en az biri, bölmede bölen birimsiz bir sayı olmalıdır.
İki uzunluk çarpılamaz.

`calc()` iç içe yazılabilir ve `var()` çağrılarını da kapsayabilir. Özel özelliklerle
birlikte kullanıldığında bir ölçeğin tek bir tabandan türetilmesini sağlar — beşinci blok
bunun sonucunu veriyor.

#### min ve max Sınır Koyar

Üçüncü blok bir okuma alışkanlığını düzeltiyor. `min()` bir **üst sınır** koyar: verilen
değerlerin en küçüğü seçilir, dolayısıyla sonuç hiçbirini aşamaz. `max()` bir **alt sınır**
koyar.

Adlarla işlevlerin ters gibi görünmesi sık karıştırılan bir noktadır. Çıktıda
`min(90%, 600px)` yazımı, kapsayan 1200 birim olduğunda 600'de durdu — yani bir üst sınır
işlevi gördü.

Bu iki fonksiyonun bir üstünlüğü vardır: `min-width` ve `max-width` gibi ayrı özellikler
yazmadan, aynı bildirimin içinde sınır koyarlar. Ayrıca kısayol değerlerinin içinde de
kullanılabilirler — `padding: min(5%, 2rem)` yazılabilir, oysa `padding` için bir `max-`
özelliği yoktur.

#### Kursun Biçem Dosyası

Kurs boyunca istasyon sayfasının biçem dosyası adım adım büyüdü. Toparlanmış hâli, bu
kursta kurulan bütün kararları taşır:

```css
/* istasyon.css — toparlanmış hâl */

/* 1. Taban */
*, *::before, *::after { box-sizing: border-box; }
:root { font-size: 100%; }

/* 2. Ölçekler */
:root {
  --ton: 203;
  --marka-koyu: hsl(var(--ton) 61% 20%);
  --zemin:      hsl(var(--ton) 18% 97%);
  --cizgi:      hsl(207 15% 86%);
  --metin:      hsl(211 29% 16%);
  --metin-soluk: hsl(211 12% 45%);
  --uyari:      hsl(0 66% 33%);

  --aralik-0: 1rem;
  --aralik-1: calc(var(--aralik-0) * 1.5);
  --aralik-2: calc(var(--aralik-1) * 1.5);
  --aralik-3: calc(var(--aralik-2) * 1.5);

  --yazi-govde: clamp(1rem, 0.9rem + 0.35vw, 1.125rem);
  --yazi-h1:    clamp(1.75rem, 1.3rem + 2.2vw, 2.5rem);
}

/* 3. Belge */
body {
  color: var(--metin);
  font-family: "Istasyon Sans", system-ui, sans-serif;
  font-size: var(--yazi-govde);
  line-height: 1.5;
}

main {
  max-width: min(70ch, 100% - var(--aralik-1));
  margin-inline: auto;
  padding-block: var(--aralik-2);
}

main > * + *       { margin-block-start: var(--aralik-0); }
main > section     { margin-block-start: var(--aralik-3); }

/* 4. Künye */
.kunye {
  padding: var(--aralik-1);
  margin-block-end: var(--aralik-2);
  background-color: var(--zemin);
  border-block-end: 4px solid var(--marka-koyu);
}
.kunye > :first-child { margin-block-start: 0; }
h1 { font-size: var(--yazi-h1); line-height: 1.15; }

/* 5. Ölçüm çizelgesi */
.cizelge-sarmalayici { overflow-x: auto; }
.olcum-cizelgesi { width: 100%; min-width: 40rem; border-collapse: collapse; }
.olcum-cizelgesi th,
.olcum-cizelgesi td {
  padding-block: 0.5rem;
  padding-inline: 0.75rem;
  border-block-end: 1px solid var(--cizgi);
}
.olcum-cizelgesi thead th {
  position: sticky;
  z-index: 10;
  inset-block-start: 0;
  background-color: var(--zemin);
  text-transform: uppercase;
  letter-spacing: 0.04em;
}
:where(.olcum-cizelgesi) .deger {
  text-align: end;
  font-variant-numeric: tabular-nums;
}
:where(.olcum-cizelgesi) .eksik { color: var(--uyari); }
.olcum-cizelgesi tbody tr:nth-child(odd) { background-color: var(--zemin); }

/* 6. Konum bölümü */
.konum-bolumu { display: flow-root; }
.konum-gorseli {
  float: inline-start;
  inline-size: min(200px, 40%);
  margin-inline-end: var(--aralik-0);
  margin-block-end: 0.5rem;
}
.konum-gorseli img { max-width: 100%; height: auto; }
.kaynak-notu { clear: both; color: var(--metin-soluk); font-size: 0.875em; }

/* 7. Durumlar */
a:focus-visible,
input:focus-visible {
  outline: 3px solid var(--marka-koyu);
  outline-offset: 2px;
}
input, select, textarea, button { font: inherit; color: inherit; }
input[required] { border-inline-start: 3px solid var(--uyari); }
input:invalid   { border-color: var(--uyari); }

.gorsel-gizli {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}
```

Dosya yedi bölüme ayrılmış durumda ve sıra rastgele değil: taban kuralları en üstte, en
düşük özgüllükle; bileşen kuralları altta. Basamaklama dersinde kurulan sıra ölçütü, bu
düzenin öngörülebilir olmasını sağlar — aynı özgüllükteki bir çakışma her zaman sonra
yazılan lehine çözülür.

Ölçekler tek bir yerde tanımlı ve birbirinden türetilmiş durumda. `--aralik-1` değeri
`--aralik-0` değerine bağlı; taban değiştiğinde bütün aralıklar birlikte değişir.

`main` ögesinin genişliği iki kısıtın küçüğü: okunabilir satır uzunluğu ve ekran kenarında
bırakılacak boşluk. Bu, kutu boyutlandırma, birim ve fonksiyon derslerinin birleşimidir.

#### Özet

- `calc()` farklı birimleri aynı ifadede karıştırır; toplama ve çıkarma işleçlerinin iki
  yanında boşluk zorunludur ve çarpmada en az bir işlenen birimsiz olmalıdır.
- `min()` bir üst sınır, `max()` bir alt sınır koyar; adlar işlevlerin tersi gibi görünse de
  seçilen değer sırasıyla en küçük ve en büyüktür.
- `clamp(a, t, b)`, `max(a, min(t, b))` bileşiminin adıdır ve üç bölgeli davranır: tercih
  edilen değer aralıktaysa kullanılır, dışındaysa ilgili sınıra kelepçelenir.
- Ölçeklenen tipografide tercih edilen değer sabit bir taban ile görüntü alanına oranlı bir
  bileşenin toplamı olarak yazılır; yalnızca görüntü alanı birimi yazmak yakınlaştırma
  bağını koparır.
- Fonksiyonlar özel özelliklerle birleştirildiğinde bir ölçek tek bir tabandan türetilir ve
  ölçeğin tamamı tek bir değerin değiştirilmesiyle kaydırılır.

#### Kurs Kapanışı

Bu kurs, Web'in Temelleri ve HTML kursunda kurulan ölçüm istasyonu belgesini biçimlendirdi
ve bunu üç katmanda yaptı.

Birinci konu **kuralların belgeye nasıl ulaştığını** kurdu: bir bildirimin sözdizimi,
seçicinin döndürdüğü küme, çakışmaların basamaklama ve özgüllükle çözümü, değerin kalıtımla
ya da başlangıç değeriyle doldurulması. Bu konunun ayırt edici yanı, sorulan her sorunun
hesaplanabilir bir yanıtı olmasıydı: özgüllük bir üçlü, basamaklama bir sıralama, kalıtım
bir ağaç yürüyüşüdür.

İkinci konu **kutuların nasıl ölçüldüğünü ve dizildiğini** kurdu: kutu modelinin
aritmetiği, iki boyutlandırma modelinin sayısal farkı, normal akışın kuralları, dış boşluk
çökmesi, kaydırma, konumlandırma ve yığılma bağlamı. Buradaki tekrarlanan ders şuydu: bir
yerleşim sorunu önce akışın kurallarıyla çözülmeye çalışılır; akıştan çıkaran her bildirim
bir bedelle gelir.

Üçüncü konu **görsel kararları ölçeğe bağladı**: renk uzayları ve kontrast hesabı, modüler
tipografi ölçeği, yazı tipi yükleme davranışı, arka plan katmanları, geçişler, kenarlıklar,
birimler ve hesap fonksiyonları. Buradaki ilke, tek tek değerler yerine türetilmiş ölçekler
kurmaktı.

Kursun kapsamadığı bir şey kaldı: **iki boyutlu düzen**. Bu kursta kutular ya akışta alt
alta dizildi ya da kaydırmayla bir kenara itildi. Sütunlar kurmak, kutuları bir ızgaraya
yerleştirmek, aralarındaki boşlukları çökmeden vermek ve bütün bunları ekran genişliğine
göre uyarlamak ayrı bir modelin işidir.

Düzen Sistemleri ve Duyarlı Tasarım kursu bu modeli kurar. Esnek kutu ve ızgara
düzenleriyle iki boyutlu yerleşim, içerikten türetilen kesme noktalarıyla duyarlı tasarım
ve biçem mimarisinin ölçeklenebilir biçimde düzenlenmesi orada ele alınır. Bu kursta
öğrenilen her şey orada da geçerlidir: özgüllük hesabı aynı hesaptır, kutu modeli aynı
modeldir, ölçekler aynı ölçeklerdir. Değişen tek şey, kutuların birbirine göre nasıl
yerleştirildiğidir.

