---
title: 'Duyarlı Görseller'
source: 'https://academia.sh/tr/kurslar/duzen-ve-duyarli-tasarim/duyarli-gorseller'
course: 'Düzen Sistemleri ve Duyarlı Tasarım'
language: tr
updated: '2026-08-17T18:09:28+00:00'
license: 'CC BY-SA 4.0'
---

# Duyarlı Görseller

Çözünürlük değişimi ile sanat yönetiminin ayrılması, kaynak boyutu bildiriminin çözülmesi, aday seçim kuralı, kaynak sırasının etkisi ve ayrılan yerin içerik kaymasını önlemesi.

Yazı ölçekle birlikte değişiyor, düzen kabına göre uyarlanıyor. Geriye sayfanın en ağır ögesi
kalıyor: konum bölümündeki görsel.

Görselde iki ayrı sorun aynı anda bulunur ve karıştırıldıklarında ikisi de çözülmez. Birincisi
**ölçü** sorunudur: aynı kare, dar bir ekranda gereğinden büyük bir dosyayla indirilir ya da
yoğun bir ekranda bulanık görünür. İkincisi **içerik** sorunudur: geniş bir manzara karesi dar
bir ekranda küçüldüğünde ayrıntısı okunamaz hâle gelir; gereken küçültme değil kırpmadır.

Bu ders iki sorunu ayırır ve her birinin aracını gösterir.

## Çözünürlük Değişimi

Birinci sorunun adı **çözünürlük değişimidir (resolution switching)**: aynı görselin farklı
piksel ölçülerindeki kopyaları hazırlanır ve tarayıcı hangisini indireceğine karar verir.

Karar iki bilgiye dayanır. `srcset` özniteliği bir **aday kümesi (candidate set)** verir; her
aday, dosyanın gerçek piksel genişliğiyle birlikte yazılır. `sizes` özniteliği ise görselin
sayfada kaplayacağı yeri bildirir — buna **kaynak boyutu (source size)** denir.

İkincisi neden gereklidir? Tarayıcı, biçem dosyasını çözmeden önce görsel indirmeye başlar;
görselin sayfada ne kadar yer kaplayacağını o anda bilemez. `sizes`, bu bilgiyi biçem
dosyasından bağımsız olarak belgeye yazar.

```js
// srcset.mjs — kaynak boyutunun cozulmesi ve aday secimi
// sizes: sirayla denenir, ilk eslesen kosulun uzunlugu kaynak boyutudur.
const SIZES = [
  ["(min-width: 812px)", "33vw"],
  ["(min-width: 500px)", "50vw"],
  [null, "calc(100vw - 48px)"],
];

// srcset: her aday, gercek piksel genisligiyle (w tanimlayicisi) bildirilir
const ADAYLAR = [320, 480, 640, 960, 1280, 1920];

function kosulDogru(kosul, gorusAlani) {
  if (kosul === null) return true;
  const m = kosul.match(/^\((min|max)-width:\s*(\d+)px\)$/);
  return m[1] === "min" ? gorusAlani >= Number(m[2]) : gorusAlani <= Number(m[2]);
}

function uzunluk(ifade, gorusAlani) {
  let m = ifade.match(/^(\d+(?:\.\d+)?)vw$/);
  if (m) return (Number(m[1]) * gorusAlani) / 100;
  m = ifade.match(/^calc\((\d+(?:\.\d+)?)vw\s*-\s*(\d+(?:\.\d+)?)px\)$/);
  if (m) return (Number(m[1]) * gorusAlani) / 100 - Number(m[2]);
  m = ifade.match(/^(\d+(?:\.\d+)?)px$/);
  if (m) return Number(m[1]);
  throw new Error(`cozulemeyen uzunluk: ${ifade}`);
}

function kaynakBoyutu(gorusAlani) {
  for (const [kosul, ifade] of SIZES) {
    if (kosulDogru(kosul, gorusAlani)) return { ifade, px: uzunluk(ifade, gorusAlani) };
  }
  throw new Error("sizes listesi bos");
}

// Secim kurali: gereken piksel genisligini karsilayan en kucuk aday.
// Hicbiri karsilamiyorsa en buyuk aday secilir.
function secim(gereken) {
  return ADAYLAR.find((a) => a >= gereken) ?? ADAYLAR[ADAYLAR.length - 1];
}

console.log("sizes: " + SIZES.map(([k, i]) => (k ? `${k} ${i}` : i)).join(", "));
console.log("srcset adaylari: " + ADAYLAR.map((a) => `${a}w`).join(", "));
console.log("\ngorus  kaynak boyutu ifadesi   kaynak   piksel  gereken  secilen  fazla");
console.log("alani                          boyutu   orani            aday     piksel");
for (const V of [360, 500, 700, 812, 1280, 1600]) {
  for (const dpr of [1, 2, 3]) {
    const { ifade, px } = kaynakBoyutu(V);
    const gereken = px * dpr;
    const secilen = secim(gereken);
    const fazla = secilen - gereken;
    console.log(
      `${String(V).padStart(5)}  ${ifade.padEnd(21)}  ${px.toFixed(0).padStart(6)}  ` +
        `${String(dpr + "x").padStart(6)}  ${gereken.toFixed(0).padStart(7)}  ` +
        `${String(secilen + "w").padStart(7)}  ${fazla.toFixed(0).padStart(6)}`,
    );
  }
}

// aday genisliklerinin piksel sayisi (3:2 en boy orani varsayilarak)
console.log("\n--- aday dosyalarin piksel sayisi (3:2 en boy orani) ---");
console.log("aday   genislik x yukseklik   piksel      320w'in kaci kadar");
for (const a of ADAYLAR) {
  const y = Math.round((a * 2) / 3);
  const piksel = a * y;
  const taban = 320 * Math.round((320 * 2) / 3);
  console.log(
    `${String(a + "w").padStart(5)}  ${String(a + " x " + y).padStart(18)}  ` +
      `${String(piksel).padStart(9)}  ${(piksel / taban).toFixed(1).padStart(18)}x`,
  );
}

// sizes yanlis yazilirsa: gorsel 33vw yer kapliyorken 100vw bildirilmis olsun
console.log("\n--- sizes yanlis yazilirsa (gercekte 33vw, bildirilen 100vw) ---");
console.log("gorus  piksel  dogru sizes  yanlis sizes  secilen piksel orani");
console.log("alani  orani   ile secilen  ile secilen");
for (const V of [812, 1280, 1600]) {
  for (const dpr of [1, 2]) {
    const dogru = secim(kaynakBoyutu(V).px * dpr);
    const yanlis = secim(V * dpr);
    const y1 = Math.round((dogru * 2) / 3), y2 = Math.round((yanlis * 2) / 3);
    const oran = (yanlis * y2) / (dogru * y1);
    console.log(
      `${String(V).padStart(5)}  ${String(dpr + "x").padStart(6)}  ` +
        `${String(dogru + "w").padStart(11)}  ${String(yanlis + "w").padStart(12)}  ${oran.toFixed(1).padStart(19)}x`,
    );
  }
}
```

```
sizes: (min-width: 812px) 33vw, (min-width: 500px) 50vw, calc(100vw - 48px)
srcset adaylari: 320w, 480w, 640w, 960w, 1280w, 1920w

gorus  kaynak boyutu ifadesi   kaynak   piksel  gereken  secilen  fazla
alani                          boyutu   orani            aday     piksel
  360  calc(100vw - 48px)        312      1x      312     320w       8
  360  calc(100vw - 48px)        312      2x      624     640w      16
  360  calc(100vw - 48px)        312      3x      936     960w      24
  500  50vw                      250      1x      250     320w      70
  500  50vw                      250      2x      500     640w     140
  500  50vw                      250      3x      750     960w     210
  700  50vw                      350      1x      350     480w     130
  700  50vw                      350      2x      700     960w     260
  700  50vw                      350      3x     1050    1280w     230
  812  33vw                      268      1x      268     320w      52
  812  33vw                      268      2x      536     640w     104
  812  33vw                      268      3x      804     960w     156
 1280  33vw                      422      1x      422     480w      58
 1280  33vw                      422      2x      845     960w     115
 1280  33vw                      422      3x     1267    1280w      13
 1600  33vw                      528      1x      528     640w     112
 1600  33vw                      528      2x     1056    1280w     224
 1600  33vw                      528      3x     1584    1920w     336

--- aday dosyalarin piksel sayisi (3:2 en boy orani) ---
aday   genislik x yukseklik   piksel      320w'in kaci kadar
 320w           320 x 213      68160                 1.0x
 480w           480 x 320     153600                 2.3x
 640w           640 x 427     273280                 4.0x
 960w           960 x 640     614400                 9.0x
1280w          1280 x 853    1091840                16.0x
1920w         1920 x 1280    2457600                36.1x

--- sizes yanlis yazilirsa (gercekte 33vw, bildirilen 100vw) ---
gorus  piksel  dogru sizes  yanlis sizes  secilen piksel orani
alani  orani   ile secilen  ile secilen
  812      1x         320w          960w                  9.0x
  812      2x         640w         1920w                  9.0x
 1280      1x         480w         1280w                  7.1x
 1280      2x         960w         1920w                  4.0x
 1600      1x         640w         1920w                  9.0x
 1600      2x        1280w         1920w                  2.3x
```

Birinci tablo iki adımı ayrı ayrı gösteriyor. Önce `sizes` listesi soldan sağa denenir ve
koşulu doğru olan **ilk** uzunluk kaynak boyutu olur — 812 birimlik bir görüntü alanında 33
görüntü alanı birimi, yani 268 piksel. Sonra bu değer aygıtın **piksel oranıyla (device pixel
ratio)** çarpılır: iki katlık bir ekranda 536 gerçek piksel gerekir. Aday kümesinden bu sayıyı
karşılayan en küçük dosya seçilir.

Sayılar bir noktayı hemen görünür kılıyor: aynı sayfa, aynı görüntü alanı genişliğinde, piksel
oranına göre 320, 640 ya da 960 piksellik dosya indirir. Görüntü alanı geniş olduğu için büyük
dosya seçilmez; kaynak boyutu ile piksel oranının çarpımı seçer.

İkinci tablo adaylar arasındaki farkın büyüklüğünü veriyor. Piksel sayısı genişliğin **karesiyle**
büyür: 1920 piksellik dosya, 320 piksellik dosyanın 36 katı piksel taşır. Bu yüzden aday
kümesinde bir basamak yanlış seçim, katlanarak büyüyen bir maliyettir.

## Kaynak Boyutu Yanlış Bildirilirse

Üçüncü tablo, en sık yapılan hatanın ölçüsünü veriyor. Görsel gerçekte görüntü alanının üçte
birini kaplarken `sizes` bildirimi tamamını kapladığını söylerse, tarayıcı gereğinden çok büyük
adayı seçer. Fark 2,3 ile 9 kat arasında değişiyor.

Hatanın gizlenmesi kolaydır, çünkü görsel doğru görünür — yalnız gereğinden büyük bir dosyayla.
Denetim yolu şudur: kaynak boyutu bildirimi, biçem dosyasındaki genişlik kurallarının bir
kopyasıdır ve düzen değiştiğinde birlikte güncellenmelidir. İkisinin ayrı yerlerde durması bu
yazımın yapısal zayıflığıdır.

İki not daha. Aday kümesi genişlik yerine piksel oranı tanımlayıcısıyla da yazılabilir
(`gorsel.jpg 1x, gorsel@2x.jpg 2x`); bu yazımda kaynak boyutu hesaba katılmaz ve yalnız aygıtın
piksel oranı seçer. Sabit ölçülü küçük görsellerde — bir simge, bir rozet — yeterlidir.

İkincisi, buradaki seçim kuralı bir modeldir. Aday seçimi tarayıcının takdirine bırakılmıştır;
ağ koşullarına ya da kullanıcı ayarına göre daha küçük bir aday seçilebilir. Yazılan
bildirimin görevi doğru bilgiyi vermektir, seçimi zorlamak değil.

## Sanat Yönetimi

İkinci sorun ölçüyle çözülmez. Geniş bir panorama karesi dar bir ekranda ne kadar küçültülürse
küçültülsün, ölçüm istasyonunun bulunduğu nokta ayırt edilemez hâle gelir. Gereken, aynı karenin
farklı bir kırpımıdır.

Bu ayrıma **sanat yönetimi (art direction)** denir ve aracı `picture` ögesidir. Öge, koşullu
`source` ögeleri ve bir `img` ögesi taşır.

```js
// sanat.mjs — picture ogesinde kaynak secimi ve ayrilan yerin hesabi
// Kural: kaynaklar belge sirasinda denenir, kosulu dogru olan ILK kaynak secilir.

const KAYNAKLAR = [
  { ad: "konum-genis", kosul: "(min-width: 812px)", en: 1440, boy: 600 },   // 12:5 panorama
  { ad: "konum-orta", kosul: "(min-width: 500px)", en: 960, boy: 640 },     // 3:2
  { ad: "konum-dar", kosul: null, en: 480, boy: 600 },                      // 4:5 kirpik
];

const SIZES = [
  ["(min-width: 812px)", 0.33],
  ["(min-width: 500px)", 0.5],
  [null, null],   // calc(100vw - 48px)
];

function kosulDogru(kosul, V) {
  if (kosul === null) return true;
  const m = kosul.match(/^\(min-width:\s*(\d+)px\)$/);
  return V >= Number(m[1]);
}

function gosterilenGenislik(V) {
  for (const [kosul, oran] of SIZES) {
    if (kosulDogru(kosul, V)) return oran === null ? V - 48 : V * oran;
  }
}

function sec(liste, V) {
  return liste.find((k) => kosulDogru(k.kosul, V));
}

console.log("kaynaklar belge sirasinda:");
for (const k of KAYNAKLAR) {
  console.log(`  ${k.ad.padEnd(12)} ${String(k.kosul ?? "(kosulsuz img)").padEnd(20)} ${k.en}x${k.boy}  en boy orani ${(k.en / k.boy).toFixed(2)}`);
}

console.log("\ngorus  gosterilen  secilen       en boy  ayrilan");
console.log("alani  genislik    kaynak        orani   yukseklik");
for (const V of [360, 500, 700, 812, 1280, 1600]) {
  const g = gosterilenGenislik(V);
  const k = sec(KAYNAKLAR, V);
  const oran = k.en / k.boy;
  console.log(
    `${String(V).padStart(5)}  ${g.toFixed(0).padStart(10)}  ${k.ad.padEnd(12)}  ` +
      `${oran.toFixed(2).padStart(6)}  ${(g / oran).toFixed(0).padStart(9)}`,
  );
}

// sira bozulursa: kosullu kaynaklar dardan genise siralanirsa
const TERS = [KAYNAKLAR[1], KAYNAKLAR[0], KAYNAKLAR[2]];
console.log("\n--- kosullu kaynaklar dardan genise siralanirsa ---");
console.log("gorus  secilen kaynak  dogru secim  ayni mi");
for (const V of [360, 500, 812, 1280]) {
  const t = sec(TERS, V), d = sec(KAYNAKLAR, V);
  console.log(
    `${String(V).padStart(5)}  ${t.ad.padEnd(14)}  ${d.ad.padEnd(11)}  ${(t.ad === d.ad ? "evet" : "HAYIR").padStart(7)}`,
  );
}

// ayrilan yer kaynaktan kaynaga degisir: her kaynak kendi olculerini bildirmezse
console.log("\n--- ayni gosterilen genislikte (500px) kaynaklarin yukseklikleri ---");
for (const k of KAYNAKLAR) {
  console.log(`  ${k.ad.padEnd(12)} ${(500 / (k.en / k.boy)).toFixed(0).padStart(4)}px`);
}
```

```
kaynaklar belge sirasinda:
  konum-genis  (min-width: 812px)   1440x600  en boy orani 2.40
  konum-orta   (min-width: 500px)   960x640  en boy orani 1.50
  konum-dar    (kosulsuz img)       480x600  en boy orani 0.80

gorus  gosterilen  secilen       en boy  ayrilan
alani  genislik    kaynak        orani   yukseklik
  360         312  konum-dar       0.80        390
  500         250  konum-orta      1.50        167
  700         350  konum-orta      1.50        233
  812         268  konum-genis     2.40        112
 1280         422  konum-genis     2.40        176
 1600         528  konum-genis     2.40        220

--- kosullu kaynaklar dardan genise siralanirsa ---
gorus  secilen kaynak  dogru secim  ayni mi
  360  konum-dar       konum-dar       evet
  500  konum-orta      konum-orta      evet
  812  konum-orta      konum-genis    HAYIR
 1280  konum-orta      konum-genis    HAYIR

--- ayni gosterilen genislikte (500px) kaynaklarin yukseklikleri ---
  konum-genis   208px
  konum-orta    333px
  konum-dar     625px
```

Üç kaynak, üç ayrı en boy oranı. Dar düzende dikey bir kırpım kullanılıyor: aynı genişlikte daha
çok dikey yer kaplıyor ve istasyonun bulunduğu nokta ölçekte kalıyor. Geniş düzende panorama
kırpımı, çevredeki arazi biçimini de gösteriyor.

İkinci tablo, ortam sorgularından ayrılan bir kuralı ölçüyor. Biçem dosyasında eşleşen
kuralların **sonuncusu** kazanıyordu; `picture` içinde koşulu doğru olan **ilki** seçilir.
Kaynaklar dardan genişe sıralandığında geniş koşullar hiç sıraya gelmez: 812 ve 1280 birimde
orta kırpım seçilir ve panorama kullanılmaz. Kaynaklar geniş koşuldan dar koşula doğru sıralanır.

Üçüncü blok, kırpımların birbirinden ne kadar farklı yer kapladığını gösteriyor. Aynı 500
birimlik genişlikte panorama 208, dikey kırpım 625 birim yükseklik ister. Bu fark, ayrılan yer
sorununu doğrudan doğurur.

## Ayrılan Yer

Görsel indirilmeden önce tarayıcı ona ne kadar yer ayıracağını bilemez. Bilmezse yer ayırmaz;
görsel geldiğinde altındaki içerik aşağı itilir. Web'in Temelleri ve HTML kursunda tanıtılan
**içerik kayması** budur.

Çözüm, en boy oranını belgeye yazmaktır. `img` ögesine `width` ve `height` öznitelikleri
verildiğinde tarayıcı oranı ikisinin bölümünden hesaplar ve görselin kaplayacağı kutuyu
önceden ayırır. Öznitelikler piksel değeridir ama sabit ölçü anlamına gelmez; biçem dosyasındaki
`inline-size` bildirimi görselin gerçek genişliğini belirler, öznitelikler yalnız oranı taşır.

Sanat yönetiminde bir ek gerekir. Her kırpımın oranı farklı olduğu için ayrılacak yer de
farklıdır; bu yüzden **her** `source` ögesi kendi `width` ve `height` değerlerini bildirir. Tek
bir oran yazıldığında kaynak değiştiği anda yer yeniden hesaplanır ve kayma oluşur.

Aynı sonuç biçem tarafında `aspect-ratio` bildirimiyle de kurulabilir; iki yol birlikte
kullanıldığında biçem kazanır.

```html
<figure class="konum-gorseli">
  <picture>
    <source media="(min-width: 812px)"
            srcset="konum-genis-960.jpg 960w, konum-genis-1440.jpg 1440w"
            sizes="33vw" width="1440" height="600">
    <source media="(min-width: 500px)"
            srcset="konum-orta-640.jpg 640w, konum-orta-960.jpg 960w"
            sizes="50vw" width="960" height="640">
    <img src="konum-dar-480.jpg"
         srcset="konum-dar-320.jpg 320w, konum-dar-480.jpg 480w"
         sizes="calc(100vw - 48px)"
         width="480" height="600"
         loading="lazy" decoding="async"
         alt="Kuzey Yamaç ölçüm istasyonunun bulunduğu sırtın güneybatıdan görünümü">
  </picture>
  <figcaption>Ölçüm istasyonu, sırtın güneybatı yamacında 1840 metrede.</figcaption>
</figure>
```

```css
/* duzen.css — 11. adım: görselin kutusu */
.konum-gorseli img {
  inline-size: 100%;
  block-size: auto;
  object-fit: cover;
  object-position: 50% 35%;
  background-color: var(--zemin);
}

.konum-gorseli figcaption {
  margin-block-start: 0.5rem;
  color: var(--metin-soluk);
  font-size: 0.875em;
}
```

Belge tarafında dört bildirim var ve her biri ayrı bir soruya yanıt veriyor: `media` hangi
kırpımın kullanılacağını, `srcset` o kırpımın hangi ölçülerde bulunduğunu, `sizes` sayfada ne
kadar yer kaplayacağını, `width` ile `height` hangi oranda yer ayrılacağını söylüyor. Alternatif
metin kırpımdan bağımsızdır ve `img` ögesinde tek bir kez yazılır — üç kaynak da aynı sahneyi
gösterir.

Biçem tarafında `inline-size: 100%` görseli kabına oturtur, `block-size: auto` oranı korur.
`object-fit: cover` bildirimi, kutunun oranı görselin oranından farklı kaldığında görseli
gerdirmek yerine kırpar; `object-position` kırpmanın hangi noktayı merkez alacağını bildirir.
Yüzde 35 değeri, ufuk çizgisinin üst üçte birde kalmasını sağlar.

Arka plan görselleri için karşılık `image-set()` fonksiyonudur; aynı aday mantığını biçem
dosyasında kurar. Ancak arka plan görselleri belgede karşılığı olmayan süsleyici görsellerdir;
içerik taşıyan bir kare arka plana konmaz.

## Özet

- Görselde iki ayrı sorun vardır: aynı karenin ölçüsünü seçmek (çözünürlük değişimi) ve dar
  düzende farklı bir kırpım göstermek (sanat yönetimi).
- Aday seçimi iki çarpanla belirlenir: `sizes` bildiriminden çözülen kaynak boyutu ve aygıtın
  piksel oranı; görüntü alanının genişliği tek başına seçmez.
- Aday dosyaların piksel sayısı genişliğin karesiyle büyür; kaynak boyutu yanlış bildirildiğinde
  seçilen dosya 2 ile 9 kat arası fazla piksel taşır.
- `picture` ögesinde koşulu doğru olan **ilk** kaynak seçilir; bu, biçem dosyasındaki "sonuncusu
  kazanır" kuralının tersidir ve kaynaklar geniş koşuldan dar koşula sıralanır.
- Kırpımların en boy oranları farklı olduğu için her kaynak kendi `width` ve `height` değerlerini
  bildirir; tek bir oran yazmak kaynak değiştiğinde içerik kayması üretir.
- `object-fit` ve `object-position`, kutunun oranı görselinkinden farklı olduğunda gerdirme
  yerine kırpma ve kırpma merkezi seçimi sağlar.

## Sonraki Adım

Buraya kadar biçem hep ortamın ölçülebilir niteliklerine uyarlandı: genişlik, kabın ölçüsü,
piksel oranı. Kullanıcının kendi tercihleri de aynı yolla okunabilir — koyu ya da açık renk
şeması isteyip istemediği, kontrastı artırmak isteyip istemediği, hareketin azaltılmasını
isteyip istemediği. Sonraki ders bu tercihleri ele alır ve sonuncusu bir soru bırakacaktır:
hareket azaltma tercihi tam olarak **neyi** azaltır?
