Skip to content

Bölüm 11 / 20

Responsive tasarım

Media query, mobile-first, esnek görseller.

14 dakikalık okuma

İlan metninde geçen "responsive" kelimesinin karşılığı bu derstir. Duyarlı tasarım, ekran boyutuna göre ayrı siteler yapmak değil; tek bir düzenin akıp uyum sağlamasıdır. Bugün bu işin büyük kısmı medya sorgusu bile gerektirmez — 8, 9 ve 10. derslerde gördüğün clamp(), minmax(), auto-fit ve flex-wrap zaten esnektir. Medya sorgusu düzenin biçim değiştirmesi gerektiğinde devreye girer. Bu ders kırılma noktasının nasıl seçildiğini, mobil-öncelikli yazmanın neden pratik olduğunu, esnek görselleri ve modern karşılığı olan kapsayıcı sorgularını anlatıyor.

01Ne öğreneceksin

  • Viewport meta etiketi olmadan hiçbir şeyin çalışmaması
  • CSS pikseli ile cihaz pikseli farkı; cihaz piksel oranı (DPR)
  • Medya sorgusu sözdizimi, mantıksal operatörler ve aralık yazımı
  • width dışındaki özellikler: pointer, hover, orientation, prefers-*
  • Mobil öncelikli yazım: min-width mi max-width mi
  • Kırılma noktası cihaza göre değil içeriğe göre seçilir
  • Medya sorgusuz duyarlılık: clamp, auto-fit, flex-wrap, min()
  • Esnek görseller: max-width, srcset/sizes, object-fit, aspect-ratio
  • Kapsayıcı sorguları (@container) — bileşen bazlı duyarlılık
  • Dokunmatikte :hover'ın olmaması ve dokunma hedefi boyutu
  • Test: cihaz emülasyonunun ne gösterdiği, ne göstermediği

02Önce viewport, sonra her şey

1. ve 2. derslerde geçmişti; burada nedenini görüyoruz. Mobil tarayıcılar, duyarlı olmayan eski siteler düzgün görünsün diye sanal bir görünüm alanı kullanır: sayfayı 980 CSS pikseli genişliğinde çizip ekrana sığacak kadar küçültürler. Sonuç, uzaktan bakılan bir masaüstü sayfasıdır ve tüm medya sorguların "geniş ekran" dalını çalıştırır.

<meta name="viewport" content="width=device-width, initial-scale=1">

Bu satır "görünüm alanı cihazın gerçek genişliği olsun, ölçek 1'de başlasın" der. Duyarlı tasarımın ön koşuludur — bu satır olmadan yazdığın hiçbir medya sorgusu mobilde beklediğin gibi çalışmaz.

content içine eklenen iki değer bilinmelidir ve ikisi de erişilebilirlik ihlalidir:

<!-- YAZMA: kullanıcının yakınlaştırmasını engeller (WCAG 1.4.4) -->
<meta name="viewport" content="width=device-width, maximum-scale=1, user-scalable=no">

Görme güçlüğü olan kullanıcılar sayfayı parmakla büyütür; bunu kapatmak, ölçülebilir bir erişilebilirlik hatasıdır ve denetimlerde doğrudan düşer. Modern iOS bu ayarı zaten yok sayar.

CSS pikseli ≠ cihaz pikseli

width: 320px yazdığında bu fiziksel piksel değildir. Modern telefonlar bir CSS pikselini 2, 3 hatta 4 fiziksel pikselle çizer; bu orana cihaz piksel oranı (DPR) denir. Pratik sonuçları:

  • Telefonun "1170 piksel" reklamı yapılan ekranı CSS'te 390 px genişliğindedir.
  • Görseller bu yüzden 2x/3x çözünürlükte sunulur — srcset'in varlık sebebi.
  • 1px kenarlık yüksek DPR'li ekranda saç teli inceliğinde görünür; tasarımcının "daha kalın olsun" demesinin sebebi budur.
  • JavaScript'ten window.devicePixelRatio ile okunur.

03Medya sorguları

/* Klasik yazım */
@media (min-width: 900px) { … }
@media (max-width: 899.98px) { … }
@media (min-width: 600px) and (max-width: 899px) { … }
@media (orientation: landscape) { … }
@media print { … }

/* Modern aralık yazımı — daha okunaklı, 0.98px hilesi gerekmez */
@media (width >= 900px) { … }
@media (600px <= width < 900px) { … }

Eski yazımda max-width: 899.98px gibi tuhaf değerler görürsün: sebebi min-width: 900px ile max-width: 900px'in tam 900'de ikisinin birden eşleşmesidir. Aralık yazımı bu sorunu ortadan kaldırır.

ÖzellikNe sorarKullanım
width / heightGörünüm alanı ölçüsüEn yaygın
orientationDikey mi yatay mıGenişlikten daha az güvenilir
pointer: coarseKaba işaretçi var mı (parmak)Dokunma hedefini büyütmek
hover: noneÜzerine gelme mümkün müFare olmayan cihazda :hover'a bağlı arayüz kurmamak
prefers-color-schemeKoyu/açık tema tercihi12. dersin konusu
prefers-reduced-motionHareket azaltılsın mı6. derste görüldü
prefers-contrastYüksek kontrast isteniyor muErişilebilirlik
printYazdırma çıktısıMenü/reklam gizleme, bağlantı adreslerini yazdırma

pointer ve hover özellikleri, "mobil = dokunmatik" varsayımının yanlışlığını çözer: dokunmatik ekranlı dizüstü bilgisayarlar, fare bağlanmış tabletler ve klavyeyle kullanılan telefonlar vardır. Genişlik cihaz türünü söylemez.

/* Doğru soru: "ekran dar mı" değil, "parmakla mı kullanılıyor" */
@media (pointer: coarse) {
  .simge-dugme { min-width: 44px; min-height: 44px; }
}

/* Fare yoksa hover'a bağlı gizli menü kurma */
@media (hover: hover) {
  .kart:hover .eylemler { opacity: 1; }
}

JavaScript tarafında aynı sorgular okunabilir — 12. derste tema anahtarını yazarken işine yarayacak:

const dar = window.matchMedia("(width < 700px)");
dar.addEventListener("change", (e) => { /* e.matches */ });

04Mobil öncelikli yazım

İki yaklaşım vardır:

Mobil öncelikli (min-width)Masaüstü öncelikli (max-width)
Taban stilDar ekranGeniş ekran
SorgularEkran büyüdükçe eklerEkran küçüldükçe geri alır
Kod miktarıGenelde daha azGenelde daha çok (ezme ve sıfırlama)
Eski/zayıf cihazEn yalın stili alırAğır stili alıp sonra ezer
/* Mobil öncelikli — taban dar ekran, ekledikçe genişliyor */
.kartlar { display: grid; gap: 1rem; }            /* tek sütun */

@media (width >= 600px) { .kartlar { grid-template-columns: repeat(2, 1fr); } }
@media (width >= 960px) { .kartlar { grid-template-columns: repeat(3, 1fr); } }

Mobil öncelikliliğin asıl gerekçesi moda değil, karmaşıklıktır: dar ekran düzeni neredeyse her zaman daha basittir (tek sütun, yığılmış), geniş ekran düzeni ona ekleme yaparak kurulur. Tersini yaptığında her kırılma noktasında bir öncekini geri almak zorunda kalırsın ve kurallar birikir.

Aynı düşünce ilerlemeli geliştirme (progressive enhancement) fikriyle uyumludur: en yalın hâl herkes için çalışır, imkânlar arttıkça üzerine eklenir.

Kırılma noktası nasıl seçilir

Cihaz genişliklerine göre seçilmez. "iPhone 390, iPad 768, dizüstü 1366" listesi her yıl değişir ve hiçbir zaman tam değildir. Doğru ölçüt içeriktir: pencereyi yavaşça daralt ve düzenin bozulduğu ilk noktada kırılma noktası koy.

  • Metin satırı 75 karakteri aştı mı → genişliği sınırla (max-width: 70ch).
  • Üç sütun sıkıştı mı → ikiye in.
  • Yan menü içeriği ezmeye başladı mı → üste al ya da hamburgere çevir.

Sonuçta iki-üç kırılma noktası çoğu projeye yeter. Onlarca kırılma noktası olan bir stil dosyası genelde yanlış araç seçildiğinin işaretidir — o iş muhtemelen clamp() ya da auto-fit ile medya sorgusuz yapılabilirdi.

05Medya sorgusu gerektirmeyen duyarlılık

Modern CSS'in en büyük değişimi budur: esnekliğin çoğu artık kuralın kendisinde.

/* 1. Genişlik: küçük olan kazansın */
.icerik { width: min(100% - 2rem, 70ch); margin-inline: auto; }

/* 2. Akıcı yazı boyutu — taban ve tavanlı */
h1 { font-size: clamp(1.5rem, 1rem + 2.5vw, 3rem); }

/* 3. Kart ızgarası — sütun sayısına tarayıcı karar verir */
.kartlar { display: grid; gap: 1rem;
           grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); }

/* 4. Sığmazsa alt satıra geç */
.etiketler { display: flex; flex-wrap: wrap; gap: .5rem; }

/* 5. Sabit ama taşmayan kutu */
.kart { width: 800px; max-width: 100%; }

/* 6. Boşluk ekranla birlikte büyüsün, ama bir tabanın altına inmesin */
.bolum { padding-block: max(2rem, 5vh); }

Karar kuralı basittir: ölçüler değişiyorsa (genişlik, boşluk, yazı boyutu, sütun sayısı) medya sorgusuna gerek yoktur. Düzen biçim değiştiriyorsa — yan menü üste taşınıyor, tablo kart görünümüne dönüyor, gezinme hamburgere çevriliyor — medya sorgusu gerekir.

06Esnek görseller ve medya

/* Her projede bulunan iki satır */
img, video, svg { max-width: 100%; height: auto; }

height: auto olmadan, HTML'deki height özniteliği görseli ezer ve oran bozulur. 2. derste öğrendiğin kural hâlâ geçerli: width/height özniteliklerini yaz (yerleşim kayması için), CSS ile de esnet.

<img src="foto-800.jpg"
     srcset="foto-400.jpg 400w, foto-800.jpg 800w, foto-1600.jpg 1600w"
     sizes="(width >= 900px) 50vw, 100vw"
     width="1600" height="900" alt="…">

sizes, tarayıcıya "bu görsel yerleşimde ne kadar yer kaplayacak" der; tarayıcı bunu DPR ile çarpıp srcset'ten uygun dosyayı seçer. sizes yazmazsan tarayıcı 100vw varsayar ve gereğinden büyük dosya indirir. Bu, mobil kullanıcının veri paketini boşa harcayan en yaygın hatalardan biridir.

/* Kırpma: kutuya sığdır ama oranı bozma */
.kapak img { width: 100%; aspect-ratio: 16 / 9; object-fit: cover; object-position: center; }

/* contain: tamamı görünsün, boşluk kalsın (logo için) */
.logo img { aspect-ratio: 1; object-fit: contain; }

object-fit: cover, farklı oranlarda gelen kullanıcı görsellerini tek tip kartlarda göstermenin standart yoludur. object-position ile kırpma odağını kaydırabilirsin — portrelerde top genelde daha iyi sonuç verir.

Yazı tipleri de duyarlılığın parçasıdır: web yazı tipi inene kadar metnin görünmez kalması (FOIT) algılanan hızı düşürür ve inince oluşan geçiş yerleşim kaymasına yol açar. font-display: swap ile önce yedek yazı tipi gösterilir; kaymayı azaltmak için yedek yazı tipinin ölçüleri (size-adjust) ayarlanabilir.

07Kapsayıcı sorguları

Medya sorgularının yapısal bir sınırı vardır: görünüm alanını ölçerler, bileşenin bulunduğu yeri değil. Aynı kart bileşeni geniş bir içerik alanında da, dar bir yan sütunda da kullanılabilir; ekran geniş olduğu hâlde kartın yeri dardır ve medya sorgusu bunu göremez.

/* Ebeveyni sorgulanabilir hâle getir */
.kart-yuvasi { container-type: inline-size; container-name: kart; }

/* Artık ölçülen şey EKRAN değil, KAPSAYICI genişliği */
@container kart (width >= 400px) {
  .kart { display: grid; grid-template-columns: 120px 1fr; gap: 1rem; }
}

Bu, bileşen tabanlı geliştirmenin (10. ve 11. hat) eksik parçasıydı: bir bileşen artık nerede kullanıldığını bilmeden kendi düzenine karar verebiliyor. Bilinmesi gereken sınırlar: kapsayıcı ögesinin kendisi kendi sorgusuna göre stillenemez (bu yüzden bir sarmalayıcı gerekir) ve container-type: inline-size ögeye bir boyut sınırlaması getirir. Ayrıca cqw/cqi gibi kapsayıcı birimleri de vardır.

08Tuzaklar ve test

BelirtiOlası sebep
Yatay kaydırma çıkıyor100vw (kaydırma çubuğunu içerir), sabit genişlikli öge, taşan tablo/görsel, negatif kenar boşluğu, min-width: auto yüzünden daralmayan flex/grid ögesi
Mobilde her şey küçükViewport meta eksik
Dokunma hedefleri küçükpointer: coarse dalı yazılmamış; 24×24 px ölçütü karşılanmıyor (6. ders)
Menü telefonda açılmıyorYalnız :hover ile açılıyor — dokunmatikte :hover güvenilir değildir
Tam ekran bölüm taşıyor100vh yerine 100dvh gerekiyor (8. ders)
%200 büyütmede düzen bozuluyorSabit px genişlikler, taşmaya izin vermeyen kaplar (WCAG 1.4.4 / 1.4.10)

Yatay taşmayı bulmanın hızlı yolu konsolda tek satırdır:

document.querySelectorAll("*").forEach(e => {
  if (e.getBoundingClientRect().right > document.documentElement.clientWidth) console.log(e);
});

Test disiplini: DevTools cihaz emülasyonu boyutu ve DPR'yi taklit eder; taklit etmediği şeyler vardır — gerçek dokunma davranışı, yazılım klavyesinin açılınca görünüm alanını kısaltması, mobil tarayıcının adres çubuğu davranışı, ağ ve CPU farkı, iOS Safari'nin kendine özgü davranışları. Kritik ekranlar en az bir gerçek cihazda denenir. Ek olarak dört testi alışkanlık hâline getir: (1) pencereyi 320 px'e kadar daralt, (2) %200 yakınlaştır, (3) yalnız klavyeyle gez, (4) yazdırma önizlemesini aç.

09Mini alıştırma — Bir sayfayı 320 px'e kadar daralt

Yaklaşık 20 dakika · tek HTML dosyası + DevTools cihaz emülasyonu

  1. Kur: üst bar, yan menü, kart ızgarası ve altbilgiden oluşan bir sayfa yaz (10. dersteki grid-template-areas iskeletini kullanabilirsin).
  2. Viewport testi: viewport meta satırını sil ve cihaz emülasyonunda aç. Sayfa nasıl göründü? Medya sorguların çalıştı mı? Geri koy.
  3. DPR: konsolda window.devicePixelRatio yaz; emülasyonda cihaz değiştirip tekrar bak.
  4. Kırılma noktası bulma: pencereyi yavaşça daralt ve düzenin ilk bozulduğu genişliği not et. Kırılma noktanı oraya koy — önceden seçtiğin değerle aynı mı?
  5. Mobil öncelikli çevirme: düzeni önce max-width sorgularıyla yaz, sonra min-width ile yeniden yaz. Hangisi daha az satır tuttu?
  6. Aralık yazımı: @media (min-width: 900px) kuralını @media (width >= 900px) olarak yeniden yaz ve çalıştığını doğrula.
  7. Medya sorgusuz ızgara: kart ızgarasındaki tüm medya sorgularını silip repeat(auto-fit, minmax(260px, 1fr)) ile değiştir. Kaç satır kod gitti?
  8. clamp(): başlık boyutunu clamp() ile akıcı yap ve pencereyi daralt.
  9. Görsel: büyük bir görseli srcset/sizes ile ekle. Network panelinde hangi dosyanın indirildiğini gör; pencereyi daraltıp sert yenile — farklı dosya mı indi? sizes'ı silip tekrarla.
  10. object-fit: farklı oranlarda üç görseli aynı boyutlu kartlarda göster; cover ve contain arasında geçiş yap.
  11. Dokunma: @media (pointer: coarse) dalında simge düğmelerini 44 px'e çıkar. Emülasyonda dokunma moduna geçip doğrula.
  12. :hover tuzağı: yalnız :hover ile açılan bir alt menü yaz ve dokunma emülasyonunda açmayı dene. Sonra @media (hover: hover) ile koru ve dokunmatik için tıklamayla açılan bir yol ekle.
  13. Yatay taşma: bir ögeye width: 100vw ver. Kaydırma çıktı mı? Yukarıdaki konsol betiğiyle suçluyu bul.
  14. 320 px ve %200: pencereyi 320 px'e daralt, sonra %200 yakınlaştır. Kesilen, taşan ya da erişilemeyen bir öge kaldı mı?
  15. Kapsayıcı sorgusu: aynı kart bileşenini bir kez geniş içerik alanına, bir kez dar yan sütuna koy. Medya sorgusuyla ikisini birden doğru yapabiliyor musun? Sonra @container ile çöz.

10Kendini sına

Mobile-first yaklaşımı nedir, neden tercih edilir?

Taban stilleri dar ekran için yazıp, ekran genişledikçe min-width medya sorgularıyla ekleme yapmaktır. Tersi (masaüstü öncelikli) geniş ekranı taban alır ve max-width sorgularıyla küçük ekranda kuralları geri alır. Tercih edilmesinin asıl gerekçesi karmaşıklıktır: dar ekran düzeni neredeyse her zaman daha basittir (tek sütun, yığılmış içerik), karmaşık düzen ona eklenerek kurulur — böylece her kırılma noktasında bir öncekini iptal eden kurallar birikmez ve stil dosyası küçük kalır. İkinci gerekçe, zayıf cihazların en yalın stili almasıdır. Üçüncüsü, mobil kullanımın çoğu kamu hizmetinde masaüstünü geçmiş olmasıdır — varsayılan durumun mobil olması doğal karşılıktır. Ön koşul, <meta name="viewport" content="width=device-width, initial-scale=1"> satırıdır; o olmadan mobil tarayıcı sayfayı 980 px varsayar ve hiçbir medya sorgusu beklendiği gibi çalışmaz. Son olarak bugün mobile-first'ün önemli bir kısmı medya sorgusu bile gerektirmez: clamp(), minmax(), auto-fit ve flex-wrap zaten esnektir.

Kırılma noktalarını (breakpoint) neye göre seçersin?

Cihaz genişliklerine göre değil, içeriğe göre. "iPhone 390, iPad 768, dizüstü 1366" listesi her yıl değişir, hiçbir zaman tam değildir ve aynı genişlikte bambaşka cihazlar bulunur. Doğru yöntem: pencereyi yavaşça daralt ve düzenin bozulduğu ilk noktaya kırılma noktası koy — metin satırı 75 karakteri aştığında genişliği sınırla, üç sütun sıkıştığında ikiye in, yan menü içeriği ezmeye başladığında üste al. Pratikte iki–üç kırılma noktası çoğu projeye yeter. Onlarca kırılma noktası olan bir stil dosyası genelde yanlış araç seçildiğinin işaretidir: o iş muhtemelen clamp() ya da repeat(auto-fit, minmax(...)) ile hiç medya sorgusu olmadan yapılabilirdi. Kural olarak: ölçüler değişiyorsa (boyut, boşluk, sütun sayısı) medya sorgusu gerekmez; düzen biçim değiştiriyorsa (menü hamburgere dönüyor, tablo kart olacak, yan sütun üste taşınacak) gerekir. Bir de genişliğin cihaz türünü söylemediğini unutma: dokunma hedefi kararı için pointer: coarse, :hover'a bağlı arayüz için hover: hover sorulur.

Görselleri duyarlı yapmanın yolları nelerdir? srcset ile sizes ne işe yarar?

Temel satır img { max-width: 100%; height: auto; } — görsel kabına sığar ve oranı korunur (height: auto olmadan HTML'deki height özniteliği oranı bozar). Bu, dosyanın boyutunu küçültmez; onun için srcset gerekir: aynı görselin farklı çözünürlüklerini genişlikleriyle listelersin (foto-800.jpg 800w) ve tarayıcı, cihazın piksel oranını da hesaba katarak uygun olanı seçer. sizes ise tarayıcıya görselin yerleşimde ne kadar yer kaplayacağını söyler ((width >= 900px) 50vw, 100vw); yazılmazsa tarayıcı 100vw varsayar ve gereğinden büyük dosya indirir — mobil veri paketini boşa harcayan yaygın bir hatadır. Biçim ya da kırpma değişecekse <picture> + <source> kullanılır (AVIF/WebP sunumu ya da mobilde farklı bir kadraj). Kutuya sığdırırken oranı korumak için aspect-ratio + object-fit: cover, odak için object-position kullanılır. Ve performans/kayma tarafı: width/height özniteliklerini yaz (CLS), ekran dışı görsellere loading="lazy" ver ama ilk ekrandaki hero görseline verme.

Kapsayıcı sorguları (@container) neden gerekti? Medya sorgusundan farkı nedir?

Medya sorguları görünüm alanını ölçer; bileşenin bulunduğu yeri değil. Bileşen tabanlı geliştirmede aynı kart geniş bir içerik alanında da, dar bir yan sütunda da, bir kalıcı pencerede de kullanılır — ekran geniş olduğu hâlde kartın yeri dar olabilir ve medya sorgusu bunu göremez. Sonuç: bileşen, kullanıldığı yeri bilmeden doğru düzeni seçemez ve yerleşime özel sınıflar (kart--dar) yazmak zorunda kalırsın. @container bunu çözer: ebeveyne container-type: inline-size verilir, sorgu artık kapsayıcının genişliğini ölçer ve bileşen kendi düzenine kendi karar verir. Bilinmesi gereken sınırlar: bir öge kendi kapsayıcı sorgusuna göre stillenemez (bu yüzden bir sarmalayıcı gerekir), container-type ögeye boyut sınırlaması getirir, ve cqw/cqi gibi kapsayıcı birimleri ayrıca vardır. Medya sorguları ortadan kalkmaz: sayfa iskeleti, yazdırma, koyu tema ve pointer/hover gibi cihaz düzeyindeki sorular hâlâ onların işidir.

11Sınav köşesi

Yazılıda nasıl sorulur

(1) Responsive tasarım nedir? (2) Viewport meta etiketi ne işe yarar, yazılmazsa ne olur? (3) Media query sözdizimi; min-width ile max-width farkı. (4) Mobile-first nedir, neden tercih edilir? (5) Kırılma noktaları nasıl belirlenir? (6) Esnek görsel için hangi CSS ve HTML özellikleri kullanılır? (7) srcset ve sizes. (8) object-fit değerleri. (9) CSS pikseli ile cihaz pikseli farkı; devicePixelRatio. (10) Medya sorgusu kullanmadan duyarlı düzen nasıl kurulur? (11) @container nedir? (12) Yazdırma için stil nasıl verilir?

Sözlüde nasıl anlatılır — 2 dakikalık taslak

Düzen: (1) Tanım — "duyarlı tasarım, ayrı bir mobil site yapmak değil; tek düzenin ekrana uyum sağlamasıdır. Ön koşulu viewport meta etiketidir, o olmadan mobil tarayıcı sayfayı 980 px varsayar." (2) Yaklaşım — "mobil öncelikli yazarım: taban dar ekran, min-width ile ekleme yaparım; tersi her adımda önceki kuralları geri almayı gerektirir." (3) Kıdem vuruşu — "kırılma noktasını cihaz listesine göre değil içeriğe göre seçerim: pencereyi daraltıp düzenin bozulduğu ilk noktaya koyarım. Çoğu projede iki üç nokta yeter; onlarca varsa yanlış araç seçilmiştir." (4) Modern araçlar — "ölçüler değişiyorsa medya sorgusu yazmam: clamp(), min() ve repeat(auto-fit, minmax(260px, 1fr)) işi görür. Düzen biçim değiştiriyorsa medya sorgusu kullanırım; bileşenin kendi yerine göre karar vermesi gerekiyorsa @container." (5) Ayrıntı ve test — "görselde srcset/sizes yazarım, sizes eksikse tarayıcı 100vw varsayıp gereksiz büyük dosya indirir. Genişlik cihaz türünü söylemediği için dokunma hedeflerini pointer: coarse ile büyütür, :hover'a bağlı arayüz kurmam. Testte 320 px'e daraltır, %200 yakınlaştırır ve kritik ekranı gerçek cihazda denerim."