Ders 05 / 23
Izgara ve Esnek Kutu Seçimi
İki düzen modelinin hangi soruyu yanıtladığı, son satır davranışının hesapla gösterilen farkı, yayılma isteğinin esnek kutuda doğurduğu maliyet ve iki modelin iç içe kullanımı.
İçindekiler
Önceki dört ders iki düzen modelini ayrı ayrı kurdu. Esnek kutu tek eksende dağıtıyor, ızgara iki eksende hat tanımlıyor. İkisiyle de bir kart dizisi yan yana dizilebilir, ikisiyle de bir gezinme çubuğu kurulabilir; seçim çoğu zaman en son öğrenilen modele göre yapılır.
Bu ders seçimi alışkanlıktan çıkarıp ölçüte bağlar. Ölçüt tek bir sorudur: düzen kararını içerik mi veriyor, yoksa siz mi veriyorsunuz? Sorunun yanıtı iki modelin adında değil, hesaplarının çıktığı yerde saklıdır.
Ölçünün Nereden Geldiği
Esnek kutuda hesap ögeden başlar. Her ögenin bir temel boyu vardır; bu boylar toplanır, kapsayıcıyla farkı alınır ve kalan alan büyüme ile daralma çarpanlarına göre bölüştürülür. Kapsayıcı ögelerin ne istediğini sorar, sonra pazarlık yapar. Bu yüzden esnek kutuya içerikten düzene çalışan model denir.
Izgarada hesap kapsayıcıdan başlar. Şerit tanımı, hiçbir öge yerleştirilmeden önce
hatları belirler; ögeler sonra bu hatlara oturur. Şerit ölçüsü içerikten türetilebilir
(auto, min-content, max-content) ama bu bir seçenektir, kuralın kendisi değildir.
Izgara düzenden içeriğe çalışır.
Ayrım, iki modelin hangi soruya iyi yanıt verdiğini belirler:
- Ögelerin sayısı ve ölçüsü önceden bilinmiyorsa, kalan alanı onlar arasında paylaştırmak gerekiyorsa — soru tek eksenlidir ve esnek kutu daha az bildirimle çözer.
- Ögelerin birbirine göre hem satırda hem sütunda hizalanması gerekiyorsa, bir ögenin kapladığı alan diğerinin yerini belirliyorsa — soru iki eksenlidir ve ızgara gerekir.
“Tek eksenli” ifadesi ögelerin tek satırda kaldığı anlamına gelmez. flex-wrap: wrap
bildirilmiş bir esnek kapsayıcı da birden çok satır üretir. Ayrım şudur: esnek kutuda her
satır kendi içinde hesaplanır, satırlar birbirini görmez. Izgarada bütün satırlar aynı
hat kümesini paylaşır.
Son Satırın Davranışı
Bu ayrımın en somut sonucu, ögeler tam bölünmediğinde ortaya çıkar. Aşağıdaki program aynı yedi kartı aynı kapsayıcıda iki modelle yerleştirir.
// secim.mjs — ayni kart dizisinin esnek kutu ve izgarayla yerlesimi const KAPSAYICI = 980; // px const ARALIK = 16; // gap const TABAN = 200; // flex-basis / minmax alt siniri const OGE = 7; // iki modelde de bir satira sigan oge sayisi ayni kuralla bulunur const sigan = Math.max(1, Math.floor((KAPSAYICI + ARALIK) / (TABAN + ARALIK))); // izgara: repeat(auto-fill, minmax(200px, 1fr)) -> her serit ayni olcu const seritOlcu = (KAPSAYICI - ARALIK * (sigan - 1)) / sigan; // esnek kutu: flex: 1 1 200px + flex-wrap: wrap -> her satir kendi icinde paylasir function esnekSatirlar() { const satirlar = []; for (let i = 0; i < OGE; i += sigan) { const adet = Math.min(sigan, OGE - i); satirlar.push({ adet, olcu: (KAPSAYICI - ARALIK * (adet - 1)) / adet }); } return satirlar; } const yaz = (v) => v.toFixed(1).padStart(6); console.log(`kapsayici=${KAPSAYICI}px gap=${ARALIK}px taban=${TABAN}px oge=${OGE}`); console.log(`bir satira sigan oge: ${sigan}\n`); console.log("--- izgara: repeat(auto-fill, minmax(200px, 1fr)) ---"); let no = 1; for (let s = 0; s < Math.ceil(OGE / sigan); s++) { const adet = Math.min(sigan, OGE - s * sigan); let x = 0; const yerler = []; for (let i = 0; i < adet; i++) { yerler.push(`k${no++} ${yaz(x)}..${yaz(x + seritOlcu)}`); x += seritOlcu + ARALIK; } console.log(`satir ${s + 1}: ` + yerler.join(" ")); } console.log("\n--- esnek kutu: flex: 1 1 200px; flex-wrap: wrap ---"); no = 1; esnekSatirlar().forEach((satir, s) => { let x = 0; const yerler = []; for (let i = 0; i < satir.adet; i++) { yerler.push(`k${no++} ${yaz(x)}..${yaz(x + satir.olcu)}`); x += satir.olcu + ARALIK; } console.log(`satir ${s + 1}: ` + yerler.join(" ")); }); console.log("\n--- ilk sutunun sag kenari satirdan satira ---"); const ilkIzgara = seritOlcu; esnekSatirlar().forEach((satir, s) => { const fark = satir.olcu - ilkIzgara; console.log( `satir ${s + 1}: izgara ${yaz(ilkIzgara)} esnek ${yaz(satir.olcu)} fark ${yaz(fark)}`, ); });
kapsayici=980px gap=16px taban=200px oge=7 bir satira sigan oge: 4 --- izgara: repeat(auto-fill, minmax(200px, 1fr)) --- satir 1: k1 0.0.. 233.0 k2 249.0.. 482.0 k3 498.0.. 731.0 k4 747.0.. 980.0 satir 2: k5 0.0.. 233.0 k6 249.0.. 482.0 k7 498.0.. 731.0 --- esnek kutu: flex: 1 1 200px; flex-wrap: wrap --- satir 1: k1 0.0.. 233.0 k2 249.0.. 482.0 k3 498.0.. 731.0 k4 747.0.. 980.0 satir 2: k5 0.0.. 316.0 k6 332.0.. 648.0 k7 664.0.. 980.0 --- ilk sutunun sag kenari satirdan satira --- satir 1: izgara 233.0 esnek 233.0 fark 0.0 satir 2: izgara 233.0 esnek 316.0 fark 83.0
İlk satırlar birebir aynı. İki model de kapsayıcıya dört kartın sığdığını aynı aritmetikle buluyor ve dört kartı aynı yerlere koyuyor. Ayrım ikinci satırda başlıyor.
Izgarada ikinci satır aynı hatları kullanır. Üç kart kalmıştır ama şeritler dörttür; kartlar ilk üç şeride oturur, dördüncü şerit boş kalır. Kartların ölçüsü değişmez: 233 birim.
Esnek kutuda ikinci satır kendi hesabını yapar. Üç kart 980 birimi paylaşır ve her biri 316 birime çıkar. Son satır 83 birim daha geniş kartlarla dolar; birinci satırın kart kenarları ile ikinci satırınkiler hiçbir noktada çakışmaz.
Bu bir kusur değil, iki modelin tanımının sonucudur. Hangisinin doğru olduğu ne istendiğine bağlıdır. Kartlar bir çizelgenin görsel karşılığıysa ve sütun hizası okumayı taşıyorsa ızgara doğrudur. Kartlar birbirinden bağımsız etiketlerse ve boş yer bırakmak istenmiyorsa esnek kutunun davranışı istenen davranıştır.
Sık görülen bir düzeltme girişimi, son satıra “hayalet” ögeler eklemektir: sığmayan boşluğu doldurmak için görünmez kutular. Bu, esnek kutuya ızgara işi yaptırmanın belirtisidir; belgeye anlamı olmayan öge eklemek yerine model değiştirmek daha ucuzdur.
Yayılma İsteğinin Bedeli
İkinci ölçüt, bir ögenin diğerlerinden daha geniş olması istendiğinde ortaya çıkar. Izgara
bunu tek bildirimle yazar: grid-column: span 2. Esnek kutuda karşılığı bir orandır ve o
oran şerit sayısına bağlıdır.
// yayilma.mjs — iki serit genisligindeki bir kartin iki modeldeki bedeli const ARALIK = 16; const TABAN = 200; console.log("minmax(200px, 1fr), gap 16px — iki serit kaplayan kartin olcusu"); console.log("kapsayici serit serit olcu span 2 olcu ayni sonuc icin flex-basis"); for (const W of [640, 780, 980, 1180, 1420]) { const serit = Math.max(1, Math.floor((W + ARALIK) / (TABAN + ARALIK))); const olcu = (W - ARALIK * (serit - 1)) / serit; const span2 = 2 * olcu + ARALIK; // izgarada: grid-column: span 2 const yuzde = (span2 / W) * 100; // esnek kutuda elle yazilacak taban console.log( `${String(W).padStart(9)} ${String(serit).padStart(5)} ` + `${olcu.toFixed(1).padStart(10)} ${span2.toFixed(1).padStart(11)} ` + `${yuzde.toFixed(2).padStart(24)}%`, ); } console.log("\n--- ayni kart tek bir flex-basis degeriyle yazilirsa ---"); const SECILEN = 49.18; // 980px genisligine gore hesaplanan deger console.log(`flex: 0 0 ${SECILEN}% (980px icin dogru olan deger)`); console.log("kapsayici uygulanan olcu o genislikte dogru olan sapma"); for (const W of [640, 780, 980, 1180, 1420]) { const serit = Math.max(1, Math.floor((W + ARALIK) / (TABAN + ARALIK))); const olcu = (W - ARALIK * (serit - 1)) / serit; const dogru = 2 * olcu + ARALIK; const uygulanan = (W * SECILEN) / 100; const sapma = Math.round((uygulanan - dogru) * 10) / 10 || 0; console.log( `${String(W).padStart(9)} ${uygulanan.toFixed(1).padStart(14)} ` + `${dogru.toFixed(1).padStart(22)} ${sapma.toFixed(1).padStart(6)}`, ); }
minmax(200px, 1fr), gap 16px — iki serit kaplayan kartin olcusu
kapsayici serit serit olcu span 2 olcu ayni sonuc icin flex-basis
640 3 202.7 421.3 65.83%
780 3 249.3 514.7 65.98%
980 4 233.0 482.0 49.18%
1180 5 223.2 462.4 39.19%
1420 6 223.3 462.7 32.58%
--- ayni kart tek bir flex-basis degeriyle yazilirsa ---
flex: 0 0 49.18% (980px icin dogru olan deger)
kapsayici uygulanan olcu o genislikte dogru olan sapma
640 314.8 421.3 -106.6
780 383.6 514.7 -131.1
980 482.0 482.0 0.0
1180 580.3 462.4 117.9
1420 698.4 462.7 235.7
Birinci tablo, “iki şerit genişliğinde” ifadesinin sabit bir oran olmadığını gösteriyor. Şerit sayısı üçken oran yüzde 66’ya yakın, altıya çıktığında yüzde 33’e iniyor. Aynı görsel istek, kapsayıcı genişliğine göre beş farklı sayı demek.
İkinci tablo bedeli ölçüyor. Bir genişlik seçilip oran ona göre yazıldığında, o genişlikte sapma sıfırdır; diğer genişliklerde 100 ile 235 birim arasında değişir. Kart, dar kapsayıcıda olması gerekenden dar, geniş kapsayıcıda olması gerekenden geniştir. Doğru sonucu her genişlikte tutturmanın yolu, oranı genişlik aralıklarına göre yeniden yazmaktır — yani düzen kararını ızgaranın hesapladığı şeyi elle hesaplayarak taklit etmektir.
Izgarada span 2 bildirimi bu hesabın hiçbirini yazmaz; şerit ölçüsü ne olursa olsun iki
şerit ve aralarındaki bir aralık kadar yer kaplar.
Sıra, Yayılma ve Çakışma
Üçüncü ölçüt, ögelerin birbirini görüp görmediğidir.
Esnek kutuda ögeler tek boyutlu bir dizidir. Sıra order ile değiştirilebilir ama bir öge
başka bir ögenin üstüne gelemez, iki satırı birden kaplayamaz. Izgarada ögeler hücrelere
yerleşir; aynı hücreye iki öge yerleştirmek geçerlidir ve ikisi üst üste gelir. Bir görselin
üzerine yazı bindirmek, konumlandırma kullanmadan yalnız ızgarayla yazılabilir.
Bir başka ayrım da erişilebilirlik tarafında sürer. Izgara Yerleşimi dersinde görüldüğü gibi
görsel sıranın belge sırasından ayrılması odak sırasını bozar. Izgara bu ayrımı çok daha
kolay üretir: hat numarasıyla yerleştirme, belge sırasını tamamen yok sayar. Esnek kutuda aynı
riski yalnız order ve ters yönler doğurur. Modelin gücü, yanlış kullanıldığında aynı ölçüde
zarar verir.
İki Modelin İç İçe Kullanımı
Ölçütler karşılaştırma gibi görünse de seçim çoğu sayfada “ikisinden biri” değildir. Sayfanın büyük bölümleri iki eksenlidir; bir bölümün içindeki düğme ve etiket dizileri tek eksenlidir. Iç içe kullanım kuralın sonucudur.
/* duzen.css — 5. adım: iki modelin iş bölümü */ .istasyon-duzeni { /* iki eksenli: bölümler birbirine göre hizalanır */ display: grid; grid-template-columns: minmax(0, 2fr) minmax(0, 1fr); grid-template-areas: "kunye kunye" "olcumler yan-bilgi" "konum yan-bilgi"; gap: var(--aralik-1); } .kunye { grid-area: kunye; } .olcum-bolumu { grid-area: olcumler; } .konum-bolumu { grid-area: konum; } .yan-bilgi { grid-area: yan-bilgi; } .olcum-kartlari { /* iki eksenli: kart kenarları sütun sütun hizalanır */ display: grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); gap: var(--aralik-0); } .olcum-kartlari .ozet { grid-column: 1 / -1; } .kart-basligi { /* tek eksenli: başlık ve rozet aynı satırda */ display: flex; align-items: baseline; gap: 0.5rem; } .kart-basligi .birim { margin-inline-start: auto; }
Üç kapsayıcı, üç ayrı soru. Sayfa düzeni bölümleri hem satırda hem sütunda hizalar: ızgara. Kart dizisi, kartların kenarlarının satırlar arasında çakışmasını ister: ızgara. Kart başlığındaki iki öge yalnız bir doğrultuda dizilir ve aradaki boşluk içerikten artar: esnek kutu.
Bir sınır burada belirir. Kart başlığının içindeki .birim ögesi, komşu karttaki .birim
ögesiyle hizalanmaz; her kart kendi esnek hesabını yapar. Bu hizanın kurulması için iç
kapsayıcının dış ızgaranın hatlarını görmesi gerekir. subgrid değeri, iç içe bir ızgaranın
kendi şeritlerini tanımlamak yerine kapsayanın hatlarını devralmasını sağlar ve bu sorunu
çözer. Değerin tanınıp tanınmadığı bir özellik sorgusuyla sınanır; sorgu yazımı sonraki
konuda ele alınacaktır.
Karar Sırası
Seçim üç soruyla bitirilebilir ve sıraları önemlidir.
- Ögeler birbirine göre iki eksende hizalanacak mı? Yanıt evetse ızgara. Bu soru, kalan ikisini gereksiz kılacak kadar belirleyicidir.
- Ölçüyü içerik mi belirliyor? Ögelerin sayısı bilinmiyor, ölçüleri metnin uzunluğundan geliyor ve aralarındaki boşluk artan alandan dağıtılacaksa esnek kutu daha az bildirim ister.
- Bir öge diğerlerinden farklı bir alan mı kaplayacak? Yayılma, üst üste binme ya da belirli bir hücreye yerleştirme isteği varsa ızgara.
Ölçütlerin hiçbiri “ızgara yeni, esnek kutu eski” biçiminde bir sıralama içermez. İki model aynı düzen katmanının iki ayrı aracıdır ve birbirinin yerine geçmez.
Özet
- Esnek kutu hesabı ögeden başlatır ve her satırı kendi içinde çözer; ızgara hesabı kapsayıcıdan başlatır ve bütün satırlara aynı hat kümesini uygular.
- Ögeler tam bölünmediğinde iki model ayrışır: ızgarada son satırın ögeleri şerit ölçüsünü korur, esnek kutuda kalan ögeler satırı paylaşarak genişler.
- Bir ögenin iki şerit kaplaması ızgarada tek bildirimdir; esnek kutuda karşılığı şerit sayısıyla değişen bir orandır ve tek bir değerle her genişlikte doğru olmaz.
- Üst üste binme, belirli hücreye yerleştirme ve iki eksenli hizalama yalnız ızgarada tanımlıdır; sıra bozma riski de aynı ölçüde büyür.
- Sayfa düzeyinde ızgara, bileşen içinde esnek kutu yaygın bir iş bölümüdür; bunun nedeni alışkanlık değil, iki düzeyin farklı sayıda eksende karar vermesidir.
Sonraki Adım
Buraya kadar düzenin birimi hep bir kutu oldu: kart, bölüm, düğme. Kutular hatlara yerleştirildi ya da eksende dağıtıldı. Ama sayfadaki metnin kendisi de düzenlenebilir bir akıştır; uzun bir açıklama paragrafı geniş bir ekranda tek satır hâlinde uzadığında okunması zorlaşır. Sonraki ders metin akışını kutulara bölmeden sütunlara ayıran modeli ele alır: sütun sayısı ile sütun genişliği arasındaki ilişki nasıl kurulur ve içerik sütunlar arasında hangi kuralla dağılır?
İlerlemeni kaydetmek ve not almak için Giriş yap
Notlarım
Not almak için giriş yapmalısın.