İçeriğe geç
XDD.baby Yazılım Öğrenme
Sözlük Hata rehberi 0/56

Blok 3 · Ders 21

CSS yerleşim: flexbox, grid ve mobil uyum

Öğeleri yan yana ya da alt alta dizmek (flexbox), ızgara kurmak (grid), konumlandırma ve her ekrana uyan (responsive) tasarım.

Bu dersin sonunda

  • Bir öğenin display değerine bakıp sayfada nasıl yer kaplayacağını söyleyebilirsin
  • Flexbox ile öğeleri bir sıraya dizip justify-content, align-items, gap ve flex: 1 ile yerleştirebilirsin
  • Grid ile fr, repeat ve minmax kullanarak ekran genişliğine göre sütun sayısı değişen bir kart ızgarası kurabilirsin
  • position ile bir rozeti kartın köşesine koyabilir, başlığı kaydırırken ekranın tepesinde tutabilirsin
  • Mobil öncelikli CSS yazıp @media sorgularıyla geniş ekrana uyarlayabilir, DevTools'ta telefon genişliğinde deneyebilirsin
  • Renkleri ve boşlukları CSS değişkenlerinde toplayıp tek satırdan değiştirebilirsin
Bu sayfada
  1. Hazırlık: deneme dosyası
  2. display: her kutunun bir davranışı var
  3. Flexbox: öğeleri bir sıraya dizmek
  4. Grid: satır ve sütun ızgarası
  5. position: öğeyi yerinden oynatmak
  6. Telefona uyum: duyarlı tasarım
  7. CSS değişkenleri
  8. Uygulama: duyarlı şampiyon kartları
  9. Görevler
  10. Kendini sına
  11. Kayıt noktası

Ders 20'de kutuların içini boyadın: renk, yazı tipi, kenarlık, iç ve dış boşluk. Ama kutular hep alt alta dizildi. Bu derste kutuları yerleştiriyorsun: yan yana dizmek, ortalamak, ızgaraya oturtmak, bir köşeye iğnelemek ve en önemlisi aynı sayfanın telefonda da bilgisayarda da düzgün görünmesini sağlamak. Dersin sonunda telefonda tek sütun, bilgisayarda üç sütun olan bir şampiyon kartları sayfan olacak.

Bu derste yeni bir programlama kavramı yok; hepsi CSS. Ama CSS'in en çok zaman yediği yer burası. Yerleşim kuralları sessizce çalışır ya da sessizce çalışmaz; hata mesajı çıkmaz. Bu yüzden her bölümde "neden çalışmadı" sorusunu DevTools ile nasıl cevaplayacağını da göreceksin.

Hazırlık: deneme dosyası

Ders 20'deki sampiyon-karti klasörüne dokunma. Dersin sonunda o karta benzeyen altı kartı tek bir sayfaya dizeceksin; bunun için yeni bir klasörle başlıyorsun. C:\kod içinde sampiyon-kartlari adında bir klasör oluştur ve VS Code'da File → Open Folder ile aç. İçine deneme.html adında bir dosya oluştur ve aşağıdaki iskeleti yaz. Bu dersin küçük denemelerinin hepsini bu dosyada yapacaksın.

deneme.html — sampiyon-kartlari — Visual Studio Code
deneme.html
<!doctype html><html lang="tr"><head>    <meta charset="utf-8">    <meta name="viewport" content="width=device-width, initial-scale=1">    <title>Yerleşim denemeleri</title>    <style>        * {            box-sizing: border-box;        }        body {            font-family: "Segoe UI", Arial, sans-serif;            margin: 12px;        }        /* bölümlerdeki CSS'i buraya yaz */    </style></head><body>    <!-- bölümlerdeki HTML'i buraya yaz --></body></html>
Ln 1, Col 1Spaces: 4UTF-8HTML

Ders 19'daki iskelet ve Ders 20'deki <style> etiketi; içinde iki kural hazır: kutu hesabını kolaylaştıran box-sizing: border-box ve Ders 20'deki yazı tipi listesi.

Sayfayı Live Server ile aç (Ders 18): deneme.html düzenleyicide açıkken sağ alttaki Go Live düğmesine bas. Tarayıcı http://127.0.0.1:5500/deneme.html adresiyle açılır; başka bir adres açılırsa adres çubuğuna bunu kendin yaz. Ders 20'deki gibi örneklerin üstünde iki kutu göreceksin: "<style> içine" yazan CSS'i yorum satırının yerine, "<body> içine" yazan HTML'i gövdeye yaz. Kaydedince Live Server sayfayı kendiliğinden yeniler. Yeni bir örneğe geçince öncekini sil.

Kutuların altındaki tarayıcı pencereleri resim değil, senin tarayıcında o anda çizilen gerçek sayfalar. Bazılarında örnek daha okunur görünsün diye bir iki renk ya da boşluk kuralı fazladan var; asıl kurallar kutudaki kodla aynı. Önizlemenin genişliği ekranına göre değişir, telefondan okuyorsan bazıları farklı görünecek: bu, dersin konusunun ta kendisi.

display: her kutunun bir davranışı var

Ders 19'da yazdığın sayfayı hatırla: başlıklar, paragraflar ve bölümler alt alta dizildi, ama paragrafın içindeki <strong> ya da <a> cümlenin içinde kaldı, alta inmedi. İlkine blok öğe, ikincisine satır içi öğe dendiğini orada gördün. Bu sözcüklerin CSS'te bir karşılığı var: tarayıcı her etikete önceden bir görünüm türü verir. Bu türün adı display; değerini sen de değiştirebilirsin.

Önce farkı gör. <div> ve <span> etiketlerine aynı sınıfı ver:

deneme.html · <style> içine
.kutu {    background-color: #c5cae9;    border: 1px solid #5c6bc0;    padding: 4px 8px;}
deneme.html · <body> içine
<div class="kutu">Ahri</div><div class="kutu">Garen</div><span class="kutu">Jinx</span><span class="kutu">Lux</span>
127.0.0.1:5500/deneme.html

İki div kendi satırlarını açtı ve bütün genişliği kapladı. İki span ise yan yana durdu ve yalnız yazıları kadar yer tuttu. İkisi de aynı CSS'i aldı; farkı yaratan display değeri.

  • block (blok): Kendi satırını açar, bütün genişliği kaplar; sonraki öğe alta iner. div, p, h1–h6, ul, section, header, main, footer böyledir.
  • inline (satır içi): Yazının bir parçası gibi davranır. Yalnız içeriği kadar yer kaplar, satır dolunca alt satıra kayar. span, a, strong, em, code böyledir.

inline öğeye genişlik verilmez

Satır içi öğelerin bir kısıtı var: width ve height onlarda işlemez. Üstten ve alttan verdiğin margin de satırları itmez. Ahri ve Garen'in adlarını 120 piksellik rozetlere çevirmeyi dene:

deneme.html · <style> içine
.rozet {    background-color: #ffe082;    padding: 4px 8px;    width: 120px;}
deneme.html · <body> içine
<p>Takımda <span class="rozet">Ahri</span> ve <span class="rozet">Garen</span> var.</p>

Rozetler genişlemez; yine yazı kadar kalır. CSS'in hata mesajı yoktur, satır sessizce görmezden gelinir. Ama DevTools sessiz kalmaz.

img bu kuralın istisnasıdır: satır içi durur ama içi dışarıdan gelen bir resim olduğu için width ve height alır. Ders 19'da resme width ve height nitelikleriyle boyut verebilmen bundandı.

Sık hatawidth verdin ama span genişlemiyor

Ne zaman çıkar? Satır içi bir öğeye (span, a, strong) width ya da height verince. DevTools uyarısı: "The display: inline property prevents width from having an effect."

Sayfadaki Ahri rozetine sağ tıkla ve İncele'yi seç (Inspect). DevTools açılır, Elements sekmesinde (Türkçe arayüzde Öğeler) <span class="rozet"> satırı seçili gelir. Yanındaki Styles (Stiller) bölmesinde width: 120px soluk görünür ve yanında küçük bir ⓘ simgesi çıkar. Üzerine gelince şunu yazar:

The display: inline property prevents width from having an effect.

Try setting display to something other than inline.

DevTools Türkçe açılıyorsa aynı uyarı şöyle görünür:

display: inline özelliği, width özelliğinin etkili olmasını engeller.

display öğesini inline dışında bir değere ayarlamayı deneyin.

Nasıl düzelir? Uyarının söylediğini yap: display değerini değiştir. Yazının içinde kalsın ama boyut alsın istiyorsan display: inline-block, kendi satırını açsın istiyorsan display: block yaz.

inline-block: ikisinin karışımı

inline-block öğe yazının içinde, satırda durur ama bir kutu gibi width, height, margin ve padding alır. Düğmeler (button) tarayıcıda zaten böyledir. Rozet kuralına tek satır ekle:

deneme.html · <style> içine
.rozet {    display: inline-block;    background-color: #ffe082;    padding: 4px 8px;    width: 120px;}
127.0.0.1:5500/deneme.html

Farkı görebilesin diye önizlemede iki hâli alt alta koyduk: üst satırdaki sarı rozetlerde display yok, alt satırdaki yeşillerde inline-block var. Sarılar yazı kadar kaldı, yeşiller 120 piksel oldu ama ikisi de cümlenin içinde duruyor.

none: hiç yokmuş gibi

display: none öğeyi sayfadan kaldırır: çizilmez, yer de kaplamaz; çevresindeki öğeler boşluğu kapatır. HTML'de durmaya devam eder (DevTools'ta görürsün), yalnız ekrana gelmez. Telefonda bir menüyü gizlemek ya da bir uyarıyı sonradan göstermek için kullanılır; Ders 22'de JavaScript ile öğeleri böyle açıp kapatacaksın.

block kendi satırını açar Ahri Garen inline yazının içinde akar Takımda Ahri ve Garen var. width işlemez: kutu yazı kadar inline-block satırda durur, boyut alır Takımda Ahri ve Garen var. width işler: kutu verilen genişlikte none çizilmez, yer kaplamaz önce Ahri Teemo Garen sonra Ahri Garen yeri kapandı
Şekil 21.1 Dört display değeri: aynı içerik, dört farklı davranış.
DeğerYan yana durur mu?width / height işler mi?Varsayılan olduğu etiketler
blockHayır, kendi satırını açarEvetdiv, p, h1, ul, section, header
inlineEvet, yazı gibi akarHayırspan, a, strong, em
inline-blockEvetEvetbutton
noneHiç görünmezYokSen verirsin

Flexbox: öğeleri bir sıraya dizmek

Üç şampiyonu yan yana koymak istiyorsun. div'ler blok olduğu için alt alta dizilir. inline-block ile yan yana gelirler ama aralarındaki boşluğu ayarlamak, onları dikeyde hizalamak ve kalan yeri paylaştırmak zahmetlidir. Flexbox tam bu iş için yapıldı.

Kural tek cümlelik: display: flex kapsayıcıya yazılır, yani dizilecek öğelerin hepsini içine alan öğeye. İçindeki öğeler kendiliğinden yan yana dizilir.

deneme.html · <style> içine
.takim {    display: flex;    gap: 8px;    padding: 8px;    background-color: #ede7f6;}.sampiyon {    background-color: #7e57c2;    color: white;    padding: 12px 16px;    border-radius: 8px;}
deneme.html · <body> içine
<div class="takim">    <div class="sampiyon">Ahri</div>    <div class="sampiyon">Garen</div>    <div class="sampiyon">Jinx</div></div>
127.0.0.1:5500/deneme.html

.sampiyon kuralına dokunmadık; üç blok öğe, kapsayıcıları flex olunca yan yana geçti. gap: 8px aralarına 8 piksel boşluk koydu. Önce display: flex satırını silip kaydet, sonra geri yaz; farkı kendi gözünle gör.

Ders 20'den hatırla: bir öğenin hemen içindekiler onun çocukları, onu saran öğe de ebeveynidir. Flex yalnız doğrudan çocukları dizer; çocukların içindekiler (torunlar) etkilenmez.

Ana eksen ve çapraz eksen

Flexbox'u anlamanın anahtarı iki yöndür. Öğelerin dizildiği yöne ana eksen, ona dik yöne çapraz eksen denir. Varsayılan olarak ana eksen soldan sağa, çapraz eksen yukarıdan aşağıdır.

flex-direction: row (varsayılan) Ahri Garen Jinx ana eksen çapraz eksen flex-direction: column Ahri Garen Jinx ana eksen çapraz eksen justify-content: ana eksende align-items: çapraz eksende
Şekil 21.2 Ana eksen öğelerin dizildiği yöndür. flex-direction: column yazınca iki eksen yer değiştirir.

justify-content: ana eksende dağıtmak

justify-content öğeleri ana eksen boyunca nereye toplayacağını ya da aralarındaki boş yeri nasıl dağıtacağını söyler. Kapsayıcıya yazılır:

CSS
.takim {    display: flex;    justify-content: center;}
127.0.0.1:5500/deneme.html
DeğerNe yapar?Nerede işine yarar?
flex-startHepsini başa toplar (varsayılan)Sıradan listeler
centerHepsini ortaya toplarOrtalanmış düğme grubu
flex-endHepsini sona toplarSağa yaslı "Kaydet / İptal"
space-betweenİlki başa, sonuncusu sona; boşluk aralaraSolda logo, sağda menü
space-evenlyBütün boşluklar eşit, kenarlar dahilEşit aralıklı simgeler

align-items: çapraz eksende hizalamak

align-items öğeleri çapraz eksende hizalar. Etkisini görmek için kapsayıcının çapraz eksende yeri olmalı; bu yüzden .takim'a bir yükseklik ver. Önizlemede fark daha iyi görünsün diye ortadaki Garen'i de biraz uzattık:

CSS
.takim {    display: flex;    gap: 6px;    height: 64px;    align-items: center;}
127.0.0.1:5500/deneme.html

İlk satıra dikkat et: varsayılan değer stretch (gerdir), öğeleri kapsayıcının yüksekliği kadar uzatır. Flex'e yeni geçenlerin şaşırdığı ilk şey budur: "Kutularıma yükseklik vermedim, neden bu kadar uzunlar?" Cevap: align-items vermediğin için gerildiler.

flex-direction: ekseni çevirmek

flex-direction: column öğeleri alt alta dizer; bu durumda ana eksen yukarıdan aşağı döner, justify-content de dikeyde çalışmaya başlar. Bir kartın düğmesini kartın dibine itmek için kullanılır:

CSS
.kart {    display: flex;    flex-direction: column;    justify-content: space-between;    height: 160px;}

Bu kartın ilk çocuğu (başlık) tepede, son çocuğu (düğme) dipte durur; aradaki boşluk kendiliğinden dağılır. Uygulamada üst çubuğu telefonda alt alta, bilgisayarda yan yana dizmek için aynı özelliği kullanacaksın.

gap ve flex-wrap

gap öğelerin arasına boşluk koyar, kenarlara koymaz. Eskiden her öğeye margin verip sonuncusundan silmek gerekirdi; artık tek satır yeter. Flex'te de, birazdan göreceğin grid'de de aynı çalışır.

Peki öğeler sığmazsa? Varsayılan değer flex-wrap: nowrap: flex bütün öğeleri tek satırda tutmaya çalışır. Önce öğeleri daraltır; en uzun sözcükten daha fazla daraltamayınca öğeler kapsayıcıdan taşar. flex-wrap: wrap yazarsan sığmayanlar alt satıra geçer. Denemek için takıma Lux, Yasuo ve Teemo'yu da ekle, kapsayıcıyı 300 piksele daralt:

CSS
.takim {    display: flex;    flex-wrap: wrap;    gap: 8px;    width: 300px;}
127.0.0.1:5500/deneme.html

İlk satırda Yasuo'nun bir kısmı ve Teemo'nun tamamı açık mor kapsayıcının dışına taştı. Telefonda bu taşma bütün sayfanın yana kaymasına yol açar. İkinci satırda ise sığmayanlar alta indi.

flex: 1 ile kalan yeri doldurmak

Bazen bir öğenin "geri kalan her yeri" kaplamasını istersin. Oyuncu satırını düşün: solda ad, sağda can, ortada can çubuğu. Ad kısa da olsa uzun da olsa çubuk aradaki boşluğu doldurmalı. Bu işi flex: 1 yapar; kapsayıcıya değil, büyümesini istediğin öğeye yazılır:

deneme.html · <style> içine
.oyuncu {    display: flex;    align-items: center;    gap: 10px;    padding: 10px;    background-color: #263238;    color: white;    border-radius: 8px;}.can {    flex: 1;    height: 12px;    background-color: #43a047;    border-radius: 6px;}
deneme.html · <body> içine
<div class="oyuncu">    <span>Ahri</span>    <div class="can"></div>    <span>100 can</span></div><div class="oyuncu">    <span>Aurelion Sol</span>    <div class="can"></div>    <span>85 can</span></div>
127.0.0.1:5500/deneme.html

Uzun adlı satırda çubuk kısaldı ama boşluk yine tam doldu. Aynı satırdaki birden çok öğeye flex: 1 verirsen yeri eşit paylaşırlar; birine flex: 2 verirsen o, ötekilerin iki katı pay alır.

Tam ortalamak

CSS'te bir şeyi hem yatayda hem dikeyde ortalamak eskiden ünlü bir dertti. Flex ile üç satır:

deneme.html · <style> içine
.zafer {    display: flex;    justify-content: center;    align-items: center;    height: 160px;    background-color: #1a237e;    color: #ffd54f;    font-size: 32px;    font-weight: bold;    border-radius: 8px;}
deneme.html · <body> içine
<div class="zafer">ZAFER</div>
127.0.0.1:5500/deneme.html

justify-content: center yatayda (ana eksen), align-items: center dikeyde (çapraz eksen) ortaladı. İkisini karıştırırsan ezberlemeye çalışma, şekle dön: "justify ana eksen, align çapraz eksen."

Sık hataDikey ortalama çalışmıyor: kapsayıcının yüksekliği yok

Ne zaman çıkar? align-items: center yazdın ama yazı yine tepede gibi duruyor. Hata mesajı yok, DevTools'ta da uyarı yok; çünkü kural gerçekten çalışıyor.

CSS
.zafer {    display: flex;    justify-content: center;    align-items: center;    background-color: #1a237e;    color: #ffd54f;    font-size: 32px;}
127.0.0.1:5500/deneme.html

Yükseklik vermediğin blok öğe, içeriği kadar uzar. Kutu zaten yazı kadar olduğu için dikeyde ortalanacak boş yer yok. DevTools'ta .zafer'in üzerine gelince sayfada boyandığı alanı görürsün: yazıyı sımsıkı sarar.

Nasıl düzelir? Kapsayıcıya yükseklik ver: height: 160px ya da içerik büyürse uzayabilsin diye min-height: 160px. Bütün ekranı kaplayan bir giriş ekranı istiyorsan Ders 20'deki vh birimini kullan: min-height: 100vh.

Sık hatadisplay: flex kapsayıcıya değil çocuklara yazılmış

Ne zaman çıkar? justify-content ya da gap hiçbir şey yapmıyor, öğeler alt alta duruyor. DevTools uyarısı: "The display: block property prevents justify-content from having an effect."

CSS
.takim {    justify-content: center;    gap: 8px;    padding: 8px;    background-color: #ede7f6;}.sampiyon {    display: flex;    background-color: #7e57c2;    color: white;    padding: 12px 16px;    border-radius: 8px;}

display: flex dizilecek öğelerin kendisine (.sampiyon) yazıldı. Her şampiyon kendi içindeki yazıyı dizen bir kapsayıcı oldu; .takim ise sıradan bir blok olarak kaldı. Bu yüzden şampiyonlar yine alt alta, .takim'daki justify-content ve gap boşa gitti. DevTools bunu iki yerden gösterir:

Chrome DevTools Elements panelinde div class takim satırı seçili. Altındaki üç div class sampiyon satırının her birinin yanında flex rozeti var, takim satırının yanında yok. Styles bölmesinde takim kuralındaki justify-content ve gap satırları soluk, yanlarında bilgi simgesi. Açılan uyarıda: The display: block property prevents justify-content from having an effect. Try adding display: grid or display: flex to make this element into a container.
Şekil 21.3 DevTools'un gerçek görüntüsü: flex rozeti çocukların yanında, kapsayıcıda ise yok; justify-content satırının üzerine gelince uyarı açılıyor.
  • Elements ağacında flex rozeti hangi satırın yanındaysa flex kapsayıcı odur. Burada rozet .sampiyon satırlarının yanında; .takim'ın yanında yok.
  • Styles bölmesinde .takim'ın justify-content ve gap satırları soluk ve yanlarında ⓘ var. Üzerine gelince şekildeki uyarı açılır.

DevTools Türkçe açılıyorsa aynı uyarı şöyledir:

display: block özelliği, justify-content özelliğinin etkili olmasını engeller.

Bu öğeyi kapsayıcı haline getirmek için display: grid veya display: flex eklemeyi deneyin.

Nasıl düzelir? display: flex satırını .sampiyon'dan alıp .takim'a taşı. Sor kendine: "Yan yana dizilecek olanlar kim, onları içine alan kim?" display: flex içine alana yazılır.

Grid: satır ve sütun ızgarası

Yedi şampiyon kartını flex ile dizdiğini düşün: sığmayanlar alta geçsin diye flex-wrap: wrap, eşit paylaşsınlar diye her karta flex: 1. min-width: 120px kartların 120 pikselden fazla daralmasını engeller; sığmayan kart alt satıra iner:

deneme.html · <style> içine
.kartlar {    display: flex;    flex-wrap: wrap;    gap: 8px;}.kart {    flex: 1;    min-width: 120px;    padding: 12px;    background-color: #e3f2fd;    border: 2px solid #1e88e5;    border-radius: 8px;}
deneme.html · <body> içine
<div class="kartlar">    <div class="kart">Ahri</div>    <div class="kart">Garen</div>    <div class="kart">Jinx</div>    <div class="kart">Lux</div>    <div class="kart">Yasuo</div>    <div class="kart">Teemo</div>    <div class="kart">Annie</div></div>
127.0.0.1:5500/deneme.html

Son satıra bak: oradaki kartlar üsttekilerden daha geniş. Flex tek boyutludur: her satırı ayrı ayrı doldurur, alt satırdaki kartlar üstteki sütunlara hizalanmaz. Kartları hem satır hem sütun olarak hizalamak istiyorsan grid kullanırsın.

Sütunları tanımlamak

Grid'in ana özelliği grid-template-columns: sütun sayısını ve genişliklerini yan yana yazarsın. Üç tane 150 piksellik sütun:

deneme.html · <style> içine
.izgara {    display: grid;    grid-template-columns: 150px 150px 150px;    gap: 8px;}

Denemek için önceki örneğin HTML'inde dış kutunun sınıfını kartlar yerine izgara yap ve .kart kuralındaki flex: 1 ile min-width satırlarını sil; grid'de kartların genişliğini artık sütunlar belirliyor.

Kartlar soldan sağa sütunları doldurur, sütunlar bitince yeni satıra geçer. Satırları tanımlaman gerekmez; grid gerektiği kadar satırı kendisi açar. Ama sabit pikselin bir sorunu var: üç sütun ve iki boşluk 466 piksel eder. Telefonun ekranı bundan darsa ızgara taşar.

fr: kalan yerden pay

fr birimi "kalan yerden bir pay" demek. 1fr 1fr 1fr yazınca boşluklar ayrıldıktan sonra kalan genişlik üç eşit paya bölünür; ekran ne kadar genişse sütunlar o kadar geniş olur. Paylar eşit olmak zorunda değil: 2fr 1fr 1fr ilk sütuna iki pay verir.

CSS
.izgara {    display: grid;    grid-template-columns: 1fr 2fr 1fr;    gap: 20px;}
grid-template-columns: 1fr 2fr 1fr; gap: 20px; 1fr 140 px 2fr 280 px 1fr 140 px kapsayıcı: 600 px · sarı şeritler: 20 px boşluk 1. Boşluklar ayrılır: 600 pikselden 2 × 20 düşer, 560 kalır. 2. Kalan paylara bölünür: 1 + 2 + 1 = 4 pay, bir pay 140 piksel.
Şekil 21.4 1fr 2fr 1fr hesabı: önce boşluklar ayrılır, kalan genişlik paylara bölünür.

repeat ve minmax

Aynı değeri üst üste yazmak yerine repeat kullanırsın: repeat(3, 1fr), 1fr 1fr 1fr ile aynıdır. İçindeki virgüle dikkat: ilk değer kaç kez, ikincisi neyin tekrar edileceği.

minmax(en-az, en-çok) bir sütuna alt ve üst sınır koyar. minmax(140px, 1fr) "en az 140 piksel ol, yer varsa bir pay kadar büyü" demek. Tek başına pek işe yaramaz; asıl gücü bir sonraki adımda ortaya çıkar.

auto-fit: sığdığı kadar sütun

Sütun sayısını sen yazmasan, ekran kaç sütun alıyorsa o kadar açılsa? repeat'in ilk değerine sayı yerine auto-fit yazınca grid tam olarak bunu yapar:

deneme.html · <style> içine
.izgara {    display: grid;    grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));    gap: 8px;}

Bunu şöyle oku: "En az 140 piksellik sütunlardan sığdığı kadar aç, kalan yeri onlara eşit paylaştır." Ekran daralınca sütun sayısı kendiliğinden azalır. Ekran genişliğine göre tek bir koşul yazmadan (koşullu CSS'i birazdan @media ile göreceksin) her ekrana uyan bir ızgara kurmuş olursun.

127.0.0.1:5500/deneme.html

Kaç sütun gördüğün ekranına bağlı: önizleme, okuduğun sayfanın genişliğini alır ve ızgara ona uyar. Bilgisayardaysan tarayıcı pencereni daraltıp genişlet; sütun sayısı pencereyle birlikte değişir. Hangi genişlikte olursa olsun alt satırdaki kartlar üsttekilerle aynı genişlikte kalır; flex örneğindeki sorun yok.

repeat(auto-fit, minmax(140px, 1fr)) · gap: 8px kapsayıcı 280 px → 1 sütun (2 sütun için 288 px gerekir) kapsayıcı 420 px → 2 sütun (3 sütun için 436 px gerekir) kapsayıcı 640 px → 4 sütun (5 sütun için 732 px gerekir)
Şekil 21.5 Aynı kural üç farklı genişlikte: 140 piksellik en az genişlikle kaç sütun sığıyorsa o kadar sütun açılır.

auto-fill: boş sütunlar yerinde kalsın

auto-fit'in bir kardeşi var: auto-fill. Kart çokken ikisi aynı davranır. Fark kart az olunca çıkar; iki ızgaraya ikişer kart koyup dene. .izgara kuralı olduğu gibi kalsın, yeni iki kuralı onun altına yaz: özgüllükleri eşit, sonra yazılan kazanır (Ders 20).

deneme.html · <style> içine
.sigdir {    grid-template-columns: repeat(auto-fit, minmax(80px, 1fr));}.doldur {    grid-template-columns: repeat(auto-fill, minmax(80px, 1fr));}
deneme.html · <body> içine
<div class="izgara sigdir">    <div class="kart">Ahri</div>    <div class="kart">Garen</div></div><div class="izgara doldur">    <div class="kart">Ahri</div>    <div class="kart">Garen</div></div>
127.0.0.1:5500/deneme.html

İki ızgara da sığan sütunların hepsini açar. auto-fit (sığdır) boş kalanları kapatıp yeri dolu kartlara paylaştırır: iki kart bütün satırı kaplar. auto-fill (doldur) boş sütunları yerinde bırakır: kartlar en dar hâllerine yakın kalır. Kart sayısı değişen listelerde, örneğin bir dükkânda tek bir eşya kaldığında, kartın bütün ekran boyunca uzamasını istemiyorsan auto-fill seç.

Sık hatarepeat(3 1fr): virgülü unutmak

Ne zaman çıkar? repeat içine virgül koymayı unutunca ızgara bozulur, kartlar alt alta tek sütuna dizilir. DevTools satırın başına uyarı üçgeni koyar: "Invalid property value".

CSS
.izgara {    display: grid;    grid-template-columns: repeat(3 1fr);    gap: 8px;}

Tarayıcı anlamadığı bir değeri görünce o satırı bütünüyle atar; geriye display: grid kalır, tanımlanmış sütun olmadığı için grid tek sütun açar. DevTools'ta Styles bölmesinde satırın üstü çizilir ve başına ⚠ gelir. Üzerine gelince Invalid property value yazar; Türkçe arayüzde Geçersiz özellik değeri.

Ders 19'da HTML için bir doğrulayıcı kullandın; CSS'in de var. W3C CSS doğrulayıcısını aç, By direct input sekmesine bu kuralı yapıştır ve Check'e bas. Sonuç şöyle görünür:

Kırmızı başlıkta Sorry! We found the following errors (1). Altındaki satırda sırasıyla 3, .izgara, kesik çizgili boş bir kutu, is an incorrect operator yazısı ve kesik çizgili kutu içinde repeat(3 1fr) var.
Şekil 21.6 W3C CSS doğrulayıcısının gerçek sonucu: satır numarası, seçici ve mesaj.

Mesajın başındaki kesik çizgili küçük kutunun içinde tek bir boşluk var; mesaj "boşluk yanlış bir operatör" diyor. Tarayıcı 3 ile 1fr arasında virgül bekliyordu, boşluk buldu. Doğrulayıcının Türkçe seçeneği yok, mesajlar İngilizce gelir.

Nasıl düzelir? Virgülü ekle: repeat(3, 1fr). Bir kuralın hiç işlemediğini görünce ilk bakacağın yer DevTools'taki üstü çizili satırlardır.

Uzun içerik ızgarayı taşırabilir

1fr sütunun bir gizli kuralı var: içerikten daha dar olamaz. Boşluksuz uzun bir sözcük (bir oyuncu adı, bir bağlantı) sütunu kendi genişliğine kadar şişirir. Üç eşit sütun isteyip ilk karta uzun bir ad yaz:

deneme.html · <style> içine
.izgara {    display: grid;    grid-template-columns: repeat(3, 1fr);    gap: 8px;}
deneme.html · <body> içine
<div class="izgara">    <div class="kart">Oyuncu: SihirdarVadisininEnHızlıYasuoOyuncusu</div>    <div class="kart">Oyuncu: Deniz</div>    <div class="kart">Oyuncu: Ece</div></div>
Sık hataUzun bir sözcük ızgarayı taşırıyor

Ne zaman çıkar? Sütunlar eşit olmalıyken biri çok geniş, ötekiler ezik; telefonda sayfa yana kayıyor. Hata mesajı yok.

127.0.0.1:5500/deneme.html

Üç sütun eşit olmalıydı ama değil: bilgisayarda ilk sütun uzun sözcüğe yer açmak için genişledi, öteki ikisi daraldı. Telefonda ise ilk kart tek başına önizlemenin bütün genişliğini kaplıyor, öteki ikisi önizlemenin dışına itiliyor. Grid ve flex öğelerinin bir kuralı var: varsayılan olarak içlerindeki en uzun sözcükten dar olamazlar.

Nasıl düzelir? Öğeye iki izin ver: min-width: 0 ("içeriğinden dar olabilirsin") ve overflow-wrap: break-word ("sığmayan sözcüğü böl").

CSS
.kart {    min-width: 0;    overflow-wrap: break-word;}
127.0.0.1:5500/deneme.html

Aynı iki satır flex öğelerinde de işe yarar. Kullanıcının yazdığı her şey (ad, yorum, bağlantı) bir gün çok uzun gelir; kartlarına bu iki satırı baştan koymak iyi bir alışkanlıktır.

Flex mi, grid mi?

İkisi rakip değil, birlikte kullanılır. Uygulamada kartları grid ile dizip her kartın içini flex ile düzenleyeceksin.

DurumSeçimNeden
Menüdeki bağlantılar bir satırdaFlexTek sıra, öğeler içerikleri kadar
Solda logo, sağda menüFlexjustify-content: space-between
Bir öğe kalan yeri doldursunFlexflex: 1
Kart galerisi, envanterGridSatırlar ve sütunlar hizalı
Ekrana göre sütun sayısı değişsinGridrepeat(auto-fit, minmax(...))

Kısaca: tek yönde dizmek için flex, iki yönde hizalamak için grid.

position: öğeyi yerinden oynatmak

Şimdiye kadarki her şey normal akışta (normal flow) çalıştı: her öğe sırayla yer kaplar, biri büyürse ötekileri iter. Ama bazen bir öğenin akışın dışına çıkıp belli bir yere iğnelenmesini istersin: kartın köşesindeki "YENİ" rozeti, ekranın sağ altında hep duran bir düğme, kaydırırken tepede kalan bir başlık. Bunu position özelliği yapar.

DeğerNeye göre yerleşir?Akışta yer kaplar mı?
staticHiçbir şeye; normal akış (varsayılan)Evet
relativeKendi normal yerine göre kayarEvet, eski yeri boş kalır
absoluteEn yakın konumlanmış atasına göreHayır
fixedTarayıcı penceresine göreHayır
stickyNormal akışta durur, kayınca yapışırEvet

Yeri top, right, bottom ve left ile verirsin: top: 8px "üst kenardan 8 piksel içeride" demek. static öğede bu dört özellik işlemez. position değeri static dışında bir şey olan öğeye konumlanmış (positioned) öğe denir. Bir öğenin ataları ise ebeveyni, ebeveyninin ebeveyni diye <html>'e kadar uzanan zincirdir.

relative kendi yerinden kayar eski yeri kutu top: 22px; left: 60px; absolute konumlanmış ataya göre YENİ kart: position: relative rozet: top 8px, right 8px fixed sayfa kayar, düğme kalır Yukarı sticky kayınca tepeye yapışır başlık (top: 0)
Şekil 21.7 Dört position değeri ve neye göre yerleştikleri.

relative ve absolute: köşeye rozet

relative tek başına seyrek kullanılır: öğeyi kendi yerinden biraz kaydırır ama eski yeri boş kalır. Asıl işi başkadır: içindeki absolute öğelere referans olmak.

absolute öğe akıştan tamamen çıkar; başka hiçbir öğeyi itmez, onlar da onu görmez. Yerini en yakın konumlanmış atasının kenarlarına göre alır. Böyle bir ata yoksa sayfanın kendisine göre yerleşir.

Kalıp hep aynıdır: kapsayıcıya position: relative, iğnelenecek öğeye position: absolute ve yer bilgisi.

deneme.html · <style> içine
.kart {    position: relative;    width: 220px;    padding: 16px;    background-color: #e3f2fd;    border: 2px solid #1e88e5;    border-radius: 8px;}.rozet {    position: absolute;    top: 8px;    right: 8px;    padding: 2px 8px;    background-color: #e53935;    color: white;    font-size: 12px;    font-weight: bold;    border-radius: 999px;}
deneme.html · <body> içine
<div class="kart">    <span class="rozet">YENİ</span>    <h3>Ahri</h3>    <p>Büyücü · Orta koridor</p></div>
127.0.0.1:5500/deneme.html

Rozet HTML'de başlıktan önce yazılmasına rağmen başlığı aşağı itmedi; akışın dışında, kartın sağ üst köşesinden 8 piksel içeride duruyor. border-radius: 999px kenarları tamamen yuvarlayıp hap biçimi verir.

Sık hataRozet kartın değil, sayfanın köşesine gidiyor

Ne zaman çıkar? absolute öğenin atalarından hiçbiri konumlanmamışsa. Kartın position: relative satırını sil ve kaydet:

127.0.0.1:5500/deneme.html

Rozet referans bulamadı ve sayfanın sağ üst köşesine uçtu. Hata mesajı yok; DevTools'ta rozetin top ve right satırları sorunsuz görünür, çünkü kurallar gerçekten uygulanıyor, yalnız sandığın kutuya göre değil.

Nasıl düzelir? Rozetin durması gereken kapsayıcıya position: relative ekle. Rozet her zaman en yakın konumlanmış atayı arar; o yoksa sayfaya göre yerleşir.

fixed ve sticky: kaydırınca yerinde kalanlar

fixed öğe tarayıcı penceresine iğnelenir: sayfa kayarken o hiç kımıldamaz. "Yukarı çık" düğmeleri ve sohbet balonları böyledir. sticky ise relative ile fixed'in arasıdır: normal akışta yerinde durur, sayfa kayıp öğe top ile verdiğin noktaya gelince oraya yapışır. Sabit kalan başlıklar ve tablo başlıkları böyle yapılır. sticky için top yazmak şarttır; yazmazsan yapışacak yer bilinmez ve öğe normal kayar.

deneme.html · <style> içine
.ust {    position: sticky;    top: 0;    padding: 12px 16px;    background-color: #1a237e;    color: white;}.kart {    margin: 12px 0;    padding: 12px;    background-color: #e3f2fd;    border: 2px solid #1e88e5;    border-radius: 8px;}.yukari {    position: fixed;    right: 12px;    bottom: 12px;    padding: 8px 14px;    background-color: #e53935;    color: white;    text-decoration: none;    border-radius: 999px;}
deneme.html · <body> içine
<header class="ust">Sihirdar Vadisi Rehberi</header><main>    <div class="kart">Ahri: Orta koridor</div>    <div class="kart">Garen: Üst koridor</div>    <div class="kart">Jinx: Alt koridor</div>    <div class="kart">Lux: Destek</div>    <div class="kart">Yasuo: Orta koridor</div>    <div class="kart">Teemo: Üst koridor</div></main><a class="yukari" href="#">Yukarı</a>

href="#" sayfanın en başını gösterir; düğmeye basınca sayfa tepeye döner. Aşağıdaki önizlemenin içini fareyle ya da parmağınla aşağı kaydır:

127.0.0.1:5500/deneme.html

Kendi sayfanda denerken pencere bütün kartları birden gösteriyorsa kaydıracak yer kalmaz ve sticky hiçbir şey yapmıyormuş gibi görünür. Kartları kopyalayıp çoğalt ya da tarayıcı penceresini kısalt.

sticky öğe ebeveyninin dışına çıkmaz: ebeveyni ekrandan çıkınca o da onunla birlikte gider. Bu yüzden sayfanın üst çubuğunu body'nin doğrudan çocuğu yaparsın.

z-index: kim üstte?

Konumlanmış öğeler üst üste binebilir. Hangisinin üstte çizileceğini z-index belirler: sayısı büyük olan üstte durur. Yalnız konumlanmış öğelerde (ve flex ya da grid öğelerinde) işe yarar. Bir değer vermezsen HTML'de sonra gelen üstte çizilir; çoğu zaman sorun da buradan çıkar.

Sık hataKaydırınca kartlar yapışkan başlığın üstünden geçiyor

Ne zaman çıkar? Kartlara rozet için position: relative verdiysen ve başlık sticky ise. Kartlar da başlık da konumlanmış öğedir; HTML'de sonra geldikleri için kartlar başlığın üstüne çizilir. Yukarıdaki sayfada .kart kuralına position: relative ekleyip sayfayı biraz kaydırınca gerçekte şu görünür:

Sayfa aşağı kaydırılmış. Mavi Sihirdar Vadisi Rehberi başlığının alt yarısını Ahri: Orta koridor kartı örtüyor, başlığın yazısı kartın arkasında kalmış. Altta öteki kartlar ve sağ altta kırmızı Yukarı düğmesi.
Şekil 21.8 Başsız Chromium'da çekilmiş gerçek görüntü: sayfa 90 piksel kaydırılmış, Ahri'nin kartı mavi başlığın üstüne binmiş.

Nasıl düzelir? Başlığa bir z-index ver; kartların varsayılan değerinden büyük herhangi bir sayı olur:

CSS
.ust {    position: sticky;    top: 0;    z-index: 10;}

z-index: 10 yazınca başlık bütün kartların üstünde kalır. Uygulamada bu satırı baştan yazacaksın.

Telefona uyum: duyarlı tasarım

Bilgisayarda üç sütun hâlinde güzel duran bir sayfa, 390 piksellik bir telefon ekranında sığmaz. Ya yazılar okunamayacak kadar küçülür ya da sayfa yana kayar. Bugün sitelerin önemli bir kısmı telefondan açılıyor; bu yüzden her sayfayı her genişlikte düzgün görünecek biçimde yazarsın.

Görüntü alanı

Ders 20'deki vw ve vh birimlerini hatırla: "pencere genişliğinin ve yüksekliğinin yüzde biri". O pencerenin, yani tarayıcının sayfayı çizdiği alanın bir adı var.

@media: koşullu CSS

Medya sorgusu (media query), içindeki kuralları yalnız belli bir koşulda uygulayan bir CSS bloğudur. En çok kullanılan koşul görüntü alanının genişliği:

deneme.html · <style> içine
.duzen {    display: grid;    gap: 12px;}@media (min-width: 500px) {    .duzen {        grid-template-columns: 1fr 2fr;    }}
deneme.html · <body> içine
<div class="duzen">    <aside class="yan">Koridorlar: Üst, Orta, Alt</aside>    <main class="icerik">Orta koridorda oynayan şampiyonlar büyü hasarıyla öne çıkar. Ahri, Lux ve Annie iyi başlangıçlardır.</main></div>

@media (min-width: 500px) "görüntü alanı en az 500 piksel genişse" demek. Koşul doğruysa süslü parantezin içindeki kurallar da uygulanır; değilse yok sayılır. 500'ün altında .duzen sıradan bir tek sütunlu grid'dir, 500 ve üstünde iki sütun olur.

127.0.0.1:5500/deneme.html

Bilgisayardan bakıyorsan önizleme 500 pikselden geniş, iki kutu yan yana. Telefondan bakıyorsan önizleme daha dar, kutular alt alta. Aynı CSS, iki ayrı sonuç: medya sorgusu tam olarak bu.

YazımAnlamı500 pikselde çalışır mı?
@media (min-width: 500px)Genişlik 500 ya da daha fazlaEvet
@media (max-width: 500px)Genişlik 500 ya da daha azEvet
@media (min-width: 501px)Genişlik 501 ya da daha fazlaHayır

Sınırın kendisi iki yazımda da dahildir: tam 500 pikselde ikisi de çalışır. Bu yüzden ikisini aynı sayıyla birlikte kullanırsan o tek pikselde iki kural çakışır. Tek yönü seçip ona bağlı kalmak bu dertten kurtarır.

Sık hataViewport satırı yok: telefon @media kurallarını da yanlış uygular

Ne zaman çıkar? <meta name="viewport"> satırını unutunca. Bilgisayarda her şey normal görünür; sorun yalnız gerçek telefonda ya da DevTools'un telefon görünümünde çıkar.

Ders 19'da bu satır olmadan yazıların küçüldüğünü görmüştün. Medya sorguları işin içine girince bir sorun daha çıkar. Aşağıdaki iki görüntü, üstteki .duzen örneğine bir başlık eklenmiş sayfanın başsız Chromium'da 390 piksellik bir telefon taklidiyle çekilmiş gerçek hâlleri:

Viewport satırı olmayan sayfa telefonda: Koridor rehberi başlığı ve iki sütunlu düzen 980 piksele göre çizilip küçültülmüş, yazılar okunamayacak kadar ufak.
Viewport satırı yok
Viewport satırı olan sayfa telefonda: büyük Koridor rehberi başlığı, kutular tek sütun hâlinde alt alta, yazılar okunur boyutta.
Viewport satırı var
Şekil 21.9 Aynı sayfa, aynı telefon genişliği. Tek fark <head> içindeki viewport satırı.

İlkinde telefon sayfayı 980 piksel genişliğinde sandı. 980, 500'den büyük olduğu için @media (min-width: 500px) kuralı telefonda da devreye girdi ve iki sütun açıldı; sonra bütün sayfa telefonun ekranına sığsın diye küçültüldü. İkincisinde görüntü alanı 390 piksel; iki sütun kuralı uygulanmadı, yazılar gerçek boyutunda. Yani viewport satırı yoksa telefon için yazdığın bütün medya sorguları boşa gider.

Nasıl düzelir? Her HTML dosyasının <head> kısmına viewport satırını yaz. VS Code'da ! ile gelen Emmet iskeletinde (Ders 19) bu satır zaten vardır; iskeleti elle yazıyorsan atlama.

Mobil öncelikli yazmak ve kırılım noktaları

İki yol var. Önce bilgisayar için yazıp max-width sorgularıyla telefonda bozulanları geri alabilirsin. Ya da önce telefon için yazıp min-width sorgularıyla geniş ekranlara özellik eklersin. İkincisine mobil öncelikli yazmak denir ve bugün yaygın olan yol budur.

Neden? Telefondaki düzen genelde en sadesidir: tek sütun, alt alta kutular. Bu sade düzeni temel CSS yaparsan telefon için hiçbir şeyi geri almak zorunda kalmazsın. Ekran genişledikçe sütun, boşluk, yan yana dizilim eklersin. Kurallar üst üste biner: 900 pikselde hem 600 hem 900 sorgusu çalışır, sonra yazılan kazanır.

0 600 px 900 px 1200 px temel CSS: her genişlikte geçerli, tek sütun @media (min-width: 600px) iki sütun ekler @media (min-width: 900px) üç sütun ekler
Şekil 21.10 Mobil öncelikli bir sayfanın kırılım noktaları: temel CSS her genişlikte geçerli, sorgular geniş ekranlara ekleme yapar.

Sayıları nereden bulacaksın? Telefon modellerinin listesini ezberleme; her yıl yenileri çıkıyor. Sayfanı dar bir ekranda aç, yavaşça genişlet ve düzenin çirkinleştiği ya da boşa yer kaldığı genişliği bul. O sayı senin kırılım noktandır. Başlangıç için 600, 900 ve 1200 piksel çoğu sayfada iş görür.

Sık hata@media kuralı hiç çalışmıyor: sıra yanlış

Ne zaman çıkar? Medya sorgusunu temel kuralın önüne yazınca. Ekran ne kadar geniş olursa olsun ızgara tek sütun kalır.

CSS
@media (min-width: 600px) {    .izgara {        grid-template-columns: repeat(3, 1fr);    }}.izgara {    display: grid;    grid-template-columns: 1fr;    gap: 8px;}

@media bloğu seçicilerin özgüllüğünü değiştirmez. İki kuralın seçicisi de .izgara; Ders 20'deki sıra kuralına göre sonra yazılan kazanır. Geniş ekranda da sondaki grid-template-columns: 1fr kazanır. Izgarayı DevTools'ta incele:

DevTools Styles bölmesi. En üstte deneme.html satır 20'deki izgara kuralı: display grid, grid-template-columns 1fr, gap 8px. Altında deneme.html satır 16'daki @media (min-width: 600px) kuralı; içindeki grid-template-columns: repeat(3, 1fr) satırının üstü çizili.
Şekil 21.11 Geniş bir pencerede DevTools'un Styles bölmesi: kazanan kural üstte, @media içindeki satırın üstü çizili.

Medya sorgusunun koşulu doğru, içindeki kural da devreye girdi; ama aynı özgüllükte, daha sonra yazılmış bir kural onu ezdi. Üstü çizili satır "bu kural geçerliydi ama başka bir kural onu ezdi" demek.

Nasıl düzelir? Temel kuralları üste, medya sorgularını dosyanın sonuna yaz; birden çok sorgu varsa küçükten büyüğe sırala.

DevTools'ta telefon genişliği

Her denemede telefonu eline alman gerekmez. Chrome, Edge ve Brave'de:

  1. F12 ile DevTools'u aç.
  2. DevTools'un sol üst köşesinde, seçme okunun yanındaki telefon-tablet simgesine tıkla ya da Ctrl+Shift+M'ye bas. İngilizce adı Toggle device toolbar, Türkçe arayüzde Cihaz araç çubuğunu aç/kapat.
  3. Sayfanın üstünde Dimensions: Responsive (Türkçe: Boyutlar: Duyarlı) yazan bir çubuk çıkar. Yanındaki kutulara genişlik ve yükseklik yaz: telefon için 390, bilgisayar için 1280 dene. Çubuktaki listeden hazır telefon modellerini de seçebilirsin.
  4. Kırılım noktalarını görmek için çubuğun sağındaki ⋮ menüsünden (More options, Türkçe: Diğer seçenekler) Show media queries (Medya sorgularını göster) seç. Sorguların renkli şeritler hâlinde çıkar; bir şeride tıklarsan genişlik o noktaya ayarlanır.

Sınır değerini unutma: 600 piksellik bir kırılım noktan varsa 599 ve 600'ü ayrı ayrı dene. Telefon görünümü gerçek telefonun birebir aynısı değildir ama yerleşim hatalarının hemen hepsini yakalar. Tarayıcının arayüzü güncellemelerle değişebilir; simge ya da menü adı farklıysa DevTools açıkken Ctrl+Shift+P ile komut menüsünü aç ve "device" yaz.

CSS değişkenleri

Bir sayfada aynı mavi rengi başlıkta, kenarlıkta, düğmede ve rozette, toplam dokuz yerde kullandığını düşün. Renk değişecek olsa dokuz yeri tek tek bulman gerekir; birini unutursan sayfa yamalı görünür. JavaScript'te bu sorunu değişkenle çözmüştün (Ders 1). CSS'in de değişkenleri var.

deneme.html · <style> içine
:root {    --vurgu: #1e88e5;    --zemin: #e3f2fd;    --bosluk: 12px;}.panel {    margin-bottom: var(--bosluk);    padding: var(--bosluk);    background-color: var(--zemin);    border: 2px solid var(--vurgu);    border-radius: 8px;}.panel h3 {    margin: 0 0 8px;    color: var(--vurgu);}.dugme {    padding: 6px 14px;    background-color: var(--vurgu);    color: white;    border: 0;    border-radius: 6px;}

Mavi renk artık tek bir yerde yazılı: :root içinde. Kenarlık, başlık ve düğme onu adıyla kullanıyor. İki kural var:

  • Değişken -- ile başlayan bir özellik gibi tanımlanır: --vurgu: #1e88e5;. Ad sende; anlamlı ve İngilizce harfli seç.
  • Kullanırken var() içine yazılır: var(--vurgu).

:root sayfanın en dış öğesi olan <html>'i seçer. CSS değişkenleri de renk ve yazı tipi gibi içerideki öğelere kalıtımla geçer; bu yüzden :root'ta tanımlanan değişken sayfanın her yerinde kullanılabilir.

Son cümle çok işe yarar: aynı değişkeni bir bölümde yeniden tanımlarsan o bölümün içi yeni değeri kullanır. Kırmızı takım panelinin renkleri için tek bir sınıf yeter:

deneme.html · <style> içine
.kirmizi {    --vurgu: #e53935;    --zemin: #ffebee;}
deneme.html · <body> içine
<div class="panel">    <h3>Mavi takım</h3>    <button class="dugme">Hazırım</button></div><div class="panel kirmizi">    <h3>Kırmızı takım</h3>    <button class="dugme">Hazırım</button></div>
127.0.0.1:5500/deneme.html

Panelin, başlığın ve düğmenin CSS'ine dokunmadık; yalnız .kirmizi içinde iki değişkeni yeniden tanımladık. Panel kırmızı oldu, içindeki başlık ve düğme de değişkeni kalıtımla aldığı için onunla birlikte değişti.

Değişkenler medya sorgularıyla da iyi anlaşır. Geniş ekranda boşlukları büyütmek için her kuralı ayrı ayrı yeniden yazmazsın, yalnız değişkeni değiştirirsin:

CSS
@media (min-width: 700px) {    :root {        --bosluk: 24px;    }}

Okuduğun bu sayfanın sağ üstündeki tema düğmesi de böyle çalışır: açık temaya geçince yalnız renk değişkenlerinin değerleri değişir, kuralların hiçbiri yeniden yazılmaz.

Sık hata--Vurgu is not defined: değişken adını yanlış yazmak

Ne zaman çıkar? var() içindeki adı tanımladığından farklı yazınca. CSS değişkenlerinde büyük-küçük harf farklıdır: --Vurgu ile --vurgu iki ayrı değişkendir.

CSS
:root {    --vurgu: #1e88e5;}.dugme {    padding: 6px 14px;    background-color: var(--Vurgu);    color: white;    border: 0;    border-radius: 6px;}
127.0.0.1:5500/deneme.html

Düğme kayboldu: arka plan şeffaf kaldı, beyaz yazı beyaz sayfada görünmüyor. Bulunamayan değişken satırı silmez, özelliği sıfırlar; arka plan rengi için sıfır "renk yok" demektir. Tarayıcının düğmelere verdiği gri zemin de gelmedi, çünkü senin satırın onu hâlâ eziyor. DevTools'ta düğmeyi seçince var(--Vurgu) içindeki ad soluk görünür; üzerine gelince --Vurgu is not defined (Türkçe arayüzde --Vurgu tanımlı değil) yazar.

Nasıl düzelir? Adı tanımladığın gibi yaz: var(--vurgu). VS Code var(-- yazınca dosyadaki değişkenleri önerir; öneriden seçmek yazım hatasını önler.

var()'ı tamamen unutup background-color: --vurgu; yazmak ise başka bir hatadır. Tarayıcı bu satırı geçersiz sayıp atar; düğme tarayıcının varsayılan açık gri zeminine döner ve beyaz yazı yine zor okunur. DevTools bu kez satırın üstünü çizip başına ⚠ koyar: Invalid property value (Geçersiz özellik değeri).

Uygulama: duyarlı şampiyon kartları

Şimdi hepsini bir araya getiriyorsun. Altı şampiyon kartı olan bir sayfa yapacaksın:

  • Üstte sayfa adı ve bir menü. Telefonda alt alta, geniş ekranda yan yana; kaydırınca tepede kalır.
  • Kartlar ızgarada: telefonda tek sütun, tablette iki, bilgisayarda üç. Bunu tek bir grid satırı yapacak.
  • Her kartta büyük harfli bir resim alanı (şampiyon resmi yerine kendi yer tutucumuz), ad, rol ve üç değerlik bir satır. Bazı kartların köşesinde rozet.
  • Renkler değişkenlerde; her rolün kendi rengi var.

Adım 1: HTML

C:\kod\sampiyon-kartlari klasöründe index.html dosyası oluştur. Ders 19'daki semantik etiketleri kullanıyoruz: header, nav, main, article, footer. İlk kartı yaz, sonra kopyalayıp öbür beşini ondan türet; harfi, adı, rolü ve sayıları değiştir. Rozet yalnız Ahri ile Jinx'in kartında var.

index.html
<!doctype html><html lang="tr"><head>    <meta charset="utf-8">    <meta name="viewport" content="width=device-width, initial-scale=1">    <title>Şampiyon Kartları</title>    <link rel="stylesheet" href="stil.css"></head><body>    <header class="ust">        <h1 class="logo">Şampiyon Kartları</h1>        <nav class="menu">            <a href="#">Hepsi</a>            <a href="#">Büyücü</a>            <a href="#">Savaşçı</a>            <a href="#">Nişancı</a>        </nav>    </header>     <main class="izgara">        <article class="kart buyucu">            <span class="rozet">Yeni</span>            <div class="resim">A</div>            <h2>Ahri</h2>            <p class="rol">Büyücü · Orta koridor</p>            <ul class="degerler">                <li><b>100</b> can</li>                <li><b>300</b> altın</li>                <li><b>1</b> seviye</li>            </ul>        </article>        <article class="kart savasci">            <div class="resim">G</div>            <h2>Garen</h2>            <p class="rol">Savaşçı · Üst koridor</p>            <ul class="degerler">                <li><b>620</b> can</li>                <li><b>500</b> altın</li>                <li><b>3</b> seviye</li>            </ul>        </article>        <article class="kart nisanci">            <span class="rozet">Popüler</span>            <div class="resim">J</div>            <h2>Jinx</h2>            <p class="rol">Nişancı · Alt koridor</p>            <ul class="degerler">                <li><b>580</b> can</li>                <li><b>450</b> altın</li>                <li><b>2</b> seviye</li>            </ul>        </article>        <article class="kart buyucu">            <div class="resim">L</div>            <h2>Lux</h2>            <p class="rol">Büyücü · Destek</p>            <ul class="degerler">                <li><b>540</b> can</li>                <li><b>350</b> altın</li>                <li><b>2</b> seviye</li>            </ul>        </article>        <article class="kart savasci">            <div class="resim">Y</div>            <h2>Yasuo</h2>            <p class="rol">Savaşçı · Orta koridor</p>            <ul class="degerler">                <li><b>590</b> can</li>                <li><b>400</b> altın</li>                <li><b>2</b> seviye</li>            </ul>        </article>        <article class="kart nisanci">            <div class="resim">T</div>            <h2>Teemo</h2>            <p class="rol">Nişancı · Üst koridor</p>            <ul class="degerler">                <li><b>530</b> can</li>                <li><b>250</b> altın</li>                <li><b>1</b> seviye</li>            </ul>        </article>    </main>     <footer class="alt">Ders 21 · CSS yerleşim çalışması</footer></body></html>

Kartlarda iki sınıf var: kart bütün kartların ortak görünüşü, buyucu / savasci / nisanci ise rol rengi için. Bir öğeye boşlukla ayırarak birden çok sınıf verebilirsin (Ders 20). Resim alanında gerçek bir şampiyon resmi yok; oyun görsellerinin telif hakkı var, yer tutucu olarak adın baş harfini kullanıyoruz.

Menüdeki bağlantıların href="#" değeri şimdilik hiçbir yere götürmez, yalnız sayfanın başını gösterir. Bu menüye Ders 22'de JavaScript ile iş vereceksin: Büyücü'ye basınca yalnız büyücü kartları kalacak.

Adım 2: değişkenler ve temel kurallar

Aynı klasörde stil.css dosyasını oluştur. Önce temel: kutu hesabı, renk ve boşluk değişkenleri, sayfanın zemini.

stil.css
* {    box-sizing: border-box;} :root {    --zemin: #0f1020;    --kart: #1b1d33;    --cizgi: #34375a;    --yazi: #ececf4;    --soluk: #a3a6c2;    --bosluk: 16px;} body {    margin: 0;    background-color: var(--zemin);    color: var(--yazi);    font-family: "Segoe UI", Arial, sans-serif;}

Bütün renkler ve boşluk ölçüsü değişkenlerde. Sayfayı açık renklere çevirmek istersen yalnız :root'taki renk değerlerini değiştirmen yeter.

Adım 3: üst çubuk

stil.css
/* Üst çubuk: telefonda alt alta, kaydırınca yukarıda kalır */.ust {    position: sticky;    top: 0;    z-index: 10;    display: flex;    flex-direction: column;    gap: 10px;    padding: var(--bosluk);    background-color: var(--kart);    border-bottom: 1px solid var(--cizgi);} .logo {    margin: 0;    font-size: 22px;} .menu {    display: flex;    flex-wrap: wrap;    gap: 8px;} .menu a {    padding: 6px 12px;    border: 1px solid var(--cizgi);    border-radius: 999px;    color: var(--yazi);    text-decoration: none;}
  • .ust yapışkan (sticky, top: 0) ve z-index: 10 ile kartların üstünde kalıyor; kartlar rozet için relative olacağı için bu satır şart.
  • Mobil öncelikli: temel kural telefon için, bu yüzden flex-direction: column ile logo üstte, menü altta. Geniş ekran ayarını en sona yazacağız.
  • .menu bağlantıları yan yana dizer; dar telefonda sığmazsa flex-wrap: wrap alt satıra geçirir.

Adım 4: ızgara ve kart

stil.css
/* Kart ızgarası: sığdığı kadar sütun */.izgara {    display: grid;    grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));    gap: var(--bosluk);    max-width: 1100px;    margin: 0 auto;    padding: var(--bosluk);} .kart {    position: relative;    padding: 16px;    background-color: var(--kart);    border: 1px solid var(--cizgi);    border-radius: 14px;    overflow-wrap: break-word;} /* Rol renkleri: her kart kendi --renk değerini taşır */.buyucu {    --renk: #a78bfa;}.savasci {    --renk: #f87171;}.nisanci {    --renk: #fbbf24;} .rozet {    position: absolute;    top: 24px;    right: 24px;    padding: 2px 10px;    background-color: var(--zemin);    color: var(--renk);    font-size: 13px;    font-weight: bold;    border-radius: 999px;}
  • Izgaranın tamamı tek satır: repeat(auto-fit, minmax(260px, 1fr)). Telefonda 260 piksellik iki sütun sığmaz, tek sütun kalır; bilgisayarda üç sütun açılır. Tek bir @media yazmadık.
  • max-width: 1100px ve margin: 0 auto çok geniş ekranda ızgarayı ortada tutar; kartlar sonsuza kadar uzamaz.
  • .kart rozet için position: relative; uzun bir ad kartı taşırmasın diye overflow-wrap: break-word. Izgara sütunları en az 260 piksel olduğu için burada min-width: 0 gerekmez.
  • --renk değişkenini :root'a değil rol sınıflarına koyduk. Her kart kendi rengini taşıyor; rozet, resim alanı ve rol yazısı var(--renk) ile hangi kartta olduklarına göre renk alıyor.
stil.css
.resim {    display: flex;    justify-content: center;    align-items: center;    height: 120px;    margin-bottom: 12px;    background-color: var(--renk);    color: var(--zemin);    font-size: 56px;    font-weight: bold;    border-radius: 10px;} .kart h2 {    margin: 0;} .rol {    margin: 4px 0 12px;    color: var(--renk);} .degerler {    display: flex;    gap: 8px;    margin: 0;    padding: 0;    list-style: none;} .degerler li {    flex: 1;    padding: 8px;    background-color: var(--zemin);    border-radius: 8px;    color: var(--soluk);    text-align: center;} .degerler b {    display: block;    color: var(--yazi);    font-size: 18px;}
  • .resim harfi flex ile tam ortalıyor: yükseklik verdik, bu yüzden dikey ortalama çalışıyor.
  • .degerler listesi flex; her li flex: 1 ile satırı eşit paylaşıyor. list-style: none ve padding: 0 listenin madde işaretlerini ve iç boşluğunu kaldırıyor.
  • b normalde satır içi; display: block ile sayıyı kendi satırına alıp yazının üstüne koyduk.

Adım 5: alt bilgi ve geniş ekran

stil.css
.alt {    padding: var(--bosluk);    color: var(--soluk);    text-align: center;} /* 700 px ve daha geniş ekranlar */@media (min-width: 700px) {    :root {        --bosluk: 24px;    }    .ust {        flex-direction: row;        justify-content: space-between;        align-items: center;    }}

Medya sorgusu dosyanın sonunda. 700 piksel ve üstünde iki şey değişiyor: --bosluk kullanan boşluklar (üst çubuğun ve ızgaranın iç boşluğu, kartların arası, alt bilgi) tek bir değişkenle 16 pikselden 24'e çıkıyor ve üst çubuk yan yana diziliyor: logo solda, menü sağda.

Sonuç: telefonda ve bilgisayarda

Dosyaları kaydet ve index.html'i Live Server ile aç. Sonra DevTools'un telefon görünümünde genişliği 390 ve 1280 yaparak bak. Aşağıdaki görüntüler bu sayfanın başsız Chromium'da çekilmiş gerçek hâlleri:

Koyu zeminli sayfa telefonda. Üstte Şampiyon Kartları başlığı, altında Hepsi, Büyücü, Savaşçı, Nişancı düğmeleri. Altında tek sütun hâlinde Ahri kartı (mor A harfi, Yeni rozeti, 100 can, 300 altın, 1 seviye), Garen kartı (kırmızı G harfi) ve Jinx kartının başı (sarı, Popüler rozeti).
Şekil 21.12 Telefonda (390 piksel): menü başlığın altında, kartlar tek sütun.
Aynı sayfa geniş ekranda. Solda Şampiyon Kartları, sağda menü düğmeleri. Altında iki satır hâlinde üçer kart: Ahri, Garen, Jinx ve Lux, Yasuo, Teemo. Ahri'nin kartında Yeni, Jinx'in kartında Popüler rozeti var.
Şekil 21.13 Bilgisayarda (1280 piksel): başlık ve menü aynı satırda, kartlar üç sütun.

Sayfa çalıştıysa sıra bozmakta. Her denemeden sonra geri al:

  • Ekranı 700 pikselin altına ve üstüne getirip üst çubuğun dizilişinin değiştiği anı yakala. 699 ve 700'ü ayrı ayrı dene.
  • z-index: 10 satırını sil, telefon görünümünde sayfayı kaydır: kartlar başlığın üstünden geçer.
  • viewport satırını sil; telefon görünümünde listeden bir telefon modeli seçip sayfayı yenile: üç sütunlu, minicik bir sayfa görürsün.
  • Ahri'nin adını boşluksuz 40 harf yap: kart taşmamalı, ad satırlara bölünmeli.
  • minmax(260px, 1fr) yerine minmax(200px, 1fr) yaz: bilgisayarda kaç sütun oldu?

Görevler

Görevleri deneme.html'de yap; her görevde <style> ve <body> içini baştan yaz. Her görevde bitmiş sayfanın canlı önizlemesi var: hedefin o, kendi sonucunu onunla karşılaştır. Önce kendin dene, takılırsan ipuçlarını sırayla aç. Her çözümün sonunda bir sınır denemesi var; DevTools'un telefon görünümünde genişliği değiştirerek onu da yap.

Görev 1

Yetenek çubuğu

Q, W, E, R, D, F tuşlarını gösteren altı kare kutudan bir çubuk yap:

  • Çubuk 100 piksel yüksekliğinde, koyu renkli.
  • Kutular 44 piksellik kareler; yan yana, aralarında 8 piksel boşluk.
  • Kutular çubuğun hem yatayda hem dikeyde tam ortasında; harfler de kutuların ortasında.

Bitince sayfan şöyle görünmeli:

127.0.0.1:5500/deneme.html
İpucu 1

İki ayrı ortalama var: kutuları çubuğun ortasına, harfleri kutuların ortasına. İkisi de aynı üç satırlık kalıpla yapılır.

İpucu 2

Çubuğa display: flex, justify-content: center, align-items: center ve gap ver. Her kutu da kendi içindeki harf için küçük bir flex kapsayıcı olabilir.

Çözümü göster
deneme.html · <style> içine
.cubuk {    display: flex;    justify-content: center;    align-items: center;    gap: 8px;    height: 100px;    background-color: #263238;    border-radius: 12px;}.tus {    display: flex;    justify-content: center;    align-items: center;    width: 44px;    height: 44px;    background-color: #455a64;    color: white;    font-weight: bold;    border-radius: 8px;}
deneme.html · <body> içine
<div class="cubuk">    <div class="tus">Q</div>    <div class="tus">W</div>    <div class="tus">E</div>    <div class="tus">R</div>    <div class="tus">D</div>    <div class="tus">F</div></div>

Sınır denemesi: DevTools'un telefon görünümünde genişliği 320 yap. Altı kutu ve beş boşluk 304 piksel eder ama body'nin kenar boşluklarından sonra 296 piksel kalıyor; flex kutuları daraltır. Bir kutunun üzerine gelince DevTools 42.67 × 44 yazar: kareler hafifçe dikdörtgen oldu. Kutuları 60 piksel yaparsan 42.67 × 60 piksellik ince dikdörtgenler çıkar. Kareler bozulmasın istiyorsan çubuğa flex-wrap: wrap ekle ve height yerine min-height: 100px yaz: sığmayan kutular alt satıra geçer, iki satır 100 piksele sığmazsa çubuk da uzar (60 piksellik kutularda 128 piksel olur).

Görev 2

Dükkân düğmesine bildirim balonu

"Dükkân" yazan mavi bir düğme yap. Düğmenin sağ üst köşesine, köşeden 8 piksel yukarı ve 8 piksel sağa taşan, 22 piksellik kırmızı yuvarlak bir balon koy; içinde 3 yazsın.

Bitince sayfan şöyle görünmeli:

127.0.0.1:5500/deneme.html
İpucu 1

Balon düğmenin köşesine göre yerleşecek. Hangi öğe referans olmalı, hangi öğe iğnelenmeli?

İpucu 2

Düğmeye position: relative, balona position: absolute ver. Dışarı taşması için top ve right değerleri eksi olabilir: top: -8px.

İpucu 3

Balonu yuvarlak yapmak için genişlik ve yükseklik eşit olmalı, border-radius: 50% kare kutuyu daireye çevirir. İçindeki sayıyı ortalamak için yine flex.

Çözümü göster
deneme.html · <style> içine
.dukkan {    position: relative;    padding: 10px 18px;    background-color: #1e88e5;    color: white;    font-size: 16px;    border: 0;    border-radius: 8px;}.balon {    position: absolute;    top: -8px;    right: -8px;    display: flex;    justify-content: center;    align-items: center;    width: 22px;    height: 22px;    background-color: #e53935;    color: white;    font-size: 12px;    font-weight: bold;    border-radius: 50%;}
deneme.html · <body> içine
<button class="dukkan">Dükkân <span class="balon">3</span></button>

Sınır denemesi: balona 3 yerine 1024 yaz. Dört rakam 22 piksellik daireye sığmaz, yazı dairenin iki yanından taşar. Sabit width yerine min-width: 22px ve padding: 0 6px yaz, border-radius: 999px yap: tek rakamda yine yuvarlak kalır, sayı büyüyünce hap biçiminde uzar. Bir de düğmenin position: relative satırını silip balonun nereye uçtuğuna bak: sayfanın sağ üst köşesine gider, bir kısmı da ekranın dışında kalır.

Görev 3

Maç sonu skor tablosu

Grid ile dört sütunlu bir skor tablosu yap: Oyuncu, Öldürme, Ölüm, Asist. İlk satır koyu zeminli başlık satırı; altında Deniz (7, 2, 9), Ece (3, 4, 12) ve Mert (10, 6, 1). Oyuncu sütunu öteki sütunların iki katı genişlikte olsun ve tablo ekranın genişliğine uysun; sabit piksel kullanma. Tabloyu <table> ile değil, 16 tane div ile kur. Gerçek bir sayfada tablo verisi için <table> doğru seçimdir (Ders 19); burada grid'in hücreleri nasıl doldurduğunu görmek için div kullanıyoruz.

Bitince sayfan şöyle görünmeli:

127.0.0.1:5500/deneme.html
İpucu 1

Dört sütunun genişliklerini fr ile yaz: biri iki pay, üçü birer pay.

İpucu 2

Üç tane 1fr yazmak yerine repeat kullanabilirsin. grid-template-columns'a iki parça yan yana yazılabilir: önce tek bir değer, sonra bir repeat.

Çözümü göster
deneme.html · <style> içine
.tablo {    display: grid;    grid-template-columns: 2fr repeat(3, 1fr);    gap: 4px;}.tablo div {    padding: 8px;    background-color: #eceff1;}.tablo .baslik {    background-color: #37474f;    color: white;    font-weight: bold;}
deneme.html · <body> içine
<div class="tablo">    <div class="baslik">Oyuncu</div>    <div class="baslik">Öldürme</div>    <div class="baslik">Ölüm</div>    <div class="baslik">Asist</div>    <div>Deniz</div>    <div>7</div>    <div>2</div>    <div>9</div>    <div>Ece</div>    <div>3</div>    <div>4</div>    <div>12</div>    <div>Mert</div>    <div>10</div>    <div>6</div>    <div>1</div></div>

.tablo div seçicisi tablonun içindeki bütün div'leri seçer; .tablo .baslik daha özgül olduğu için başlıkların zemini koyu kalır. Grid div'leri soldan sağa dört sütuna doldurur, beşinci div kendiliğinden ikinci satıra geçer.

Sınır denemesi: DevTools'ta genişliği 360 yap. Sütunlar tam 2'ye 1 olmaz; "Öldürme" sözcüğü kendi sütununu genişletir, çünkü 1fr içerikten dar olamaz. Şimdi Mert'in adını SihirdarVadisininEnHızlıYasuoOyuncusu yap: tablo ekrandan taşar. Düzeltmek için ilk sütunu minmax(0, 2fr) yap ("en az 0, en çok 2 pay": içerik bu sütunu şişiremez) ve .tablo div'e overflow-wrap: break-word ekle. Uzun ad kendi sütununda satırlara bölünür, sayı sütunları ve başlıklar bozulmaz.

Görev 4

Maç özeti: telefonda alt alta, bilgisayarda yan yana

İki takım paneli (Mavi takım: 24 öldürme, 7 kule; Kırmızı takım: 15 öldürme, 3 kule) ve altında bir "Ayrıntılar" paragrafı ("Maç süresi 31 dakika. Maçın oyuncusu: Ahri.") yap. CSS'i mobil öncelikli yaz:

GenişlikPanellerAyrıntılar paragrafı
500 pikselden darAlt altaGizli
500 piksel ve üstüYan yana, eşit genişlikte, aralarında 16 pikselGörünür

Bitince sayfan şöyle görünmeli:

127.0.0.1:5500/deneme.html

Önizleme, ekranının genişliğine göre iki hâlden birini gösterir: bilgisayarda yan yana, telefonda alt alta.

İpucu 1

Temel kurallar telefon içindir: paneller alt alta, paragraf display: none. Geniş ekranın farklarını @media (min-width: 500px) içine yaz.

İpucu 2

Panelleri flex kapsayıcıya koy. Telefonda flex-direction: column, geniş ekranda row. Eşit genişlik için panellere flex: 1; paragrafı göstermek için display: block.

Çözümü göster
deneme.html · <style> içine
.ozet {    display: flex;    flex-direction: column;    gap: 16px;}.takim {    padding: 12px;    border-radius: 8px;}.takim h2 {    margin: 0 0 4px;    font-size: 20px;}.takim p {    margin: 0;}.mavi {    background-color: #e3f2fd;}.kirmizi {    background-color: #ffebee;}.ayrinti {    display: none;    margin: 16px 0 0;    padding: 12px;    background-color: #f5f5f5;    border-radius: 8px;}@media (min-width: 500px) {    .ozet {        flex-direction: row;    }    .takim {        flex: 1;    }    .ayrinti {        display: block;    }}
deneme.html · <body> içine
<div class="ozet">    <section class="takim mavi">        <h2>Mavi takım</h2>        <p>Öldürme: 24 · Kule: 7</p>    </section>    <section class="takim kirmizi">        <h2>Kırmızı takım</h2>        <p>Öldürme: 15 · Kule: 3</p>    </section></div><p class="ayrinti">Maç süresi 31 dakika. Maçın oyuncusu: Ahri.</p>

Sınır denemesi: DevTools'ta genişliği 499 yap; paneller alt alta, paragraf gizli olmalı. 500 yap; paneller yan yana (her biri 230 piksel), paragraf görünür olmalı. min-width sınırın kendisini de kapsar.

Görev 5

Eşya dükkânı sayfası

Koyu temalı bir eşya dükkânı sayfası yap. Bu derste öğrendiğin hemen her şey gerekecek:

  • Renkler ve boşluk :root'ta dört değişkende: --zemin, --kart, --altin (altın sarısı) ve --bosluk (12 piksel).
  • Üstte bir altın çubuğu: solda "Dükkân", sağda altın renginde "1250 altın". Sayfa kayınca çubuk tepede kalsın.
  • Altı eşya kartı (Uzun Kılıç 350, Büyü Kitabı 435, Kalkan 400, Can İksiri 50, Çizme 300, Asa 875 altın) bir ızgarada. Her kart en az 140 piksel; ekrana kaç kart sığıyorsa o kadar sütun.
  • Uzun Kılıç'ın kartının sağ üst köşesinde "İndirim" rozeti.
  • Sağ altta, sayfa kayarken yerinden kımıldamayan altın renkli bir "Sepet" düğmesi; ekranın kenarlarına uzaklığı --bosluk kadar.
  • 700 piksel ve üstünde --bosluk ile yazılan bütün boşluklar 20 piksel olsun; bunu tek bir satırla yap.

Sınırlar: 320 piksellik ekranda iki sütun olmalı. Sayfayı kaydırınca rozetli kart altın çubuğunun üstüne çıkmamalı, Sepet düğmesi sağ altta kalmalı. Bir eşyanın adı boşluksuz 30 harf olsa bile kart taşmamalı.

Bitince sayfan şöyle görünmeli:

127.0.0.1:5500/deneme.html

Önizlemenin içini kaydır: çubuk tepede kalmalı, rozetli kart çubuğun altından geçmeli, Sepet düğmesi sağ altta yerinde durmalı.

İpucu 1

Parçalara böl: önce değişkenler ve body, sonra altın çubuğu (flex + space-between), sonra ızgara (repeat(auto-fit, minmax(…))), sonra kart ve rozet (relative + absolute), sonra Sepet düğmesi (fixed), en son medya sorgusu.

İpucu 2

Çubuk sticky ve top: 0 olacak. Kartlar rozet yüzünden relative; kaydırınca kartlar çubuğun üstüne çıkar. Bu dersin hangi hata kartıydı?

İpucu 3

Tek satırla bütün boşlukları değiştirmek için @media içinde :root'taki --bosluk değişkenini yeniden tanımla. Boşluk kullanan her kural var(--bosluk) ile yazılmış olmalı.

Çözümü göster
deneme.html · <style> içine
:root {    --zemin: #10131c;    --kart: #1c2130;    --altin: #f2c94c;    --bosluk: 12px;}body {    margin: 0;    background-color: var(--zemin);    color: white;    font-family: "Segoe UI", Arial, sans-serif;}.cubuk {    position: sticky;    top: 0;    z-index: 10;    display: flex;    justify-content: space-between;    align-items: center;    padding: var(--bosluk);    background-color: var(--kart);}.altin {    color: var(--altin);    font-weight: bold;}.esyalar {    display: grid;    grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));    gap: var(--bosluk);    padding: var(--bosluk);}.esya {    position: relative;    padding: var(--bosluk);    background-color: var(--kart);    border-radius: 10px;    overflow-wrap: break-word;}.esya h3 {    margin: 20px 0 8px;}.esya p {    margin: 0;    color: var(--altin);}.indirim {    position: absolute;    top: 8px;    right: 8px;    padding: 2px 8px;    background-color: var(--altin);    color: var(--zemin);    font-size: 12px;    font-weight: bold;    border-radius: 999px;}.sepet {    position: fixed;    right: var(--bosluk);    bottom: var(--bosluk);    padding: 10px 18px;    background-color: var(--altin);    color: var(--zemin);    font-weight: bold;    border: 0;    border-radius: 999px;}@media (min-width: 700px) {    :root {        --bosluk: 20px;    }}
deneme.html · <body> içine
<header class="cubuk">    <span>Dükkân</span>    <span class="altin">1250 altın</span></header><main class="esyalar">    <div class="esya">        <span class="indirim">İndirim</span>        <h3>Uzun Kılıç</h3>        <p>350 altın</p>    </div>    <div class="esya">        <h3>Büyü Kitabı</h3>        <p>435 altın</p>    </div>    <div class="esya">        <h3>Kalkan</h3>        <p>400 altın</p>    </div>    <div class="esya">        <h3>Can İksiri</h3>        <p>50 altın</p>    </div>    <div class="esya">        <h3>Çizme</h3>        <p>300 altın</p>    </div>    <div class="esya">        <h3>Asa</h3>        <p>875 altın</p>    </div></main><button class="sepet">Sepet</button>

* { box-sizing: border-box; } kuralı deneme dosyanın iskeletinde zaten var, bu yüzden çözümde yeniden yazmadık. Sepet düğmesine z-index gerekmedi: o da kartlar gibi konumlanmış ama HTML'de onlardan sonra geliyor, bu yüzden üstte çizilir.

Sınır denemeleri:

  • 320 piksel: kenar boşluklarından sonra 296 piksel kalır, 140 piksellik iki kart ve 12 piksellik boşluk 292 eder: iki sütun. 630 pikselde dört sütun, 1280 pikselde altı sütun açılır. Küçük bir telefonda görevin başındaki önizleme 320 pikselden dar kalabilir; orada tek sütun görmen doğru.
  • 700 piksel: --bosluk 20 olur; kartların arası, çubuğun iç boşluğu ve Sepet düğmesinin kenarlara uzaklığı birlikte büyür. 699'da yine 12.
  • Kaydırma: Altı kart çoğu ekrana sığar ve sayfa kaymaz. DevTools'un telefon görünümünde genişliği 320, yüksekliği 300 yap ve kaydır: çubuk tepede, Sepet düğmesi sağ altta kalmalı. Sonra z-index: 10 satırını sil ve yeniden kaydır: kartlar (İndirim kartı da) çubuğun üstünden geçer. Satırı geri koy.
  • Uzun ad: Kalkan'ın adını EfsaneviYıldızKırıcıBüyükKalkan yap. overflow-wrap: break-word sayesinde ad birkaç satıra bölünür, kart taşmaz.
  • Tek eşya: beş kartı sil. auto-fit boş sütunları kaldırır ve tek kart bütün satırı kaplar. Kartın 140 piksel civarında dar kalmasını istersen auto-fit yerine auto-fill yaz: boş sütunlar yerinde kalır, kart onların yanında küçük durur.

Kendini sına

Soru 1

Bir <a> etiketine height: 40px verdin ama yüksekliği değişmedi. Neden? İki farklı çözüm söyle.

Cevabı göster

<a> satır içi (inline) bir öğedir ve satır içi öğelerde width ile height işlemez; DevTools da "The display: inline property prevents height from having an effect." der. Çözüm 1: display: inline-block; bağlantı satırda kalır ama yükseklik alır. Çözüm 2: display: block; bağlantı kendi satırını açar. Bağlantı bir flex kapsayıcının çocuğuysa (menüdeki gibi) flex öğesi olduğu için yükseklik zaten işler.

Soru 2

Aşağıdaki kapsayıcıda öğeler nasıl dizilir? justify-content: space-between görünür bir şey yapar mı?

CSS
.takim {    display: flex;    flex-direction: column;    justify-content: space-between;}
Cevabı göster

Öğeler alt alta dizilir; column ana ekseni dikey yaptı. justify-content artık dikeyde çalışır. Ama kapsayıcıya yükseklik verilmediği için kutu içeriği kadar uzun; dağıtılacak boş yer yok, space-between görünür bir şey yapmaz. height: 300px eklersen ilk öğe tepeye, sonuncusu dibe gider. Dikey ortalama hatasıyla aynı sebep.

Soru 3

700 piksel genişliğindeki bir kapsayıcıda kaç sütun olur, her sütun kaç piksel?

CSS
.izgara {    display: grid;    grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));    gap: 20px;}
Cevabı göster

Üç sütun, her biri 220 piksel. Dört sütun için 4 × 200 + 3 × 20 = 860 piksel gerekir, 700'e sığmaz. Üç sütun 3 × 200 + 2 × 20 = 640 piksel ister, sığar. Sonra boşluklar ayrılır (700 − 40 = 660) ve kalan üç eşit paya bölünür: 220.

Soru 4

Bu rozet kartın köşesinde değil, sayfanın sağ üst köşesinde çıkıyor. Hangi satır eksik?

CSS
.kart {    width: 220px;    padding: 16px;    background-color: #e3f2fd;}.rozet {    position: absolute;    top: 8px;    right: 8px;}
Cevabı göster

.kart'ta position: relative eksik. absolute öğe en yakın konumlanmış atasına göre yerleşir. Kart varsayılan değerde (static) kaldığı için rozet referans bulamaz ve sayfaya göre yerleşir.

Soru 5

Görüntü alanı tam 600 piksel genişken hangi kurallar çalışır: @media (min-width: 600px) mi, @media (max-width: 600px) mi, ikisi de mi? 599 pikselde?

Cevabı göster

600 pikselde ikisi de çalışır, çünkü min-width ve max-width sınırın kendisini kapsar ("en az 600" ve "en çok 600"). 599 pikselde yalnız max-width: 600px çalışır. Bu yüzden iki yönü aynı sayıyla karıştırmak yerine mobil öncelikli yazıp yalnız min-width kullanmak işini kolaylaştırır.

Soru 6

Aşağıdaki iki başlık hangi renkte görünür?

CSS
:root {    --vurgu: purple;}.uyari {    --vurgu: red;}.baslik {    color: var(--vurgu);}
HTML
<div class="uyari">    <h2 class="baslik">Dikkat</h2></div><h2 class="baslik">Merhaba</h2>
Cevabı göster

"Dikkat" kırmızı, "Merhaba" mor. Değişkenler kalıtımla içeri geçer ve her öğe en yakın atasındaki değeri kullanır. İlk başlık .uyari'nın içinde, orada --vurgu kırmızı olarak yeniden tanımlanmış. İkinci başlık .uyari'nın dışında; değeri :root'tan, yani mordan gelir.

Soru 7

Şu iki iş için flex mi grid mi seçerdin? (a) Üst çubuktaki dört menü bağlantısını bir satıra dizmek. (b) Yirmi şampiyon kartını ekrana göre sütun sayısı değişen hizalı bir galeride göstermek.

Cevabı göster

(a) Flex: tek sıra, bağlantılar içerikleri kadar genişlikte; gap ve gerekirse flex-wrap yeter. (b) Grid: kartlar hem satır hem sütun olarak hizalı olmalı; repeat(auto-fit, minmax(…)) sütun sayısını ekrana göre kendisi ayarlar. Flex ile yapsan son satırdaki kartlar üsttekilerden farklı genişlikte olurdu.

Soru 8

Bir sayfa bilgisayarda düzgün ama telefonda bütün yazılar minicik ve sayfa üç sütunlu görünüyor. İlk neye bakarsın?

Cevabı göster

<head> içindeki <meta name="viewport" content="width=device-width, initial-scale=1"> satırına. O satır yoksa telefon tarayıcısı sayfayı yaklaşık 980 piksellik bir ekrana çizip küçültür. 980 piksel geniş sayıldığı için min-width sorguları da çalışır ve sayfa bilgisayardaki gibi çok sütunlu görünür.

Kayıt noktası

C:\kod\sampiyon-kartlari yeni bir klasör; Git'i burada ilk kez başlatıyorsun. VS Code terminalinde, bu klasördeyken:

Bir sonraki derste JavaScript sayfayla konuşmaya başlıyor (DOM): öğeleri bulup değiştirecek, tıklamalara tepki vereceksin ve bu sayfanın menüsünde Büyücü'ye basınca yalnız büyücü kartları kalacak.

İşaret yalnızca bu tarayıcıda saklanır, kimseye gönderilmez.