Ders 08 / 23
Ortam Sorguları
Ortam sorgusunun bileşenleri, kuralların basamaklamadaki yeri, iki aralığın ucundaki çakışma ve boşluk, sorgu birimlerinin kullanıcı yazı boyutuyla ilişkisi ve özellik sorguları.
İçindekiler
Bir önceki ders türetilen eşiği verdi: 812 birim. Geriye bu sayının biçem dosyasında nasıl yazılacağı kalıyor.
Koşullu biçemin aracı ortam sorgusudur (media query). Sorgu bir soru sorar, yanıt doğruysa içindeki kurallar hesaba katılır. Bu ders sorunun neye bakabildiğini, kuralların basamaklamada nereye oturduğunu ve iki aralığın birleştiği noktada ne olduğunu ele alır.
Sorgunun Bileşenleri
Bir ortam sorgusu üç parçadan kurulur.
Ortam türü (media type), çıktının hangi ortama gittiğini söyler: screen ekran, print
yazdırma ya da yazdırma önizlemesi, all her ikisi. Yazılmazsa all varsayılır.
Ortam özelliği (media feature), ortamın ölçülebilir bir niteliğini sınar ve parantez içinde
yazılır. En sık kullanılanlar width ve height; bunların yanında ekranın yönünü veren
orientation, en boy oranını veren aspect-ratio, piksel yoğunluğunu veren resolution
bulunur. Bir başka grup, ekran ölçüsünü değil giriş yeteneğini sınar: pointer işaretleme
aygıtının inceliğini, hover üzerine gelme davranışının bulunup bulunmadığını bildirir.
Mantıksal işleçler. and iki koşulu birlikte ister, virgül ise “ya da” anlamındadır; virgülle
ayrılmış dallardan biri doğruysa sorgu doğrudur. not bir sorguyu tersine çevirir.
Özellik sınamasının iki yazımı vardır. Eskisi min- ve max- öneklerini kullanır ve kapalı
aralık üretir: (min-width: 812px) bildirimi “genişlik 812’ye eşit ya da büyük” demektir.
Yenisi karşılaştırma işleçlerini doğrudan yazar: (width >= 812px). İkinci yazım açık aralık
da kurabilir ve iki sınırı tek koşulda birleştirebilir.
Değerlendirme ve Basamaklama
Sorgunun sonucu ile kuralın uygulanması iki ayrı adımdır. Sorgu doğruysa içindeki kurallar basamaklamaya katılır; hangi bildirimin kazandığına basamaklama karar verir.
Burada sık yapılan bir varsayım yanlıştır: ortam sorgusu özgüllük eklemez. Sorgunun içindeki
.istasyon-duzeni seçicisi ile dışındaki .istasyon-duzeni seçicisi aynı özgüllüktedir; ikisi
çakıştığında sonra yazılan kazanır.
// sorgu.mjs — ortam sorgusu degerlendiricisi ve basamaklama sonucu // Desteklenen yazimlar: (min-width: Npx), (max-width: Npx), (width >= Npx), // (Npx <= width < Mpx), (orientation: portrait|landscape), and, virgul, not. function ozellikDegeri(ad, ortam) { if (ad === "width") return ortam.genislik; if (ad === "height") return ortam.yukseklik; if (ad === "orientation") return ortam.genislik >= ortam.yukseklik ? "landscape" : "portrait"; throw new Error(`bilinmeyen ozellik: ${ad}`); } function ozellik(metin, ortam) { const s = metin.trim().replace(/^\(/, "").replace(/\)$/, "").trim(); let m = s.match(/^(\d+(?:\.\d+)?)px\s*(<=|<)\s*([a-z-]+)\s*(<=|<)\s*(\d+(?:\.\d+)?)px$/); if (m) { const v = ozellikDegeri(m[3], ortam); const altOk = m[2] === "<=" ? Number(m[1]) <= v : Number(m[1]) < v; const ustOk = m[4] === "<=" ? v <= Number(m[5]) : v < Number(m[5]); return altOk && ustOk; } m = s.match(/^([a-z-]+)\s*(>=|<=|>|<)\s*(\d+(?:\.\d+)?)px$/); if (m) { const v = ozellikDegeri(m[1], ortam); const n = Number(m[3]); return m[2] === ">=" ? v >= n : m[2] === "<=" ? v <= n : m[2] === ">" ? v > n : v < n; } m = s.match(/^(min-|max-)?([a-z-]+)\s*:\s*(.+)$/); if (m) { const v = ozellikDegeri(m[2], ortam); const hedef = m[3].endsWith("px") ? Number(m[3].slice(0, -2)) : m[3]; if (m[1] === "min-") return v >= hedef; if (m[1] === "max-") return v <= hedef; return v === hedef; } throw new Error(`cozulemeyen kosul: ${s}`); } function sorguEslesir(sorgu, ortam) { return sorgu.split(",").some((dal) => { const parcalar = dal.trim().split(/\s+and\s+/); const olumsuz = parcalar[0].startsWith("not "); parcalar[0] = parcalar[0].replace(/^not\s+/, ""); const sonuc = parcalar.every((p) => ozellik(p, ortam)); return olumsuz ? !sonuc : sonuc; }); } const ortamlar = [ { ad: "dar dikey", genislik: 380, yukseklik: 780 }, { ad: "orta dikey", genislik: 720, yukseklik: 1024 }, { ad: "esik ustu", genislik: 812, yukseklik: 1000 }, { ad: "genis yatay", genislik: 1280, yukseklik: 800 }, { ad: "alcak yatay", genislik: 900, yukseklik: 420 }, ]; const kurallar = [ { sorgu: null, deger: "tek sutun" }, { sorgu: "(min-width: 812px)", deger: "iki sutun" }, { sorgu: "(min-width: 1200px)", deger: "iki sutun + genis kenar" }, { sorgu: "(orientation: landscape) and (max-height: 480px)", deger: "sikisik baslik" }, ]; console.log("--- kurallar kaynak sirasinda, sonuncu eslesen kazanir ---"); kurallar.forEach((k, i) => console.log(`${i + 1}. ${k.sorgu ?? "(taban, sorgusuz)"} -> ${k.deger}`)); console.log("\nortam genislik yukseklik eslesen kurallar uygulanan"); for (const o of ortamlar) { const eslesen = kurallar .map((k, i) => (k.sorgu === null || sorguEslesir(k.sorgu, o) ? i + 1 : null)) .filter((x) => x !== null); const kazanan = kurallar[eslesen[eslesen.length - 1] - 1].deger; console.log( `${o.ad.padEnd(15)} ${String(o.genislik).padStart(8)} ${String(o.yukseklik).padStart(9)} ` + `${eslesen.join(",").padStart(16)} ${kazanan}`, ); } console.log("\n--- iki araligin ucundaki genislik ---"); const cift = [ ["(max-width: 812px)", "(min-width: 812px)"], ["(max-width: 811.98px)", "(min-width: 812px)"], ["(width < 812px)", "(width >= 812px)"], ]; for (const [a, b] of cift) { console.log(`\n${a} | ${b}`); console.log("genislik ilk ikinci durum"); for (const g of [811, 811.99, 812, 813]) { const o = { genislik: g, yukseklik: 900 }; const x = sorguEslesir(a, o); const y = sorguEslesir(b, o); const durum = x && y ? "IKISI DE eslesti" : x || y ? "tek kural" : "HICBIRI eslesmedi"; console.log( `${String(g).padStart(8)} ${String(x).padStart(5)} ${String(y).padStart(6)} ${durum}`, ); } }
--- kurallar kaynak sirasinda, sonuncu eslesen kazanir ---
1. (taban, sorgusuz) -> tek sutun
2. (min-width: 812px) -> iki sutun
3. (min-width: 1200px) -> iki sutun + genis kenar
4. (orientation: landscape) and (max-height: 480px) -> sikisik baslik
ortam genislik yukseklik eslesen kurallar uygulanan
dar dikey 380 780 1 tek sutun
orta dikey 720 1024 1 tek sutun
esik ustu 812 1000 1,2 iki sutun
genis yatay 1280 800 1,2,3 iki sutun + genis kenar
alcak yatay 900 420 1,2,4 sikisik baslik
--- iki araligin ucundaki genislik ---
(max-width: 812px) | (min-width: 812px)
genislik ilk ikinci durum
811 true false tek kural
811.99 true false tek kural
812 true true IKISI DE eslesti
813 false true tek kural
(max-width: 811.98px) | (min-width: 812px)
genislik ilk ikinci durum
811 true false tek kural
811.99 false false HICBIRI eslesmedi
812 false true tek kural
813 false true tek kural
(width < 812px) | (width >= 812px)
genislik ilk ikinci durum
811 true false tek kural
811.99 true false tek kural
812 false true tek kural
813 false true tek kural
Birinci tablo eşleşmenin biriktiğini gösteriyor. 1280 birimlik bir ortamda üç kural birden doğrudur; uygulanan değer, doğru olanların sonuncusudur. Bu, kuralların yazım sırasının anlam taşıdığı anlamına gelir: aynı bildirimi hedefleyen sorgular dar aralıktan geniş aralığa doğru sıralanmazsa geniş aralığın kuralı dar aralığınki tarafından ezilir.
Dördüncü kural farklı bir eksende çalışıyor. 900 birim genişliğindeki alçak bir ortamda hem genişlik koşulu hem yön ve yükseklik koşulu doğru; sonuncusu kazanıyor. Genişlik tek ölçü değildir: yatay tutulan ve dikeyde yeri kısıtlı bir ortamda başlığın alçalması, genişlikle ilgisi olmayan bir karardır.
Aralıkların Ucu
İkinci bölüm, iki komşu aralığın birleştiği noktayı üç yazımla sınıyor.
Birinci çift klasik hatadır. max-width: 812px ve min-width: 812px yazımlarının ikisi de
812 birimde doğrudur, çünkü iki önek de kapalı sınır üretir. Tam o genişlikte iki kural
birden uygulanır ve sonuç kaynak sırasına kalır. Hata görünmez olabilir — sonuç zaten
istenen kural olabilir — ama bildirimler farklıysa ikisinin karışımı uygulanır.
İkinci çift, sık önerilen düzeltmeyi deniyor: üst sınırı biraz aşağı çekmek. Çakışma kalkıyor ama yerine bir boşluk geliyor. 811.99 birimlik bir genişlikte hiçbir kural eşleşmiyor. Kesirli genişlikler kuramsal bir olasılık değildir; yakınlaştırma ve ölçeklendirme kesirli CSS piksel değerleri üretir.
Üçüncü çift, karşılaştırma yazımıyla aralığı gerçekten böler. (width < 812px) ve
(width >= 812px) birbirini dışlar ve birlikte bütün sayı doğrusunu kaplar. Ne çakışma ne
boşluk kalır.
En sağlam yol, ikinci aralığı hiç yazmamaktır. Taban biçem dar düzeni tarif ediyorsa yalnız
(min-width: 812px) yazılır; alt aralık için kural gerekmez ve uç noktası sorunu doğmaz.
Sorgudaki Birim
Uzunluk yazılan her yerde birim seçimi bir karardır. Ortam sorgusunda bu kararın alışılmadık
bir sonucu vardır: sorgudaki em, kök ögeye yazılan yazı boyutuna göre değil, kullanıcının
varsayılan yazı boyutuna göre çözülür.
// birim.mjs — ortam sorgusundaki uzunluk biriminin kullanici yazi boyutuyla iliskisi // Sorgudaki em, kok ogeye yazilan font-size'a degil, kullanicinin varsayilan // yazi boyutuna gore cozulur. // 16px varsayilanda olculen icerik gereksinimleri const ANA = 480, YAN = 260, ARALIK = 24, IC_BOSLUK = 48; const TABAN = 16; const GEREKEN_16 = ANA + YAN + ARALIK + IC_BOSLUK; const emEsik = GEREKEN_16 / TABAN; // ayni esigin em karsiligi const pxEsik = GEREKEN_16; console.log(`16px varsayilanda iki sutun icin gereken genislik: ${GEREKEN_16}px`); console.log(`px yazilirsa: (min-width: ${pxEsik}px)`); console.log(`em yazilirsa: (min-width: ${emEsik}em)\n`); console.log("kullanici gereken px sorgusu em sorgusu px ile sapma em ile sapma"); console.log("varsayilan genislik tetiklenir tetiklenir"); for (const d of [12, 16, 20, 24, 32]) { const gereken = (GEREKEN_16 * d) / TABAN; // butun olculer rem tabanli oldugu icin olceklenir const pxTetik = pxEsik; const emTetik = emEsik * d; console.log( `${String(d + "px").padStart(10)} ${gereken.toFixed(0).padStart(8)} ` + `${pxTetik.toFixed(0).padStart(10)} ${emTetik.toFixed(0).padStart(10)} ` + `${(pxTetik - gereken).toFixed(0).padStart(12)} ${(emTetik - gereken).toFixed(0).padStart(12)}`, ); } console.log("\n--- 20px varsayilanda 812px genisliginde ne oluyor ---"); const d = 20; const olcek = d / TABAN; const alan = 812 - IC_BOSLUK * olcek - ARALIK * olcek; const ana = (alan * 2) / 3, yan = alan / 3; console.log(`ana bolum: ${ana.toFixed(1)}px (gereken ${(ANA * olcek).toFixed(1)}px)`); console.log(`yan bilgi: ${yan.toFixed(1)}px (gereken ${(YAN * olcek).toFixed(1)}px)`); console.log(`px sorgusu bu genislikte iki sutunu acar; em sorgusu ${(emEsik * d).toFixed(0)}px degerine kadar beklemis olur`);
16px varsayilanda iki sutun icin gereken genislik: 812px
px yazilirsa: (min-width: 812px)
em yazilirsa: (min-width: 50.75em)
kullanici gereken px sorgusu em sorgusu px ile sapma em ile sapma
varsayilan genislik tetiklenir tetiklenir
12px 609 812 609 203 0
16px 812 812 812 0 0
20px 1015 812 1015 -203 0
24px 1218 812 1218 -406 0
32px 1624 812 1624 -812 0
--- 20px varsayilanda 812px genisliginde ne oluyor ---
ana bolum: 481.3px (gereken 600.0px)
yan bilgi: 240.7px (gereken 325.0px)
px sorgusu bu genislikte iki sutunu acar; em sorgusu 1015px degerine kadar beklemis olur
Sağdaki iki sütun karşılaştırmayı tek başına yapıyor. Kullanıcı varsayılan yazı boyutunu değiştirdiğinde bütün ölçüler taban birime bağlı olduğu için birlikte büyür; iki sütunun gerektirdiği genişlik de büyür. Piksel yazılan sorgu bu değişimi göremez ve eşiği yerinde bırakır: 20 birimlik bir varsayılanda düzen, ihtiyacın 203 birim altında iki sütuna geçer. Taban birim yazılan sorgu ihtiyaçla birlikte kayar ve sapma her satırda sıfır kalır.
Son blok, sapmanın somut karşılığını veriyor: eşik erken tetiklendiğinde ana bölüm 481, yan bilgi 241 birime düşer; oysa büyütülmüş yazıyla aynı içeriğin 600 ve 325 birime ihtiyacı vardır. Yazı büyütmek, sayfayı okunur kılmak için yapılan bir ayardır; düzen bu ayarı ölçeğe katmazsa ayarın kendisi sonucu bozar.
Kural şudur: ortam sorgusunda uzunluk taban birimle yazılır. Aynı gerekçe yükseklik ve en boy oranı sınamaları için de geçerlidir.
Ortam Türleri ve Özellik Sorguları
Genişlik dışındaki iki sınama, ortam sorgularının yalnız ekran ölçüsüyle ilgili olmadığını gösterir.
@media print kuralları yazdırma çıktısına uygulanır. Yazdırılan bir ölçüm sayfasında gezinme
çubuğunun, süzgeç alanının ve bağlantı süslemelerinin yeri yoktur; buna karşılık bağlantı
adreslerinin metne yazılması ve sayfa kırılmalarının denetlenmesi gerekir. Çok sütunlu düzen
dersinde tanıtılan break-inside bildirimi burada da çalışır.
Ortam sorgularından ayrı ama aynı biçimde yazılan ikinci bir yapı, özellik sorgusudur
(feature query). @supports bir bildirimin tanınıp tanınmadığını sınar; ortamı değil, biçem
dilinin çözülmesini sorgular. Izgara ve Esnek Kutu Seçimi dersinde bırakılan soru buradan
yanıtlanır: subgrid değerinin tanındığı yerde iç ızgara kapsayanın hatlarını devralır,
tanınmadığı yerde kendi şeritlerini tanımlar.
/* duzen.css — 8. adım: türetilen eşik ve yazdırma */ @media (min-width: 50.75em) { .istasyon-duzeni { grid-template-columns: minmax(480px, 2fr) minmax(260px, 1fr); grid-template-areas: "kunye kunye" "olcumler yan-bilgi" "konum yan-bilgi"; } } @supports (grid-template-columns: subgrid) { .olcum-karti { display: grid; grid-template-rows: subgrid; grid-row: span 3; } } @media print { .gezinme, .olcum-suzgeci { display: none; } .istasyon-duzeni { grid-template-columns: minmax(0, 1fr); grid-template-areas: "kunye" "olcumler" "konum" "yan-bilgi"; } .olcum-cizelgesi tr { break-inside: avoid; } }
Üç blok, üç ayrı soru soruyor. Birincisi ortamın genişliğini sınıyor ve yalnız yapıyı değiştiriyor; ölçüler taban biçemdeki iç boyutlandırmadan geliyor. İkincisi bir bildirimin tanınıp tanınmadığını sınıyor ve tanınıyorsa kart satırlarını dış ızgaranın hatlarına bağlıyor. Üçüncüsü çıktının nereye gittiğini sınıyor.
Üçünün ortak yanı, hiçbirinin taban biçemde yazılanı geri almamasıdır. Her blok kendi koşulunda yeni bir bildirim ekler.
Özet
- Ortam sorgusu ortam türü, parantez içinde yazılan ortam özellikleri ve
and, virgül,notişleçlerinden kurulur; yazılmayan türallsayılır. - Sorgu özgüllük eklemez; içindeki kurallar dışarıdakilerle aynı özgüllükte yarışır ve çakışmayı kaynak sırası çözer.
min-vemax-önekleri kapalı sınır üretir: aynı sayıya yazılan iki sorgu o noktada birlikte eşleşir; sınırı kesirle kaydırmak çakışmayı boşluğa çevirir.- Karşılaştırma yazımı aralığı çakışmasız ve boşluksuz böler; tek yönlü tek bir sorgu yazmak ise sorunu tamamen ortadan kaldırır.
- Sorgudaki taban birim kullanıcının varsayılan yazı boyutuna göre çözülür ve eşiği içerikle birlikte kaydırır; piksel yazılan eşik bu ayarı göremez.
@media printçıktının ortamını,@supportsbiçem dilinin bir bildirimi tanıyıp tanımadığını sınar; ikisi de taban biçemi geri almadan bildirim ekler.
Sonraki Adım
Bu dersteki bütün sorgular tek bir şeye bakıyordu: görüntü alanının kendisi. Oysa ölçüm kartının nasıl görünmesi gerektiğini belirleyen, ekranın genişliği değil kartın içine yerleştirildiği yerin genişliğidir. Aynı kart geniş bir ekranda dar bir yan sütunda durabilir; görüntü alanı geniş olduğu için kart geniş düzenini açar ve sıkışır. Sonraki ders sorunun öznesini değiştirir: bir bileşen, bulunduğu kabın ölçüsünü nasıl sorgular?
İlerlemeni kaydetmek ve not almak için Giriş yap
Notlarım
Not almak için giriş yapmalısın.