İçeriğe geç
academia.sh

Ders 03 / 23

Izgara Modeli

Izgara bağlamının kurulması, şerit ile hat ayrımı, pay biriminin boş alanı bölmesi, en küçük içerik ölçüsünün pay üzerindeki gizli alt sınırı ve alanların adlandırılması.

İçindekiler

Esnek kutu tek eksende dağıtır: bir doğrultuda paylaştırır, diğerinde yalnızca hizalar. Bir süzgeç çubuğu için bu yeterlidir. Ölçüm sayfasının bütünü ise iki eksende hizalanmış bölgeler ister: künye, ölçüm çizelgesi ve yan sütun birbirinin hem satırına hem sütununa göre yerleşmelidir.

Bu ders, satır ve sütunları önceden tanımlayan düzen modelini kurar. Esnek kutuda yerleşim ögelerin ölçülerinden doğar; ızgarada önce boş bir çizgi düzeni tanımlanır ve ögeler bu düzene yerleştirilir.

Izgara Bağlamı

Bir kutuya display: grid bildirildiğinde kutu ızgara kapsayıcısı (grid container) olur; doğrudan çocukları ızgara ögesi (grid item) olarak yerleştirilir. Esnek kutudaki gibi kapsayıcının kendisi akışta blok kutu olarak kalır, çocukların display değeri bloklaşır ve dış boşlukları çökmez.

Ayrım, kapsayıcıya ne bildirildiğindedir. Esnek kutuda tek bir yön bildirilir; ızgarada iki eksenin şerit tanımı yazılır:

.istasyon-duzeni {
  display: grid;
  grid-template-columns: 240px 2fr 1fr;
  grid-template-rows: auto 1fr;
  gap: 24px;
}

Şerit, Hat ve Hücre

Dört kavram birbirinden ayrı tutulur.

Şerit (grid track) iki komşu çizgi arasındaki bant: bir sütun ya da bir satır. Yukarıdaki bildirim üç sütun şeridi ve iki satır şeridi tanımlar.

Hat (grid line) şeritleri ayıran çizgi. Üç sütun şeridi dört hat üretir; hatlar başlangıçtan itibaren 1’den numaralanır. Aynı hatlar sondan başlayarak eksi değerlerle de anılır: son hat 1-1, bir öncesi 2-2. Bir ögeyi ızgaranın sonuna kadar uzatmak için şerit sayısını bilmek gerekmez, 1-1 yeterlidir.

Hücre (grid cell) bir satır şeridi ile bir sütun şeridinin kesişimi; ızgaranın en küçük birimi.

Alan (grid area) dört hatla sınırlanan dikdörtgen bölge; bir ya da daha çok hücreyi kapsar. Bir ögenin yerleşimi her zaman bir alandır.

Hatlara ad verilebilir ve ad, köşeli ayraç içinde şerit tanımının arasına yazılır:

.istasyon-duzeni {
  display: grid;
  grid-template-columns: [kenar-bas] 240px [icerik-bas] 2fr 1fr [kenar-son];
}

Adlandırılmış hat, sayı yerine kullanılabilir ve şerit sayısı değiştiğinde yerleşim bildirimlerinin yeniden yazılmasını gereksiz kılar.

Pay Birimi Boş Alanı Böler

fr, ızgaraya özgü bir birimdir: pay birimi (fractional unit). Bir uzunluk değildir; sabit şeritler ve aralıklar düşüldükten sonra kalan alanın kaç paya bölüneceğini söyler.

// serit.mjs — fr biriminin bos alani boluse ve gizli alt siniri
const ARALIK = 24;

// serit tanimi: sabit uzunluklar once ayrilir, kalan fr paylarina bolunur
const seritler = [
  { yazim: "240px", sabit: 240, fr: 0 },
  { yazim: "2fr", sabit: 0, fr: 2 },
  { yazim: "1fr", sabit: 0, fr: 1 },
];

const araToplam = ARALIK * (seritler.length - 1);
const sabitToplam = seritler.reduce((t, s) => t + s.sabit, 0);
const frToplam = seritler.reduce((t, s) => t + s.fr, 0);

function paylar(kapsayici) {
  const bos = kapsayici - sabitToplam - araToplam;
  return seritler.map((s) => s.sabit + (s.fr / frToplam) * bos);
}

const KAP = 960;
console.log(`grid-template-columns: ${seritler.map((s) => s.yazim).join(" ")}   gap: ${ARALIK}px   kapsayici: ${KAP}px`);
console.log(`sabit=${sabitToplam}  aralik=${araToplam}  bolunecek bos alan=${KAP - sabitToplam - araToplam}  fr toplami=${frToplam}`);
paylar(KAP).forEach((o, i) => console.log(`  ${seritler[i].yazim.padEnd(6)} -> ${o.toFixed(1)}px`));

console.log("\n--- fr, kapsayicinin degil bos alanin payidir ---");
for (const ekSabit of [0, 240, 480]) {
  const bir = (KAP - ekSabit - araToplam) / 3;
  console.log(`sabit serit ${String(ekSabit).padStart(3)}px -> 1fr = ${bir.toFixed(1)}px  (2fr = ${(2 * bir).toFixed(1)}px)`);
}

// dar kapsayicida fr payi, seridin en kucuk icerik olcusunun altina duser
const DAR = 600;
const enKucukIcerik = [90, 180, 160];   // her seridin en uzun bolunemez icerigi
console.log(`\n--- dar kapsayici (${DAR}px): 1fr = minmax(auto, 1fr) ---`);
let toplam = 0;
paylar(DAR).forEach((pay, i) => {
  const alt = seritler[i].fr > 0 ? enKucukIcerik[i] : 0;
  const uygulanan = Math.max(pay, alt);
  toplam += uygulanan;
  console.log(`  ${seritler[i].yazim.padEnd(6)} pay=${pay.toFixed(1).padStart(6)}  en kucuk icerik=${String(alt).padStart(3)}  uygulanan=${uygulanan.toFixed(1).padStart(6)}`);
});
console.log(`  seritler+aralik = ${(toplam + araToplam).toFixed(1)}px, kapsayici = ${DAR}px, tasma = ${(toplam + araToplam - DAR).toFixed(1)}px`);

console.log(`\n--- ayni kapsayicida minmax(0, 1fr) ---`);
let toplam2 = 0;
paylar(DAR).forEach((pay, i) => {
  toplam2 += pay;
  const ad = seritler[i].fr > 0 ? `minmax(0, ${seritler[i].fr}fr)` : seritler[i].yazim;
  console.log(`  ${ad.padEnd(16)} uygulanan=${pay.toFixed(1).padStart(6)}`);
});
console.log(`  seritler+aralik = ${(toplam2 + araToplam).toFixed(1)}px, tasma = ${(toplam2 + araToplam - DAR).toFixed(1)}px`);
grid-template-columns: 240px 2fr 1fr   gap: 24px   kapsayici: 960px
sabit=240  aralik=48  bolunecek bos alan=672  fr toplami=3
  240px  -> 240.0px
  2fr    -> 448.0px
  1fr    -> 224.0px

--- fr, kapsayicinin degil bos alanin payidir ---
sabit serit   0px -> 1fr = 304.0px  (2fr = 608.0px)
sabit serit 240px -> 1fr = 224.0px  (2fr = 448.0px)
sabit serit 480px -> 1fr = 144.0px  (2fr = 288.0px)

--- dar kapsayici (600px): 1fr = minmax(auto, 1fr) ---
  240px  pay= 240.0  en kucuk icerik=  0  uygulanan= 240.0
  2fr    pay= 208.0  en kucuk icerik=180  uygulanan= 208.0
  1fr    pay= 104.0  en kucuk icerik=160  uygulanan= 160.0
  seritler+aralik = 656.0px, kapsayici = 600px, tasma = 56.0px

--- ayni kapsayicida minmax(0, 1fr) ---
  240px            uygulanan= 240.0
  minmax(0, 2fr)   uygulanan= 208.0
  minmax(0, 1fr)   uygulanan= 104.0
  seritler+aralik = 600.0px, tasma = 0.0px

İlk blok hesabın sırasını gösteriyor: 960 birimlik kapsayıcıdan önce sabit şerit (240) ve iki aralık (48) düşülür, kalan 672 birim üç paya bölünür. 2fr iki pay alır (448), 1fr bir pay (224).

İkinci blok sık yapılan bir okuma hatasını düzeltiyor. 1fr kapsayıcının üçte biri değildir; boş alanın üçte biridir. Sabit şerit büyüdükçe aynı 1fr bildiriminin karşılığı 304’ten 144’e iner. Şerit tanımının kendisi değişmemiştir, bölünen değişmiştir.

Pay toplamı birden küçükse boş alanın tamamı dağıtılmaz: 0.5fr 0.5fr yazımı boş alanın yarısını kullanır, kalanı ızgaranın sonunda durur. Bu davranış, aynı bildirimin esnek kutudaki karşılığıyla tutarlıdır.

Payın Gizli Alt Sınırı

Üçüncü blok, ızgara tanımlarındaki en sık taşma nedenini gösteriyor. Kapsayıcı 600 birime indiğinde son şeridin payı 104 birim hesaplanır, ama şeridin içinde bölünemeyen 160 birimlik bir içerik vardır — uzun bir ölçüm kodu ya da bir tablo. Şerit 160’ta durur ve toplam kapsayıcıyı 56 birim aşar.

Nedeni şudur: 1fr yazımı, tam açılımıyla minmax(auto, 1fr) demektir. Alt sınır auto olduğunda şerit, içeriğinin en küçük içerik ölçüsünün altına inemez. Pay bu sınırın altına düşerse sınır kazanır.

Dördüncü blok çözümü veriyor: alt sınır açıkça sıfıra çekilirse şerit paya uyar ve taşma ortadan kalkar. Bedeli, içeriğin şerit içinde taşabilmesidir; taşma yönetimi CSS ile Görsel Sunum kursunda kurulan bildirimlerle o şeritte ayrıca ele alınır.

Bu, esnek kutudaki min-width: auto kısıtının ızgaradaki karşılığıdır. İki modelde de sorunun kaynağı aynıdır: içerik ölçüsü, hesaplanan paydan büyük olduğunda kısıt kazanır.

Yineleme ve Aralık

Aynı şerit tanımını elle tekrarlamak yerine repeat() yazılır. İlginç olan, yineleme sayısının bir sayı yerine iki anahtar sözcükle verilebilmesidir: auto-fill ve auto-fit. İkisi de kapsayıcıya sığan en çok şeridi üretir; ayrım, üretilen şeritlerin boş kalması durumundadır.

// yineleme.mjs — auto-fill ve auto-fit ile uretilen serit sayisi
const EN_KUCUK = 220;   // minmax(220px, 1fr)
const ARALIK = 24;
const OGE_SAYISI = 5;

// kapsayiciya sigan en cok serit: n*enKucuk + (n-1)*aralik <= genislik
function seritSayisi(genislik) {
  const n = Math.floor((genislik + ARALIK) / (EN_KUCUK + ARALIK));
  return Math.max(1, n);
}

const olcu = (genislik, n) => (genislik - ARALIK * (n - 1)) / n;

console.log("repeat(auto-fill | auto-fit, minmax(220px, 1fr))   gap: 24px   oge sayisi: 5");
console.log("genislik  serit  auto-fill olcu   dolu serit  auto-fit olcu");
for (const g of [320, 500, 760, 1000, 1240, 1600]) {
  const n = seritSayisi(g);
  const dolu = Math.min(n, OGE_SAYISI);
  const fill = olcu(g, n);
  const fit = olcu(g, dolu);
  console.log(
    String(g).padStart(8) +
    String(n).padStart(7) +
    (fill.toFixed(1) + "px").padStart(16) +
    String(dolu).padStart(12) +
    (fit.toFixed(1) + "px").padStart(15)
  );
}

console.log("\n--- sinir: serit sayisinin arttigi tam genislikler ---");
for (let n = 1; n <= 6; n++) {
  const esik = n * EN_KUCUK + (n - 1) * ARALIK;
  console.log(`${n} serit icin gereken en az genislik: ${esik}px`);
}

console.log("\n--- ayni hesap sabit serit sayisiyla: repeat(4, minmax(220px, 1fr)) ---");
for (const g of [500, 760, 1000]) {
  const n = 4;
  const pay = olcu(g, n);
  const uygulanan = Math.max(pay, EN_KUCUK);
  const tasma = uygulanan * n + ARALIK * (n - 1) - g;
  console.log(`genislik ${String(g).padStart(4)} -> pay ${pay.toFixed(1)}px, uygulanan ${uygulanan.toFixed(1)}px, tasma ${tasma.toFixed(1)}px`);
}
repeat(auto-fill | auto-fit, minmax(220px, 1fr))   gap: 24px   oge sayisi: 5
genislik  serit  auto-fill olcu   dolu serit  auto-fit olcu
     320      1         320.0px           1        320.0px
     500      2         238.0px           2        238.0px
     760      3         237.3px           3        237.3px
    1000      4         232.0px           4        232.0px
    1240      5         228.8px           5        228.8px
    1600      6         246.7px           5        300.8px

--- sinir: serit sayisinin arttigi tam genislikler ---
1 serit icin gereken en az genislik: 220px
2 serit icin gereken en az genislik: 464px
3 serit icin gereken en az genislik: 708px
4 serit icin gereken en az genislik: 952px
5 serit icin gereken en az genislik: 1196px
6 serit icin gereken en az genislik: 1440px

--- ayni hesap sabit serit sayisiyla: repeat(4, minmax(220px, 1fr)) ---
genislik  500 -> pay 107.0px, uygulanan 220.0px, tasma 452.0px
genislik  760 -> pay 172.0px, uygulanan 220.0px, tasma 192.0px
genislik 1000 -> pay 232.0px, uygulanan 232.0px, tasma 0.0px

minmax(a, b) bir şeridin alt ve üst sınırını verir: şerit a değerinin altına inmez, b değerinin üstüne çıkmaz. minmax(220px, 1fr) yazımı “en az 220 birim, kalanı pay olarak al” demektir ve yineleme sayısı bu alt sınırdan türetilir.

Şerit sayısı doğrudan çıkar: nn şerit için gereken en az genişlik n×220+(n1)×24n \times 220 + (n-1) \times 24 birimdir ve kapsayıcı bu değeri aştıkça sayı artar. İkinci blok bu eşikleri veriyor; birinci bloktaki sayılar tam olarak eşiklerle uyumludur — 1000 birimlik kapsayıcı dört şerit alır, çünkü dördüncü eşik 952’dir ve beşinci eşik 1196’dır.

Ayrım son satırda görünür. 1600 birimlik kapsayıcı altı şerit alacak kadar geniştir ama yerleştirilecek beş öge vardır. auto-fill altıncı şeridi boş olarak korur; ögeler 246,7 birimde kalır ve sağda boş bir sütun durur. auto-fit boş şeridi çökertir; şerit ve aralığı ortadan kalkar, beş öge kalan alanı paylaşır ve 300,8 birime çıkar.

Seçim ölçütü içerikle ilgilidir. Kart genişliğinin tutarlı kalması isteniyorsa ve ögeler farklı sayfalarda değişiyorsa auto-fill doğrudur: aynı kart her sayfada aynı genişliktedir. Ögelerin kapsayıcıyı her durumda doldurması isteniyorsa auto-fit doğrudur.

Üçüncü blok, sabit yineleme sayısının neden dar ekranlarda taştığını gösteriyor: repeat(4, …) yazımı dört şeridi koşulsuz üretir, alt sınır 220 birimde tutar ve 500 birimlik bir kapsayıcıda 452 birim taşma verir. Yineleme anahtar sözcükleri bu koşulu kapsayıcıdan okur; bir sonraki konuda ele alınacak ortam sorgularına gerek kalmadan şerit sayısını uyarlarlar.

Alanların Adlandırılması

Şerit tanımı ızgarayı kurar; ögelerin nereye düşeceği ayrı bir bildirimdir. En okunur yolu, alanlara ad vermek ve yerleşimi kapsayıcıda metin olarak çizmektir:

/* duzen.css — 3. adım: sayfa ızgarası */
.istasyon-duzeni {
  display: grid;
  grid-template-columns: minmax(0, 2fr) minmax(0, 1fr);
  grid-template-rows: auto auto 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; }

Dizgilerin her satırı bir satır şeridine, her sütunu bir sütun şeridine karşılık gelir. Aynı adın iki hücrede geçmesi, alanın o hücreleri kapsadığını söyler; yan-bilgi iki satır boyunca uzanır. Ad yerine nokta yazılırsa hücre boş bırakılır.

Bu yazımın değeri, düzenin biçem dosyasında görünür olmasıdır: satırların hizası ızgaranın kendisini çizer. Ögelerin belgedeki sırası ile ızgaradaki yeri birbirinden bağımsızdır — bu ayrımın erişilebilirlik sonuçları sonraki derste ele alınacaktır.

Özet

  • display: grid iki eksende şerit tanımı alır; şerit iki hat arasındaki bant, hücre iki şeridin kesişimi, alan ise dört hatla sınırlanan dikdörtgen bölgedir.
  • Hatlar baştan 1’den, sondan 1-1’den numaralanır; ada bağlanabilir ve ad, şerit sayısı değiştiğinde yerleşim bildirimlerini korur.
  • fr bir uzunluk değil pay birimidir: sabit şeritler ve aralıklar düşüldükten sonra kalan alanı böler, dolayısıyla kapsayıcının değil boş alanın oranını verir.
  • 1fr açılımıyla minmax(auto, 1fr) olduğu için şerit en küçük içerik ölçüsünün altına inmez; taşmayı önlemek için alt sınır minmax(0, 1fr) ile açıkça sıfırlanır.
  • repeat(auto-fill, …) sığan bütün şeritleri üretir ve boş olanları korur, auto-fit boş şeritleri çökerterek kalan ögelere dağıtır.

Sonraki Adım

Şerit tanımı ızgarayı kurdu, adlandırılmış alanlar da yerleşimi çizdi. Ama alan tanımı yazılmamış ögeler nereye düşer? Kapsayıcının içinde tanımlanandan çok öge varsa ne olur? Sonraki ders, ögeleri hatlara göre açıkça yerleştiren bildirimleri ve boşta kalanları yerleştiren otomatik algoritmayı ele alır.

İlerlemeni kaydetmek ve not almak için Giriş yap

Notlarım

Not almak için giriş yapmalısın.

Aramak için yazmaya başlayın.

↑↓ Esc gezin · aç · kapat