İçeriğe geç
academia.sh

Ders 12 / 23

Kullanıcı Tercihleri

Renk şeması, kontrast, zorunlu renkler ve hareket azaltma tercihlerinin sorgulanması, koyu paletin kontrast denetimi, tercihlerin birlikte çözülmesi ve hareket azaltmanın neyi kapsadığı.

İçindekiler

Buraya kadar biçem hep ortamın ölçülebilir niteliklerine uyarlandı: görüntü alanının genişliği, kabın ölçüsü, aygıtın piksel oranı. Bunların hepsi aygıtın özellikleriydi.

Kullanıcının kendisi de bir tercih bildirir. İşletim sistemi ya da tarayıcı ayarlarında koyu bir renk şeması seçmiş, kontrastın artırılmasını istemiş ya da hareketin azaltılmasını istemiş olabilir. Bu tercihler aynı sorgu yazımıyla okunur ve sayfa bunlara uyar.

Tercih Bir Ortam Özelliğidir

Dört ortam özelliği kullanıcı tercihini bildirir.

prefers-color-scheme açık ya da koyu bir yüzey tercihini verir; değerleri light, dark ve no-preference. prefers-contrast kontrastın artırılması ya da azaltılması isteğini bildirir. prefers-reduced-motion hareketin azaltılması isteğini taşır ve tek anlamlı değeri reduce. forced-colors ise ayrı bir durumu bildirir: kullanıcı, sayfanın renklerinin yerine kendi belirlediği sınırlı bir palete zorlanmasını istemiştir.

Dördü de ortam sorgularıyla aynı biçimde yazılır ve aynı basamaklama kurallarına uyar. Ayrım soruda değil öznededir: genişlik aygıtın, tercih kullanıcınındır.

Bir tasarım kararı hemen ortaya çıkar. Tercih sorguları biçemi eklemeli yazılmaya zorlar: taban biçem no-preference durumunu tarif eder, sorgu bloğu yalnız değişeni yazar. Ters yönde yazıldığında — koyu şema taban alınıp açık şema sorguyla geri alındığında — tercih bildirmemiş bir kullanıcı, tasarımın istisnası olur.

Koyu Şemanın Kontrast Denetimi

Koyu şema, açık şemanın renklerinin ters çevrilmesi değildir. Renklerin bağıl parlaklıkları doğrusal olmadığı için, zeminin değişmesi ön plandaki her rengin oranını yeniden hesaplamayı gerektirir.

CSS ile Görsel Sunum kursunda tanıtılan kontrast oranı, iki rengin bağıl parlaklıklarından hesaplanır. Aşağıdaki program istasyon paletini iki şemada denetliyor.

// kontrast.mjs — acik ve koyu renk semasinin kontrast oranlarinin denetimi
// Bagil parlaklik ve kontrast orani WCAG tanimina gore hesaplanir.

function hslRgb(h, s, l) {
  s /= 100; l /= 100;
  const k = (n) => (n + h / 30) % 12;
  const a = s * Math.min(l, 1 - l);
  const f = (n) => l - a * Math.max(-1, Math.min(k(n) - 3, Math.min(9 - k(n), 1)));
  return [f(0), f(8), f(4)];
}

function bagilParlaklik([r, g, b]) {
  const d = (c) => (c <= 0.03928 ? c / 12.92 : Math.pow((c + 0.055) / 1.055, 2.4));
  return 0.2126 * d(r) + 0.7152 * d(g) + 0.0722 * d(b);
}

function oran(renk1, renk2) {
  const l1 = bagilParlaklik(renk1), l2 = bagilParlaklik(renk2);
  const [buyuk, kucuk] = l1 > l2 ? [l1, l2] : [l2, l1];
  return (buyuk + 0.05) / (kucuk + 0.05);
}

// acik sema: CSS ile Gorsel Sunum kursunda kurulan palet
const ACIK = {
  zemin: [203, 18, 97],
  metin: [211, 29, 16],
  metinSoluk: [211, 12, 45],
  uyari: [0, 66, 33],
  cizgi: [207, 15, 86],
};

// koyu sema, yalniz zemin ve ana metin degistirilmis; kalanlar oldugu gibi birakilmis
const KOYU_EKSIK = {
  zemin: [211, 22, 12],
  metin: [211, 20, 92],
  metinSoluk: ACIK.metinSoluk,
  uyari: ACIK.uyari,
  cizgi: ACIK.cizgi,
};

const HEDEF = { metin: 4.5, metinSoluk: 4.5, uyari: 4.5, cizgi: 3 };

function tablo(baslik, sema) {
  console.log(`\n--- ${baslik} ---`);
  console.log(`zemin: hsl(${sema.zemin.join(" ")})`);
  console.log("on plan      hsl           hedef   oran   sonuc");
  for (const ad of ["metin", "metinSoluk", "uyari", "cizgi"]) {
    const o = oran(hslRgb(...sema[ad]), hslRgb(...sema.zemin));
    console.log(
      `${ad.padEnd(11)}  ${("hsl(" + sema[ad].join(" ") + ")").padEnd(14)}` +
        `${(HEDEF[ad] + ":1").padStart(6)}  ${o.toFixed(2).padStart(5)}  ${(o >= HEDEF[ad] ? "gecer" : "KALIR").padStart(5)}`,
    );
  }
}

tablo("acik sema", ACIK);
tablo("koyu sema: yalniz zemin ve metin degistirilmis", KOYU_EKSIK);

// duzeltme: her on plan icin hedefi saglayan en dusuk aydinlik degeri aranir
console.log("\n--- koyu semada gereken aydinlik degerleri ---");
console.log("on plan      su anki L  gereken L  su anki oran  duzeltilmis oran");
for (const ad of ["metin", "metinSoluk", "uyari", "cizgi"]) {
  const [h, s, l] = KOYU_EKSIK[ad];
  let gereken = l;
  while (gereken <= 100 && oran(hslRgb(h, s, gereken), hslRgb(...KOYU_EKSIK.zemin)) < HEDEF[ad]) {
    gereken += 0.5;
  }
  console.log(
    `${ad.padEnd(11)}  ${l.toFixed(1).padStart(9)}  ${gereken.toFixed(1).padStart(9)}  ` +
      `${oran(hslRgb(h, s, l), hslRgb(...KOYU_EKSIK.zemin)).toFixed(2).padStart(12)}  ` +
      `${oran(hslRgb(h, s, gereken), hslRgb(...KOYU_EKSIK.zemin)).toFixed(2).padStart(16)}`,
  );
}
--- acik sema ---
zemin: hsl(203 18 97)
on plan      hsl           hedef   oran   sonuc
metin        hsl(211 29 16) 4.5:1  13.93  gecer
metinSoluk   hsl(211 12 45) 4.5:1   4.59  gecer
uyari        hsl(0 66 33)   4.5:1   8.56  gecer
cizgi        hsl(207 15 86)   3:1   1.30  KALIR

--- koyu sema: yalniz zemin ve metin degistirilmis ---
zemin: hsl(211 22 12)
on plan      hsl           hedef   oran   sonuc
metin        hsl(211 20 92) 4.5:1  13.90  gecer
metinSoluk   hsl(211 12 45) 4.5:1   3.42  KALIR
uyari        hsl(0 66 33)   4.5:1   1.83  KALIR
cizgi        hsl(207 15 86)   3:1  12.08  gecer

--- koyu semada gereken aydinlik degerleri ---
on plan      su anki L  gereken L  su anki oran  duzeltilmis oran
metin             92.0       92.0         13.90             13.90
metinSoluk        45.0       53.0          3.42              4.54
uyari             33.0       61.0          1.83              4.53
cizgi             86.0       86.0         12.08             12.08

Açık şemada üç metin rengi de 4,5:1 ölçütünü karşılıyor. Ayraç çizgisi 1,30 oranıyla 3:1 ölçütünün altında — bu bir kusur değil, kapsam sorusudur. WCAG 1.4.11 ölçütü metin dışı kontrastı arayüz bileşenlerinin ve durum göstergelerinin sınırları için ister; bir çizelgedeki satır ayracı süsleyicidir. Aynı renk bir girdi kenarlığı ya da odak göstergesi olarak kullanılacaksa ölçütü karşılamaz ve koyulaştırılması gerekir.

Koyu şemada tablo tersine dönüyor. Zemin ve ana metin değiştirilmiş; ikisi arasında 13,90 oran var ve sorun görünmüyor. Ama değiştirilmemiş iki renk düşüyor: soluk metin 3,42, uyarı rengi 1,83. Uyarı rengi, kullanıcıyı uyarması gereken renktir ve koyu zeminde neredeyse okunamaz.

Üçüncü blok düzeltmeyi hesaplıyor. Soluk metnin aydınlık değeri 45’ten 53’e, uyarı renginin 33’ten 61’e çıkarıldığında ölçüt karşılanıyor. Uyarı renginde gereken 28 puanlık artış, doğrudan çevirmenin neden yetmediğini gösteriyor: kırmızının bağıl parlaklığı düşüktür, koyu zeminde ayırt edilmesi için çok daha açık bir tona çıkması gerekir.

Kural şudur: koyu şema, açık şemanın her renginin yeniden hesaplanmasıdır. Değiştirilmeyen renk, sessizce ölçüt dışına düşen renktir.

Tercihlerin Birlikte Çözülmesi

Kullanıcı birden çok tercihi aynı anda bildirebilir. Sorgular birbirinden bağımsız olduğu için sonuç, eşleşen bütün blokların basamaklamadaki birleşimidir.

// tercih.mjs — kullanici tercihi sorgularinin cozulmesi ve sonucun belirlenmesi

// Kurallar kaynak sirasinda; kosulu dogru olanlarin sonuncusu kazanir.
const KURALLAR = [
  { kosul: null, deger: { zemin: "acik", cizgi: "ince", gecis: "200ms", kaydirma: "yumusak" } },
  { kosul: "prefers-color-scheme: dark", deger: { zemin: "koyu" } },
  { kosul: "prefers-contrast: more", deger: { cizgi: "kalin" } },
  { kosul: "prefers-reduced-motion: reduce", deger: { gecis: "1ms", kaydirma: "ani" } },
  { kosul: "forced-colors: active", deger: { zemin: "sistem", cizgi: "sistem" } },
];

const PROFILLER = [
  { ad: "varsayilan", "prefers-color-scheme": "light", "prefers-contrast": "no-preference", "prefers-reduced-motion": "no-preference", "forced-colors": "none" },
  { ad: "koyu sema", "prefers-color-scheme": "dark", "prefers-contrast": "no-preference", "prefers-reduced-motion": "no-preference", "forced-colors": "none" },
  { ad: "koyu + az hareket", "prefers-color-scheme": "dark", "prefers-contrast": "no-preference", "prefers-reduced-motion": "reduce", "forced-colors": "none" },
  { ad: "yuksek kontrast", "prefers-color-scheme": "light", "prefers-contrast": "more", "prefers-reduced-motion": "no-preference", "forced-colors": "none" },
  { ad: "zorunlu renkler", "prefers-color-scheme": "dark", "prefers-contrast": "more", "prefers-reduced-motion": "reduce", "forced-colors": "active" },
];

function eslesir(kosul, profil) {
  if (kosul === null) return true;
  const [ozellik, deger] = kosul.split(":").map((s) => s.trim());
  return profil[ozellik] === deger;
}

function coz(profil) {
  const sonuc = {};
  const eslesen = [];
  for (const k of KURALLAR) {
    if (!eslesir(k.kosul, profil)) continue;
    eslesen.push(k.kosul ?? "taban");
    Object.assign(sonuc, k.deger);
  }
  return { sonuc, eslesen };
}

console.log("kurallar:");
for (const k of KURALLAR) {
  console.log(`  ${(k.kosul ?? "(taban)").padEnd(32)} ${JSON.stringify(k.deger)}`);
}

console.log("\nprofil             zemin    cizgi    gecis  kaydirma  eslesen kural sayisi");
for (const p of PROFILLER) {
  const { sonuc, eslesen } = coz(p);
  console.log(
    `${p.ad.padEnd(17)}  ${sonuc.zemin.padEnd(7)}  ${sonuc.cizgi.padEnd(7)}  ` +
      `${sonuc.gecis.padStart(5)}  ${sonuc.kaydirma.padEnd(8)}  ${String(eslesen.length).padStart(19)}`,
  );
}

// color-scheme bildirimi: tarayici denetimleri ve kaydirma cubugu hangi semayi kullanir
console.log("\n--- color-scheme bildiriminin tarayici denetimlerine etkisi ---");
console.log("kullanici tercihi  color-scheme bildirimi  denetimler  sayfa    uyumlu mu");
for (const tercih of ["light", "dark"]) {
  for (const bildirim of ["normal", "light", "light dark"]) {
    const destekli = bildirim.split(" ").includes(tercih);
    const denetim = destekli ? tercih : bildirim === "normal" ? "light" : bildirim.split(" ")[0];
    const sayfa = tercih;   // sayfanin kendi renkleri sorguyla zaten uyarlanmis
    console.log(
      `${tercih.padEnd(17)}  ${bildirim.padEnd(22)}  ${denetim.padEnd(10)}  ${sayfa.padEnd(7)}  ` +
        `${(denetim === sayfa ? "evet" : "HAYIR").padStart(9)}`,
    );
  }
}

// hareket azaltma neyi azaltir: ozellik turune gore politika
console.log("\n--- hareket azaltma tercihinde ozellik turlerine gore sure ---");
const HAREKETLER = [
  { ad: "translate/scale", tur: "yer degistirme", taban: 300 },
  { ad: "rotate", tur: "yer degistirme", taban: 400 },
  { ad: "opacity", tur: "gorunurluk", taban: 200 },
  { ad: "background-color", tur: "renk", taban: 150 },
  { ad: "surekli donen bant", tur: "surekli hareket", taban: 8000 },
];
console.log("ozellik                 tur               taban   azaltilmis  karar");
for (const h of HAREKETLER) {
  const kapatilir = h.tur === "yer degistirme" || h.tur === "surekli hareket";
  const yeni = kapatilir ? 0 : h.taban;
  const karar = kapatilir ? "kaldirilir" : "korunur";
  console.log(
    `${h.ad.padEnd(22)}  ${h.tur.padEnd(16)}  ${String(h.taban + "ms").padStart(6)}  ` +
      `${String(yeni + "ms").padStart(10)}  ${karar.padStart(11)}`,
  );
}
kurallar:
  (taban)                          {"zemin":"acik","cizgi":"ince","gecis":"200ms","kaydirma":"yumusak"}
  prefers-color-scheme: dark       {"zemin":"koyu"}
  prefers-contrast: more           {"cizgi":"kalin"}
  prefers-reduced-motion: reduce   {"gecis":"1ms","kaydirma":"ani"}
  forced-colors: active            {"zemin":"sistem","cizgi":"sistem"}

profil             zemin    cizgi    gecis  kaydirma  eslesen kural sayisi
varsayilan         acik     ince     200ms  yumusak                     1
koyu sema          koyu     ince     200ms  yumusak                     2
koyu + az hareket  koyu     ince       1ms  ani                         3
yuksek kontrast    acik     kalin    200ms  yumusak                     2
zorunlu renkler    sistem   sistem     1ms  ani                         5

--- color-scheme bildiriminin tarayici denetimlerine etkisi ---
kullanici tercihi  color-scheme bildirimi  denetimler  sayfa    uyumlu mu
light              normal                  light       light         evet
light              light                   light       light         evet
light              light dark              light       light         evet
dark               normal                  light       dark         HAYIR
dark               light                   light       dark         HAYIR
dark               light dark              dark        dark          evet

--- hareket azaltma tercihinde ozellik turlerine gore sure ---
ozellik                 tur               taban   azaltilmis  karar
translate/scale         yer degistirme     300ms         0ms   kaldirilir
rotate                  yer degistirme     400ms         0ms   kaldirilir
opacity                 gorunurluk         200ms       200ms      korunur
background-color        renk               150ms       150ms      korunur
surekli donen bant      surekli hareket   8000ms         0ms   kaldirilir

Birinci tablo, tercihlerin birbirini engellemediğini gösteriyor. Koyu şema ve hareket azaltma birlikte bildirildiğinde ikisi de uygulanır; her sorgu kendi özelliğini yazar, birbirinin yazdığını ezmez.

Son satır bir istisnayı işaretliyor. Zorunlu renkler etkinken sayfanın renk bildirimlerinin çoğu yok sayılır ve yerine sistemin belirlediği sınırlı bir palet konur. Bu, kullanıcı için kesin bir gereksinimdir; sayfanın yapması gereken, renk yerine başka bir ayrım aracı bırakmaktır. Yalnız renkle ayrılan bir durum — örneğin eksik ölçümü kırmızıyla göstermek — zorunlu renklerde tamamen kaybolur; aynı bilgi bir simge ya da metinle de verilmelidir.

Tarayıcının Kendi Parçaları

İkinci tablo, sayfanın renklerini değiştirmenin yetmediği bir alanı gösteriyor.

Kaydırma çubuğu, form denetimlerinin varsayılan görünümü, metin seçimi rengi — bunlar tarayıcının çizdiği parçalardır ve sayfanın renk bildirimlerinden etkilenmezler. Hangi şemayı kullanacaklarını color-scheme bildirimi söyler.

Bildirim yazılmadığında ya da yalnız light yazıldığında, kullanıcı koyu şema tercih etse bile denetimler açık kalır: koyu bir sayfada parlak beyaz bir kaydırma çubuğu ve açık zeminli girdiler görünür. color-scheme: light dark bildirimi iki şemayı da desteklediğini bildirir ve denetimler kullanıcının tercihini izler.

Bildirimin ikinci bir sonucu daha vardır: light-dark() fonksiyonu ancak bir şema seçili olduğunda çalışır. Fonksiyon iki değer alır ve etkin şemaya göre birini seçer; koyu şema kurallarını ayrı bir sorgu bloğuna yazmak yerine değer düzeyinde ayırmayı sağlar.

Hareket Azaltma Neyi Azaltır

Üçüncü tablo, bu dersin bıraktığı asıl soruya geliyor.

prefers-reduced-motion tercihinin adı yanıltıcı olabilir: tercih, animasyonun kapatılmasını değil hareketin azaltılmasını ister. Ayrım, hangi özelliğin değiştiğine bakılarak yapılır.

Bir ögenin yer değiştirmesi — kayması, dönmesi, ölçeklenmesi — göz için bir devinim üretir. Vestibüler duyarlılığı olan kullanıcılarda büyük ya da beklenmedik devinimler baş dönmesi ve mide bulantısına yol açabilir. Tercihi bildiren kullanıcı bu devinimden kaçınmak ister.

Bir ögenin görünürlüğünün ya da renginin değişmesi devinim üretmez. Bir düğmenin üzerine gelindiğinde arka planının 150 milisaniyede değişmesi, hareket azaltma tercihinde de korunabilir; hatta korunmalıdır, çünkü bu geçiş durum değişikliğinin okunmasını kolaylaştırır.

Tablodaki karar sütunu bu ayrımı uyguluyor: yer değiştirme üreten ve sürekli tekrarlanan hareketler kaldırılıyor, görünürlük ve renk geçişleri korunuyor. Bütün geçişleri tek bir kuralla sıfırlamak — evrensel seçiciye sıfır süre yazmak — kolay bir çözümdür ama arayüzü ani ve okunması zor hâle getirir.

WCAG 2.3.3 ölçütü bu politikayı doğrudan tarif eder: etkileşimle tetiklenen hareket animasyonları, işlev için gerekli değilse kapatılabilmelidir.

/* duzen.css — 12. adım: kullanıcı tercihleri */
:root {
  color-scheme: light dark;

  --zemin:       hsl(203 18% 97%);
  --metin:       hsl(211 29% 16%);
  --metin-soluk: hsl(211 12% 45%);
  --uyari:       hsl(0 66% 33%);
  --cizgi:       hsl(207 15% 86%);
  --gecis-suresi: 200ms;
}

@media (prefers-color-scheme: dark) {
  :root {
    --zemin:       hsl(211 22% 12%);
    --metin:       hsl(211 20% 92%);
    --metin-soluk: hsl(211 12% 53%);
    --uyari:       hsl(0 66% 61%);
    --cizgi:       hsl(207 15% 30%);
  }
}

@media (prefers-contrast: more) {
  :root {
    --metin-soluk: var(--metin);
    --cizgi: var(--metin);
  }
}

@media (prefers-reduced-motion: reduce) {
  :root { --gecis-suresi: 1ms; }
  html { scroll-behavior: auto; }
}

@media (forced-colors: active) {
  .olcum-cizelgesi .eksik { forced-color-adjust: none; }
  .olcum-cizelgesi .eksik::before { content: "! "; }
}

Beş blok, beş ayrı soru. Taban blok açık şemayı ve varsayılan süreyi tanımlıyor; kalan dördü yalnız değişen özel özellikleri yazıyor. Koyu şemadaki aydınlık değerleri yukarıdaki hesabın sonucu: soluk metin 53, uyarı 61. Zorunlu renkler bloğu ise renk yerine metin ayrımı ekliyor — eksik ölçüm, renk kaybolduğunda da bir işaretle görünür kalıyor.

Bütün bildirimler özel özellikler üzerinden geçtiği için bileşen kuralları hiç değişmiyor. Bir kart, bir çizelge satırı ya da bir düğme var(--metin) yazdığında dört tercihin bileşimi zaten çözülmüş olur.

Özet

  • Dört ortam özelliği kullanıcı tercihini bildirir: renk şeması, kontrast, hareket azaltma ve zorunlu renkler; hepsi ortam sorgularıyla aynı biçimde yazılır.
  • Taban biçem tercih bildirmemiş kullanıcıyı tarif eder; sorgu blokları yalnız değişeni yazar ve hiçbirini geri almaz.
  • Koyu şema, açık şemanın çevrilmesi değildir: zemin değiştiğinde her ön plan renginin oranı yeniden hesaplanır; istasyon paletinde uyarı renginin aydınlık değeri 33’ten 61’e çıkmak zorundadır.
  • Tercihler birbirinden bağımsızdır ve birlikte bildirildiklerinde hepsi uygulanır; zorunlu renkler etkinken yalnız renkle verilen ayrımlar kaybolur ve metin ya da simge yedeği gerekir.
  • color-scheme bildirimi tarayıcının kendi çizdiği parçaların hangi şemayı izleyeceğini söyler; yazılmadığında koyu bir sayfada açık denetimler kalır.
  • Hareket azaltma tercihi yer değiştirme üreten ve sürekli tekrarlanan hareketleri kaldırır; görünürlük ve renk geçişleri korunur, çünkü devinim üretmezler.

Sonraki Adım

Son tablo bir ayrım kurdu ama tanımı yapmadan kullandı: “yer değiştirme üreten” özellik hangisi? Bir ögeyi kaydıran, döndüren ve ölçekleyen bildirimlerin ortak bir adı ve ortak bir matematiği vardır; ikisini de görmeden hangi hareketin azaltılacağına karar vermek sezgiye kalır. Sonraki konu buradan başlar: bir öge kendi kutusunu bozmadan nasıl ötelenir, ölçeklenir ve döndürülür, bu işlemler hangi sırayla uygulanır ve sıra neden sonucu değiştirir?

İlerlemeni kaydetmek ve not almak için Giriş yap

Notlarım

Not almak için giriş yapmalısın.

Aramak için yazmaya başlayın.

↑↓ Esc gezin · aç · kapat