Blink Renderer'da renk görme bozukluklarını simüle etme

Mathias Bynens'tan daha fazla içerik
Mathias Bynens

Bu makalede, Geliştirici Araçları ve Blink Renderer'da renk körlüğü simülasyonunun neden ve nasıl uygulandığı açıklanmaktadır.

Arka plan: Kötü renk kontrastı

Düşük kontrastlı metin, web'de otomatik olarak algılanan en yaygın erişilebilirlik sorunudur.

Web'deki yaygın erişilebilirlik sorunlarının listesi. Düşük kontrastlı metin açık arayla en yaygın görülen sorundur.

WebAIM’in en popüler 1 milyon web sitesiyle ilgili erişilebilirlik analizine göre ana sayfaların 86%'sından fazlası düşük kontrasta sahip. Ortalama olarak her ana sayfada düşük kontrastlı metin 36 farklı örneği vardır.

Kontrast sorunlarını bulmak, anlamak ve düzeltmek için Geliştirici Araçları'nı kullanma

Chrome Geliştirici Araçları, geliştiricilerin ve tasarımcıların kontrastı iyileştirmesine ve web uygulamaları için daha erişilebilir renk şemaları seçmesine yardımcı olabilir:

Kısa bir süre önce bu listeye yeni bir araç ekledik ve bu araç diğerlerinden biraz farklı. Yukarıdaki araçlar temel olarak kontrast oranı bilgilerini göstermeye ve size bunu düzeltme seçenekleri sunmaya odaklanır. DevTools'un, geliştiricilerin bu sorunlu alanı daha iyi understanding için hâlâ bir yöntem olmadığını fark ettik. Bu sorunu gidermek için Geliştirici Araçları Oluşturma sekmesinde görme eksikliği simülasyonunu uyguladık.

Puppeteer'daki yeni page.emulateVisionDeficiency(type) API, bu simülasyonları programatik olarak etkinleştirmenize olanak tanır.

Renk körlüğü

20 kişiden yaklaşık 1'i renk körlüğünden (diğer bir deyişle doğruluk oranı daha düşük olan "renk körlüğü"nden) muzdariptir. Bu tür bozukluklar, farklı renkleri ayırt etmeyi zorlaştırarak kontrast sorunlarını artırabilir.

Renkleri görme bozukluğunun olmadığı, erimiş pastel boyaların simüle edildiği renkli bir resim
Renk simüle edilmiş renk görme bozukluğu içermeyen, erimiş boya kalemlerinin olduğu renkli bir resim.
ALT_TEXT_HERE
Erimiş pastel boyaların renkli bir resmi üzerinde renk körlüğü simülasyonunun etkisi.
Yeşil körlüğü taklit etmenin, erimiş pastel boyaların renkli resmi üzerindeki etkisi.
Yeşil renk körlüğü simülasyonunun, erimiş pastel boyaların renkli bir resmi üzerindeki etkisi.
Erimiş pastel boyaların renkli bir resmi üzerinde protanopi simülasyonunun etkisi.
Erimiş pastel boyaların renkli bir resmi üzerinde protanopi simülasyonunun etkisi.
Erimiş pastel boyaların renkli bir resmi üzerinde tritanopi simülasyonunun etkisi.
Erimiş pastel boyaların renkli bir resmi üzerinde tritanopi simülasyonunun etkisi.

Düzenli görüşü olan bir geliştirici olarak, size görsel olarak uygun olan renk çiftleri için Geliştirici Araçları'nın kötü bir kontrast oranı gösterdiğini görebilirsiniz. Bunun nedeni, kontrast oranı formüllerinin bu renk görme bozukluklarını hesaba katmasıdır! Bazı durumlarda düşük kontrastlı metinleri yine de okuyabilirsiniz, ancak görme bozukluğu olan kişiler bu ayrıcalığa sahip olmaz.

Tasarımcılara ve geliştiricilerin kendi web uygulamalarındaki bu görme bozukluklarının etkisini simüle etmelerine izin vererek eksik olanı sunmayı amaçlıyoruz: Geliştirici Araçları hem kontrast sorunlarını bulmanıza ve düzeltmenize yardımcı olmakla kalmıyor, artık bunları anlamanız da mümkün!

HTML, CSS, SVG ve C++ ile renk körlüğü kullanımını simüle etme

Özelliğimizin Blink Renderer uygulamasına geçmeden önce, web teknolojisini kullanarak eşdeğer işlevleri nasıl uygulayacağınızı anlamanız yardımcı olacaktır.

Bu renk körlüğü simülasyonlarının her birini, tüm sayfayı kaplayan bir yer paylaşımı olarak düşünebilirsiniz. Web Platformu'nun bunu yapmanın bir yolu var: CSS filtreleri! CSS filter özelliğiyle blur, contrast, grayscale, hue-rotate gibi önceden tanımlanmış bazı filtre işlevlerini ve daha fazlasını kullanabilirsiniz. Daha fazla kontrol için filter özelliği, özel SVG filtre tanımına işaret edebilecek URL'yi de kabul eder:

<style>
  :root {
    filter: url(#deuteranopia);
  }
</style>
<svg>
  <filter id="deuteranopia">
    <feColorMatrix values="0.367  0.861 -0.228  0.000  0.000
                           0.280  0.673  0.047  0.000  0.000
                          -0.012  0.043  0.969  0.000  0.000
                           0.000  0.000  0.000  1.000  0.000">
    </feColorMatrix>
  </filter>
</svg>

Yukarıdaki örnekte, renk matrisine dayalı özel bir filtre tanımı kullanılmaktadır. Kavramsal olarak her pikselin [Red, Green, Blue, Alpha] renk değeri, yeni bir renk [R′, G′, B′, A′] oluşturmak için matrisle çarpılır.

Matristeki her satırda 5 değer bulunur: R, G, B ve A için (soldan sağa) bir çarpan ve sabit kayma değeri için beşinci bir değer. 4 satır vardır: Matrisin ilk satırı yeni Kırmızı değerini, ikinci satır Yeşil, üçüncü sıra Mavi ve son sıra Alfa değerini hesaplamak için kullanılır.

Örneğimizdeki kesin sayıların nereden geldiğini merak ediyor olabilirsiniz. Bu renk matrisini renk körlüğü için iyi bir tahmin yapan nedir? Cevap: bilim! Değerler Machado, Oliveira ve Fernandes'in fizyolojik olarak doğru renk körlüğü simülasyon modeline dayanır.

Şimdi SVG filtresi var ve bunu CSS kullanarak sayfadaki rastgele öğelere uygulayabiliriz. Aynı düzeni diğer görme bozuklukları için de tekrar edebiliriz. Bu işlemin nasıl yapıldığını gösteren bir demoyu burada bulabilirsiniz:

İstesek, Geliştirici Araçları özelliğimizi şu şekilde oluşturabiliriz: Kullanıcı, DevTools kullanıcı arayüzünde bir görme bozukluğu emüle ettiğinde, denetlenen dokümana SVG filtresini ekler ve ardından kök öğeye filtre stilini uygularız. Ancak, bu yaklaşımda bazı sorunlar vardır:

  • Sayfanın kök öğesinde zaten bir filtre olabilir. Bu filtre, daha sonra kodumuz tarafından geçersiz kılınabilir.
  • Sayfada zaten filtre tanımımızla çelişen id="deuteranopia" içeren bir öğe olabilir.
  • Sayfa belirli bir DOM yapısına dayanıyor olabilir ve <svg> öğesini DOM'ye eklemekle bu varsayımları ihlal edebiliriz.

Uç durumlar bir yana, bu yaklaşımdaki esas sorun sayfada programlı olarak gözlemlenebilir değişiklikler yapıyor olmamızdır. DOM'yi inceleyen bir Geliştirici Araçları kullanıcısı, aniden hiç eklemediği bir <svg> öğesini veya hiç yazmadığı bir CSS filter öğesini görebilir. Bu kafa karıştırıcı olabilirdi. Bu işlevi Geliştirici Araçları'nda uygulamak için, bu dezavantajları içermeyen bir çözüme ihtiyacımız var.

Bunu daha az rahatsız edici hale getirmek için neler yapabileceğimize bakalım. Bu çözümün gizlememiz gereken iki bölümü vardır: 1) filter özelliğine sahip CSS stili ve 2) şu anda DOM'un bir parçası olan SVG filtre tanımı.

<!-- Part 1: the CSS style with the filter property -->
<style>
  :root {
    filter: url(#deuteranopia);
  }
</style>
<!-- Part 2: the SVG filter definition -->
<svg>
  <filter id="deuteranopia">
    <feColorMatrix values="0.367  0.861 -0.228  0.000  0.000
                           0.280  0.673  0.047  0.000  0.000
                          -0.012  0.043  0.969  0.000  0.000
                           0.000  0.000  0.000  1.000  0.000">
    </feColorMatrix>
  </filter>
</svg>

Doküman içi SVG bağımlılığını önleme

2. bölümle başlayalım: SVG'yi DOM'ye eklemekten nasıl kaçınabiliriz? İdeal bir yöntem, dosyayı ayrı bir SVG dosyasına taşımaktır. Yukarıdaki HTML'den <svg>…</svg> kopyalayıp filter.svg olarak kaydedebiliriz ancak önce bazı değişiklikler yapmamız gerekir. HTML'deki satır içi SVG, HTML ayrıştırma kurallarına uyar. Yani bazı durumlarda özellik değerlerinin önüne tırnak işareti eklemekten kurtulabilirsiniz. Bununla birlikte, ayrı dosyalardaki SVG'nin geçerli XML olması gerekir ve XML ayrıştırma işlemi HTML'den çok daha katıdır. SVG-in-HTML snippet'imizi yine burada görebilirsiniz:

<svg>
  <filter id="deuteranopia">
    <feColorMatrix values="0.367  0.861 -0.228  0.000  0.000
                           0.280  0.673  0.047  0.000  0.000
                          -0.012  0.043  0.969  0.000  0.000
                           0.000  0.000  0.000  1.000  0.000">
    </feColorMatrix>
  </filter>
</svg>

Bu geçerli bağımsız SVG'yi (ve dolayısıyla XML'i) yapmak için bazı değişiklikler yapmamız gerekir. Hangisinin olduğunu tahmin edebilir misin?

<svg xmlns="http://www.w3.org/2000/svg">
 
<filter id="deuteranopia">
   
<feColorMatrix values="0.367  0.861 -0.228  0.000  0.000
                           0.280  0.673  0.047  0.000  0.000
                          -0.012  0.043  0.969  0.000  0.000
                           0.000  0.000  0.000  1.000  0.000"
/>
 
</filter>
</svg>

İlk değişiklik, en üstteki XML ad alanı beyanıdır. İkinci ekleme, "solidus" adı verilen öğedir. <feColorMatrix> etiketinin öğeyi hem açtığını hem de kapattığını belirten eğik çizgi. Bu son değişiklik aslında gerekli değildir (bunun yerine açık olan </feColorMatrix> kapanış etiketine devam edebiliriz) ancak hem XML hem de SVG-in-HTML bu /> kısayolunu desteklediği için bu değişiklikten de yararlanabiliriz.

Neyse, bu değişikliklerle sonunda bunu geçerli bir SVG dosyası olarak kaydedip HTML dokümanımızdaki CSS filter özelliği değerinden dosyaya işaret edebiliriz:

<style>
  :root {
    filter: url(filters.svg#deuteranopia);
  }
</style>

Yaşasın, artık dokümana SVG eklememiz gerekmiyor. Bu zaten çok daha iyi. Ama... artık ayrı bir dosyaya bağımlıyız. Bu hâlâ bir bağımlılık. Bu sorundan bir şekilde kurtulabilir miyiz?

Sonuçta, bir dosyaya ihtiyacımız yok. Bir veri URL'si kullanarak URL içindeki tüm dosyayı kodlayabiliriz. Bunu gerçekleştirmek için, önceden sahip olduğumuz SVG dosyasının içeriğini alıp data: önekini ekliyor, uygun MIME türünü yapılandırıyoruz ve aynı SVG dosyasını temsil eden geçerli bir veri URL'miz var:

data:image/svg+xml,
  <svg xmlns="http://www.w3.org/2000/svg">
    <filter id="deuteranopia">
      <feColorMatrix values="0.367  0.861 -0.228  0.000  0.000
                             0.280  0.673  0.047  0.000  0.000
                            -0.012  0.043  0.969  0.000  0.000
                             0.000  0.000  0.000  1.000  0.000" />
    </filter>
  </svg>

Bunun faydası, artık dosyayı hiçbir yerde depolamamıza veya yalnızca HTML belgemizde kullanmak için diskten ya da ağ üzerinden yüklememizin gerekmemesidir. Dolayısıyla, daha önce yaptığımız gibi dosya adına başvurmak yerine, şimdi veri URL'sini işaret edebiliriz:

<style>
  :root {
    filter: url('data:image/svg+xml,\
      <svg xmlns="http://www.w3.org/2000/svg">\
        <filter id="deuteranopia">\
          <feColorMatrix values="0.367  0.861 -0.228  0.000  0.000\
                                 0.280  0.673  0.047  0.000  0.000\
                                -0.012  0.043  0.969  0.000  0.000\
                                 0.000  0.000  0.000  1.000  0.000" />\
        </filter>\
      </svg>#deuteranopia');
  }
</style>

Daha önce olduğu gibi, URL'nin sonunda yine kullanmak istediğimiz filtrenin kimliğini belirtiriz. URL'deki SVG dokümanında Base64 kodlamasının gerekli olmadığını unutmayın. Bu, okunabilirliğe zarar verir ve dosya boyutunu artırır. Veri URL'sindeki yeni satır karakterlerinin, CSS dizesi değişmez değerini sonlandırmasını önlemek için her satırın sonuna ters eğik çizgiler ekledik.

Şimdiye kadar sadece web teknolojisini kullanarak görme bozukluklarının nasıl simüle edileceğinden bahsettik. İlginçtir ki Blink Renderer'daki son uygulamamız aslında oldukça benzerdir. Burada, aynı tekniğe dayalı olarak, belirli bir filtre tanımına sahip veri URL'si oluşturmak için eklediğimiz C++ yardımcı yardımcı programını görebilirsiniz:

AtomicString CreateFilterDataUrl(const char* piece) {
  AtomicString url =
      "data:image/svg+xml,"
        "<svg xmlns=\"http://www.w3.org/2000/svg\">"
          "<filter id=\"f\">" +
            StringView(piece) +
          "</filter>"
        "</svg>"
      "#f";
  return url;
}

Bunu ihtiyaç duyduğumuz tüm filtreleri oluşturmak için nasıl kullandığımıza bakalım:

AtomicString CreateVisionDeficiencyFilterUrl(VisionDeficiency vision_deficiency) {
  switch (vision_deficiency) {
    case VisionDeficiency::kAchromatopsia:
      return CreateFilterDataUrl("…");
    case VisionDeficiency::kBlurredVision:
      return CreateFilterDataUrl("<feGaussianBlur stdDeviation=\"2\"/>");
    case VisionDeficiency::kDeuteranopia:
      return CreateFilterDataUrl(
          "<feColorMatrix values=\""
          " 0.367  0.861 -0.228  0.000  0.000 "
          " 0.280  0.673  0.047  0.000  0.000 "
          "-0.012  0.043  0.969  0.000  0.000 "
          " 0.000  0.000  0.000  1.000  0.000 "
          "\"/>");
    case VisionDeficiency::kProtanopia:
      return CreateFilterDataUrl("…");
    case VisionDeficiency::kTritanopia:
      return CreateFilterDataUrl("…");
    case VisionDeficiency::kNoVisionDeficiency:
      NOTREACHED();
      return "";
  }
}

Bu tekniğin, herhangi bir şeyi yeniden uygulamak veya tekerlekleri yeniden icat etmek zorunda kalmadan SVG filtrelerinin tüm gücüne erişmemizi sağladığını unutmayın. Bir Blink Oluşturucu özelliği uyguluyoruz, ancak bunu Web Platformu'ndan yararlanarak yapıyoruz.

SVG filtrelerini nasıl oluşturacağınızı ve bunları CSS filter özellik değerimizde kullanabileceğimiz veri URL'lerine nasıl dönüştüreceğimizi öğrendik. Bu teknikte bir sorun var mı? Hedef sayfada, veri URL'lerini engelleyen bir Content-Security-Policy olabileceği için aslında her durumda yüklenen veri URL'sine güvenmemiz mümkün değildir. Blink düzeyindeki son uygulamamız, yükleme sırasında bu "dahili" veri URL'leri için CSP'nin atlanmasına özel bir özen gösterir.

Uç durumlar bir yana, epey ilerleme kaydettik. Artık aynı dokümanda satır içi <svg> bulunmasını şart koşmadığımız için çözümümüzü yalnızca bağımsız bir CSS filter özelliği tanımına indirdik. İsteğinizi aldık. Şimdi bu sorunu da çözelim.

Doküman içi CSS bağımlılığını önleme

Özetlemek gerekirse, şu ana kadar bu noktaya geldik:

<style>
  :root {
    filter: url('data:…');
  }
</style>

Bu CSS filter özelliğini kullanmaya devam ediyoruz. Bu durum, gerçek dokümanda bir filter öğesini geçersiz kılabilir ve öğeleri bozabilir. Bu hata, Geliştirici Araçları'nda hesaplanan stilleri incelerken de ortaya çıkabilir ve bu da kafa karıştırıcı olabilir. Bu sorunlardan nasıl kaçınabiliriz? Geliştiriciler tarafından programatik olarak gözlemlenebilir durumda olmadan dokümana filtre eklemenin bir yolunu bulmamız gerekir.

filter gibi çalışan, ancak --internal-devtools-filter gibi farklı bir ada sahip olan yeni bir Chrome dahili CSS mülkü oluşturmak ortaya çıktı. Ardından, bu özelliğin Geliştirici Araçları'nda veya DOM'daki hesaplanan stillerde hiçbir zaman gösterilmemesi için özel bir mantık ekleyebiliriz. Hatta yalnızca ihtiyacımız olan tek bir öğede, yani kök öğede çalıştığından emin olabiliriz. Ancak bu çözüm ideal olmaz: filter ürününde zaten mevcut olan işlevleri yineliyor oluruz ve bu standart olmayan mülkü gizlemek için gayret göstersek bile, web geliştiricileri yine de bunu öğrenebilir ve kullanmaya başlayabilir. Bu, Web Platformu için kötü bir durumdur. DOM'de gözlemlenebilir olmadan CSS stilini uygulamak için başka bir yönteme ihtiyacımız var. Bir fikriniz var mı?

CSS spesifikasyonunda, kullandığı görsel biçimlendirme modelinin tanıtıldığı bir bölüm vardır ve temel kavramlardan biri görüntü alanı'dır. Bu, kullanıcıların web sayfasına göz atmak için kullandığı görsel görünümdür. Başlangıç içeren bloklar bu konuyla yakından alakalı bir kavramdır. Bu, yalnızca spesifikasyon düzeyinde var olan, stile uygun bir görüntü alanı <div> gibidir. Teknik özellikler, her yerde bu "görüntü alanı" kavramını ifade eder. Örneğin, içeriğin sığmadığında tarayıcının kaydırma çubuklarını nasıl gösterdiğini biliyor musunuz? Bunların tümü, bu "görüntü alanına" göre CSS spesifikasyonunda tanımlanır.

Bu viewport, Blink Oluşturucu'da ve bir uygulama ayrıntısı içinde bulunur. Varsayılan görüntü alanı stillerini spesifikasyonlara göre uygulayan kodu burada bulabilirsiniz:

scoped_refptr<ComputedStyle> StyleResolver::StyleForViewport() {
  scoped_refptr<ComputedStyle> viewport_style =
      InitialStyleForElement(GetDocument());
  viewport_style->SetZIndex(0);
  viewport_style->SetIsStackingContextWithoutContainment(true);
  viewport_style->SetDisplay(EDisplay::kBlock);
  viewport_style->SetPosition(EPosition::kAbsolute);
  viewport_style->SetOverflowX(EOverflow::kAuto);
  viewport_style->SetOverflowY(EOverflow::kAuto);
  // …
  return viewport_style;
}

Bu kodun görüntü alanının (veya daha doğru bir şekilde, baştaki kapsayıcı blokun) z-index, display, position ve overflow değerlerini işlediğini görmek için C++ veya Blink’in Style motorunun inceliklerini anlamanız gerekmez. CSS'de bu kavramlarla ilgili bilgi sahibi olabilirsiniz. Bağlamları yığma ile ilgili başka bir sihir de vardır. Doğrudan bir CSS mülküne dönüşmez ancak genel olarak bu viewport nesnesini, Blink'in içinden CSS kullanılarak şekillendirilebilen bir şey olarak düşünebilirsiniz. Tek fark, DOM'nin parçası olmayan bir DOM öğesi gibidir.

Bu sayede tam da istediğimiz şeyi elde ediyoruz. filter stillerimizi viewport nesnesine uygulayabiliriz. Bu durum, gözlemlenebilir sayfa stillerine veya DOM'a hiçbir şekilde müdahale etmeden oluşturmayı görsel olarak etkiler.

Sonuç

Buradaki küçük yolculuğumuzu özetlemek gerekirse, C++ yerine web teknolojisini kullanarak bir prototip oluşturarak işe başladık ve sonrasında bu prototipin bazı kısımlarını Blink Renderer'a taşımak için çalışmaya başladık.

  • İlk olarak veri URL'lerini satır içine alarak prototipimizi daha bağımsız hale getirdik.
  • Ardından bu dahili veri URL'lerini, yüklemelerini özel olarak büyük/küçük harfe getirerek CSP'ye uygun hale getirdik.
  • Stilleri Blink-internal viewport öğesine taşıyarak uygulamamızı DOM'den bağımsız ve programlı şekilde gözlemlenemez hale getirdik.

Bu uygulamayı benzersiz kılan, HTML/CSS/SVG prototipimizin nihai teknik tasarımı etkilemesidir. Blink Renderer'da bile Web Platformu'nu kullanmanın bir yolunu bulduk!

Daha fazla bilgi için tasarım teklifimize veya ilgili tüm yamalara referans veren Chromium izleme hatasına göz atın.

Önizleme kanallarını indirme

Varsayılan geliştirme tarayıcınız olarak Chrome Canary, Yeni geliştirilenler veya Beta sürümünü kullanabilirsiniz. Bu önizleme kanalları ile Geliştirici Araçları'nın en yeni özelliklerine erişebilir, son teknoloji ürünü web platformu API'lerini test edebilir ve sitenizdeki sorunları kullanıcılarınızdan önce tespit edebilirsiniz.

Chrome Geliştirici Araçları ekibiyle iletişime geçme

Yayındaki yeni özellikler ve değişiklikler ya da Geliştirici Araçları ile ilgili diğer konular hakkında konuşmak için aşağıdaki seçenekleri kullanın.

  • crbug.com adresinden bize öneri veya geri bildirim gönderin.
  • Geliştirici Araçları'nda Diğer seçenekler > Yardım > Geliştirici Araçları sorunu bildir'i kullanarak Geliştirici Araçları sorunlarını bildirin.Daha fazla
  • @ChromeDevTools adresine tweet gönderin.
  • Geliştirici Araçları YouTube videoları bölümündeki yenilikler veya Geliştirici Araçları İpuçları YouTube videoları bölümlerimize yorum yapın.