Ders 12 / 27
Renk ve Karşıtlık
Bileşen durumlarının kontrast eşiklerine karşı toplu denetimi, odak halkasının iki komşuya birden bakan sınavı, rengin tek kanal olamaması ve metin ölçeklemesinin getirdiği eşikler.
İçindekiler
Önceki ders bilginin görsel olmayan kanallardan nasıl aktarıldığını kurdu. Görsel kanalda karşılığı olan soru şudur: ekranda duran bir metin ya da bir bileşen sınırı, çevresinden ayırt edilebiliyor mu? Bu sorunun yanıtı ölçülebilir bir niceliğe bağlıdır ve bu, kalite ekseninde sayılabilir ölçütlerin en doğrudan olanıdır.
Kontrast oranının tanımı, formülü ve sınırları Arayüz Tasarımının Temelleri kursundaki Karşıtlık ve Erişilebilirlik dersinde kuruldu: iki rengin bağıl parlaklıklarının 0.05 kaydırmalı oranı, 1:1 ile 21:1 arasında değişir, tonu görmez ve doğrusal bir algı ölçüsü değildir. Bu ders aynı formülü gerçekleştirim tarafında kullanır ve tasarım denetiminin atladığı iki yeri ele alır: bileşenlerin durumları ve odak göstergesinin iki komşusu.
Durumlar da Denetlenir
Bir renk paleti denetlendiğinde denetlenen şey çoğunlukla varsayılan durumdur: metin rengi, zemin rengi, düğme dolgusu. Oysa bir bileşen ekranda tek bir renk çiftiyle durmaz. Düğmenin üzerine gelindiğinde dolgu değişir, devre dışı kaldığında hem dolgu hem metin değişir, odaklandığında etrafına bir halka gelir. Her durum yeni bir renk çiftidir ve her çift ayrı bir eşleşmedir.
Aşağıdaki betik ölçüm listesi arayüzünün bileşen durumlarını denetliyor.
// karsitlik.mjs — bilesen durumlarinin kontrast denetimi ve odak halkasinin iki yanli sinavi const cz = (h) => [1, 3, 5].map((i) => parseInt(h.slice(i, i + 2), 16)); function kanal(v) { const s = v / 255; return s <= 0.03928 ? s / 12.92 : Math.pow((s + 0.055) / 1.055, 2.4); } const parlaklik = ([r, g, b]) => 0.2126 * kanal(r) + 0.7152 * kanal(g) + 0.0722 * kanal(b); function kontrast(a, b) { const [x, y] = [parlaklik(cz(a)), parlaklik(cz(b))].sort((p, q) => q - p); return (x + 0.05) / (y + 0.05); } const RENK = { yuzey: "#ffffff", yuzeyIkincil: "#f2f4f7", metin: "#16202a", metinIkincil: "#5b6b7a", metinDevreDisi: "#9aa7b2", eylem: "#1f5fa8", eylemUzeri: "#ffffff", eylemVurgu: "#17497f", uyariZemin: "#fdf0c8", uyariMetin: "#6b4e00", kenarlik: "#c3ccd6", siralamaOku: "#b4bcc4", }; const PT_PX = 96 / 72; const buyukMu = (px, ag) => px >= 18 * PT_PX || (ag >= 700 && px >= 14 * PT_PX); // [ad, on plan, zemin, tur, px, agirlik] const DURUM = [ ["dugme varsayilan", "eylemUzeri", "eylem", "metin", 16, 600], ["dugme uzerine gelme", "eylemUzeri", "eylemVurgu", "metin", 16, 600], ["dugme devre disi", "metinDevreDisi", "yuzeyIkincil", "metin", 16, 600], ["suzgec etiketi", "metin", "yuzey", "metin", 16, 400], ["suzgec ipucu", "metinIkincil", "yuzey", "metin", 13, 400], ["tablo basligi", "metin", "yuzeyIkincil", "metin", 14, 700], ["satir metni", "metin", "yuzey", "metin", 16, 400], ["uyari rozeti", "uyariMetin", "uyariZemin", "metin", 13, 600], ["alan kenarligi", "kenarlik", "yuzey", "metin-disi", 0, 0], ["siralama gostergesi", "siralamaOku", "yuzeyIkincil", "metin-disi", 0, 0], ]; console.log("durum on plan zemin esik olculen sonuc"); let kalan = 0; for (const [ad, on, zemin, tur, px, ag] of DURUM) { const esik = tur === "metin-disi" ? 3.0 : buyukMu(px, ag) ? 3.0 : 4.5; const k = kontrast(RENK[on], RENK[zemin]); if (k < esik) kalan++; console.log( ad.padEnd(20) + " " + RENK[on].padEnd(10) + RENK[zemin].padEnd(10) + esik.toFixed(1).padStart(4) + k.toFixed(2).padStart(9) + " " + (k >= esik ? "gecti" : "KALDI"), ); } console.log(`${DURUM.length} eslesmenin ${kalan} tanesi esigi gecmiyor`); // Odak halkasi iki komsuya birden bakar: ic dolgu ve dis yuzey console.log("\ntek renkli odak halkasi, iki yanli sinav (esik 3.0):"); console.log("halka dolguya karsi yuzeye karsi sonuc"); for (const halka of ["#2a7de1", "#16202a", "#ffffff", "#000000"]) { const a = kontrast(halka, RENK.eylem); const b = kontrast(halka, RENK.yuzey); console.log( halka.padEnd(11) + a.toFixed(2).padStart(13) + b.toFixed(2).padStart(14) + " " + (a >= 3 && b >= 3 ? "gecti" : "KALDI"), ); } // Hangi griler iki yani birden gecer const uygunGriler = []; for (let v = 0; v <= 255; v++) { const g = "#" + v.toString(16).padStart(2, "0").repeat(3); if (kontrast(g, RENK.eylem) >= 3 && kontrast(g, RENK.yuzey) >= 3) uygunGriler.push(v); } console.log(`\niki yani birden gecen gri sayisi: ${uygunGriler.length} / 256`); console.log(` izin verilen aralik: ${uygunGriler[0]}–${uygunGriler[uygunGriler.length - 1]}`); // Iki bantli halka: her bant kendi komsusuna bakar const icBant = "#ffffff", disBant = "#16202a"; console.log("\niki bantli halka:"); console.log(` ic bant ${icBant} — dolguya (${RENK.eylem}) karsi ${kontrast(icBant, RENK.eylem).toFixed(2)}`); console.log(` dis bant ${disBant} — yuzeye (${RENK.yuzey}) karsi ${kontrast(disBant, RENK.yuzey).toFixed(2)}`); console.log(` bantlar arasi ${kontrast(icBant, disBant).toFixed(2)}`); // 1.4.1: bilgi yalniz renkle mi tasiniyor console.log("\nolcum niteligi gostergeleri (1.4.1):"); for (const [ad, kanallar] of [ ["esik asimi", ["renk"]], ["aygit hatasi", ["renk", "simge", "metin"]], ["elle girilmis", ["renk"]], ["dogrulanmis", ["renk", "metin"]], ]) { const tekKanal = kanallar.length === 1 && kanallar[0] === "renk"; console.log(` ${ad.padEnd(14)} kanallar: ${kanallar.join(", ").padEnd(22)} ${tekKanal ? "KALDI" : "gecti"}`); }
durum on plan zemin esik olculen sonuc dugme varsayilan #ffffff #1f5fa8 4.5 6.44 gecti dugme uzerine gelme #ffffff #17497f 4.5 9.15 gecti dugme devre disi #9aa7b2 #f2f4f7 4.5 2.23 KALDI suzgec etiketi #16202a #ffffff 4.5 16.48 gecti suzgec ipucu #5b6b7a #ffffff 4.5 5.48 gecti tablo basligi #16202a #f2f4f7 4.5 14.96 gecti satir metni #16202a #ffffff 4.5 16.48 gecti uyari rozeti #6b4e00 #fdf0c8 4.5 6.80 gecti alan kenarligi #c3ccd6 #ffffff 3.0 1.62 KALDI siralama gostergesi #b4bcc4 #f2f4f7 3.0 1.74 KALDI 10 eslesmenin 3 tanesi esigi gecmiyor tek renkli odak halkasi, iki yanli sinav (esik 3.0): halka dolguya karsi yuzeye karsi sonuc #2a7de1 1.57 4.10 KALDI #16202a 2.56 16.48 KALDI #ffffff 6.44 1.00 KALDI #000000 3.26 21.00 gecti iki yani birden gecen gri sayisi: 14 / 256 izin verilen aralik: 0–13 iki bantli halka: ic bant #ffffff — dolguya (#1f5fa8) karsi 6.44 dis bant #16202a — yuzeye (#ffffff) karsi 16.48 bantlar arasi 16.48 olcum niteligi gostergeleri (1.4.1): esik asimi kanallar: renk KALDI aygit hatasi kanallar: renk, simge, metin gecti elle girilmis kanallar: renk KALDI dogrulanmis kanallar: renk, metin gecti
Üzerine gelme durumu geçiyor, çünkü dolgu koyulaştığında beyaz metnin karşıtlığı artıyor. Ters yönde bir vurgu —dolguyu açan bir üzerine gelme durumu— aynı metinle eşiği kaçırırdı; durum listesinin denetlenmesinin nedeni budur.
Üç Başarısızlık
Devre dışı düğme 2.23 oranıyla kalıyor. Ölçüt etkin olmayan bileşenleri kapsam dışında bırakır, dolayısıyla bu satır bir uygunluk ihlali değildir. Ancak Klavye Erişimi dersinde kurulan kural burada ikinci bir maliyet çıkarır: devre dışı bir denetim odak da almaz. Kullanıcı düğmeyi ne okuyabilir ne de üzerine gidebilir; neden çalışmadığını öğrenmenin yolu kalmaz. Ölçüt izin verse de karar, düğmeyi etkin bırakıp basıldığında nedeni bildirmektir.
Alan kenarlığı 1.62 oranıyla kalıyor ve bu bir 1.4.11 ihlalidir. Süzgeç panelindeki arama alanının nerede başlayıp bittiğini gösteren tek şey o kenarlıktır: alan boşken içinde metin yoktur, zemini yüzeyle aynıdır. Kenarlık görünmüyorsa alan da görünmüyordur.
Sıralama göstergesi 1.74 oranıyla kalıyor. Sütunun hangi yönde sıralandığını bildiren
ok, metin dışı bir görsel bilgidir ve durumu tanımlamak için gereklidir. Önceki derste bu
bilgi aria-sort bildirimiyle ağaca yazılmıştı; ekranda gören kullanıcı için karşılığı
okun kendisidir ve ikisi aynı bilginin iki kanalıdır.
Odak Halkasının İki Komşusu
Odak göstergesi başka her şeyden farklı bir sorun çıkarır: bir ögenin etrafına çizilir ve iki farklı rengin sınırında durur. İçeride bileşenin dolgusu, dışarıda sayfanın yüzeyi vardır. Ölçüt komşu renklere karşı karşıtlık istediği için sınav iki yanlıdır.
Çıktının ikinci bölümü dört aday halkayı bu sınavdan geçiriyor. Bileşenin kendi ana rengiyle akraba bir mavi halka dolguya karşı 1.57 veriyor — düğmenin üstünde görünmez. Koyu bir halka yüzeye karşı 16.48 veriyor ama dolguya karşı 2.56 ile eşiğin altında kalıyor. Beyaz bir halka bunun tersi. Yalnızca saf siyah iki yanı birden geçiyor.
Gri taraması sınırın ne kadar dar olduğunu gösteriyor: 256 gri tonundan yalnızca 14’ü, yani 0 ile 13 arasındakiler, hem düğme dolgusuna hem beyaz yüzeye karşı 3:1 sağlıyor. Bu, tek renkli bir odak halkasının pratikte siyaha yakın olmak zorunda olduğu anlamına gelir — ve palette birden çok dolgu rengi varsa aralık daha da daralır, çoğunlukla boşalır.
Çözüm halkayı tek renkli bırakmamaktır. İki bantlı halkada iç bant bileşenin dolgusuna, dış bant sayfanın yüzeyine bakar; her bant yalnızca kendi komşusuna karşı eşiği sağlamak zorundadır. Çıktının son bölümü bunu veriyor: iç bant dolguya karşı 6.44, dış bant yüzeye karşı 16.48. Halka artık her iki tarafta da görünür ve bileşenin dolgu rengi değiştiğinde yalnızca iç bandın yeniden denetlenmesi yeter.
Renk Tek Kanal Olamaz
Son bölüm 1.4.1 ölçütünü uyguluyor ve kontrast hesabından bağımsızdır: bir bilgi rengin tek başına taşıdığı bir ayrımla veriliyorsa, o bilgi rengi ayırt edemeyen kullanıcıya ulaşmaz. Ölçüm tablosunda dört nitelik göstergesinden ikisi yalnızca satır rengiyle bildiriliyor.
İkinci kanal eklemenin üç yolu vardır: metin (rozetin içine “eşik aşımı” yazmak), biçim (satır başına bir işaret koymak) ve konum (uyarılı ölçümleri ayrı bir bölümde toplamak). Metin en güvenilir olanıdır, çünkü aynı zamanda erişilebilirlik ağacına da geçer — ikinci görsel kanal ile ağaçtaki bildirim tek hamlede sağlanır.
Buradan bir denetim kuralı çıkar: renkli her göstergenin yanında, o göstergenin taşıdığı bilgiyi renksiz bir belgede de aktarabilecek bir kanal bulunmalıdır. Sınama basittir — arayüzün ekran görüntüsü griye çevrildiğinde hangi bilgiler kayboluyor?
Metnin Ölçekleneceğini Varsaymak
İki ölçüt daha ölçülebilir ve kontrastla aynı denetime girer. 1.4.4 ölçütü metnin yüzde 200’e kadar büyütülebilmesini ve bu sırada içerik ya da işlev kaybı olmamasını ister. 1.4.12 ölçütü ise kullanıcının satır yüksekliğini, paragraf aralığını, harf ve sözcük aralığını belirli değerlere çıkarabilmesini ve metnin kırpılmamasını ister.
İkisinin de gerçekleştirim tarafındaki karşılığı aynıdır: metin taşıyan kapların yüksekliği sabitlenmez. Sabit yükseklikli bir rozet, iki katına çıkan yazı boyutunda metni kırpar; taşmayı gizleyen bir bildirim, kırpılan metni erişilemez kılar. Düzen Sistemleri ve Duyarlı Tasarım kursundaki içerik tabanlı boyutlandırma bu iki ölçütün doğrudan karşılığıdır.
Özet
- Kontrast denetimi varsayılan durumla sınırlı kalamaz; bir bileşenin her durumu yeni bir renk çiftidir ve ayrı bir eşleşme olarak denetlenir.
- Devre dışı bileşenler kontrast ölçütünden muaftır, ancak odak da alamadıkları için muafiyet çoğunlukla yanlış kararın örtüsüdür.
- Alan kenarlığı ve sıralama göstergesi gibi metin dışı görsel bilgiler 3:1 eşiğine tabidir; bunlar bileşenin varlığını ya da durumunu tanımlayan tek işarettir.
- Odak halkası iki komşu rengin sınırında durur; tek renkli bir halkanın iki yanı birden geçmesi çok dar bir aralıkta mümkündür, iki bantlı halka her bandı kendi komşusuna karşı sınar.
- Renk tek taşıyıcı olamaz; ikinci kanal metin, biçim ya da konumdur ve metin seçildiğinde aynı hamlede erişilebilirlik ağacına da geçer.
- Metnin iki katına büyütülebilmesi ve aralıklarının artırılabilmesi ölçülebilir gerekliliklerdir; karşılıkları sabit yükseklikli kaplardan kaçınmaktır.
Sonraki Adım
Bu dersteki denetimlerin tamamı bir betikle çalıştı: renk çiftleri girdi, geçen ve kalan eşleşmeler çıktı. Aynı şey önceki derslerin bir bölümü için de geçerliydi — erişilebilir ad hesabı, sekme sırası ve düzlem kusurları da hesaplanabilirdi. Bu, kaçınılmaz bir soruyu getirir: erişilebilirliğin ne kadarı otomatik denetlenebilir? Sonraki ders bu sorunun sayısal yanıtını verir, otomatik denetleyicinin yapı olarak göremediği ölçüt sınıflarını adlandırır ve elle doğrulamanın hangi sırayla yapılacağını kurar.
İlerlemeni kaydetmek ve not almak için Giriş yap
Notlarım
Not almak için giriş yapmalısın.