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

Bu makalede, DevTools ve Blink Oluşturucu'ya renk görme eksikliği simülasyonunu neden ve nasıl uyguladığımız 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'de sık karşılaşılan erişilebilirlik sorunlarının listesi. Düşük kontrastlı metin, en sık karşılaşılan sorundur.

WebAIM'in en popüler 1 milyon web sitesini incelediği erişilebilirlik analizine göre, ana sayfaların %86'sından fazlası düşük kontrasta sahip. Her ana sayfada ortalama 36 farklı düşük kontrastlı metin bulunur.

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 süre önce bu listeye diğerlerinden biraz farklı olan yeni bir araç ekledik. Yukarıdaki araçlar temel olarak kontrast oranı bilgilerini göstermeye ve bu oranı düzeltme seçenekleri sunmaya odaklanır. DevTools'ta geliştiricilerin bu sorun alanını daha derinlemesine anlayabileceği bir yöntemin eksik olduğunu fark ettik. Bu sorunu gidermek için Geliştirici Araçları Oluşturma sekmesinde görme bozukluğu simülasyonunu uyguladık.

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

Renk görme bozuklukları

Yaklaşık 20 kişiden 1'i renk görme bozukluğundan (daha az doğru bir terim olan "renk körlüğü" olarak da bilinir) muzdariptir. Bu tür bozukluklar, farklı renkleri ayırt etmeyi zorlaştırır ve kontrast sorunlarını artırabilir.

Renk görme bozukluğu taklit edilmemiş, erimiş boya kalemlerinin renkli resmi
Renk körlüğü eksiklikleri simüle edilmemiş, renkli bir eritilmiş boya resmi.
ALT_TEXT_HERE
Erimiş boya kalemlerinin renkli resminde renk körlüğü simülasyonunun etkisi.
Erimiş mum boyalardan oluşan renkli bir resimde deuteranopia'nın taklit edilmesinin etkisi.
Erimiş boya kalemlerinin renkli resminde deuteranopia'nın simüle edilmesinin etkisi.
Erimiş mum boyalardan oluşan renkli bir resimde protanopi simülasyonunun etkisi.
Erimiş mum boyalardan oluşan renkli bir resimde protanopi simülasyonunun etkisi.
Erimiş mum boyalardan oluşan renkli bir resimde tritanopiyi simüle etmenin etkisi.
Erimiş mum boyalardan oluşan renkli bir resimde tritanopi simülasyonunun etkisi.

Normal görme yetisine sahip bir geliştirici olarak, DevTools'un sizin için görsel olarak iyi görünen renk çiftleri için kötü bir kontrast oranı gösterdiğini görebilirsiniz. Bunun nedeni, kontrast oranı formüllerinin bu renk görme eksikliklerini hesaba katmasıdır. Siz bazı durumlarda düşük kontrastlı metinleri okuyabilseniz de görme engelli kullanıcılar bu ayrıcalığa sahip değildir.

Tasarımcıların ve geliştiricilerin bu görme eksikliklerinin kendi web uygulamalarındaki etkisini simüle etmelerine olanak tanıyarak eksik olan parçayı sunmayı amaçlıyoruz: DevTools artık kontrast sorunlarını bulup düzeltmenize yardımcı olmakla kalmıyor, aynı zamanda bunları anlamanıza da yardımcı oluyor.

HTML, CSS, SVG ve C++ ile renk görme bozukluklarını taklit etme

Özelliğimizin Blink Oluşturucu uygulamasına geçmeden önce, web teknolojisini kullanarak eşdeğer işlevi nasıl uygulayacağınızı anlamanız faydalı olacaktır.

Bu renk görme eksikliği simülasyonlarının her birini sayfanın tamamını kaplayan bir yer paylaşımı olarak düşünebilirsiniz. Web Platformu'nda bunu yapmanın bir yolu var: CSS filtreleri. CSS filter mülküyle blur, contrast, grayscale, hue-rotate gibi önceden tanımlanmış bazı filtre işlevlerini kullanabilirsiniz. Daha da fazla kontrol için filter mülkü, özel bir SVG filtre tanımına işaret edebilecek bir 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ı bir özel 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 matris çarpımıyla çarpılır.

Matristeki her satır 5 değer içerir: (soldan sağa) R, G, B ve A için bir çarpan ve sabit bir kaydırma 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ü satırı Mavi ve son satırı Alfa değerini hesaplamak için kullanılır.

Örneğimizdeki tam sayıların nereden geldiğini merak ediyor olabilirsiniz. Bu renk matrisinin dötronopi için iyi bir yaklaşım olmasının nedeni nedir? Yanıt: bilim. Değerler, Machado, Oliveira ve Fernandes tarafından geliştirilen fizyolojik olarak doğru bir renk görme eksikliği simülasyon modeline dayanır.

Bu SVG filtresine sahip olduğumuza göre, artık CSS kullanarak sayfadaki herhangi bir öğeye uygulayabiliriz. Diğer görme bozuklukları için de aynı kalıbı tekrarlayabiliriz. Bunun nasıl göründüğüne dair bir demoyu aşağıda bulabilirsiniz:

İstersek DevTools özelliğimizi şu şekilde oluşturabiliriz: Kullanıcı DevTools kullanıcı arayüzünde görme engelini taklit ettiğinde SVG filtresini incelenen belgeye enjekte ederiz ve ardından filtre stilini kök öğeye uygularız. Ancak bu yaklaşımın bazı sorunları vardır:

  • Sayfanın kök öğesinde zaten bir filtre olabilir. Bu filtre, kodumuz tarafından geçersiz kılınabilir.
  • Sayfada, filtre tanımımuzla çakışıp id="deuteranopia" içeren bir öğe olabilir.
  • Sayfa belirli bir DOM yapısına bağlı olabilir ve <svg> öğesini DOM'a ekleyerek bu varsayımları ihlal edebiliriz.

Bununla birlikte, bu yaklaşımın temel sorunu, sayfada programatik olarak gözlemlenebilir değişiklikler yaptığımızdır. DevTools kullanıcısı DOM'u incelediğinde hiç eklemediği bir <svg> öğesi veya hiç yazmadığı bir CSS filter öğesi görebilir. Bu kafa karıştırıcı olur. Bu işlevi Geliştirici Araçları'na uygulamak için bu dezavantajlara sahip olmayan bir çözüme ihtiyacımız var.

Bunu nasıl daha az rahatsız edici hale getirebileceğimize bakalım. Bu çözümün gizlememiz gereken iki bölümü vardır: 1) filter mülküne sahip CSS stili ve 2) şu anda DOM'un 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>

Belge içi SVG bağımlılığından kaçınma

2. bölümle başlayalım: SVG'yi DOM'a eklemekten nasıl kaçınabiliriz? Bunu ayrı bir SVG dosyasına taşımayı deneyebilirsiniz. Yukarıdaki HTML'den <svg>…</svg> öğesini 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 tırnak içine alınmaması gibi durumlarda sorun yaşamazsınız. Ancak ayrı dosyalardaki SVG'lerin geçerli XML olması gerekir ve XML ayrıştırma işlemi HTML'den çok daha katı kurallara tabidir. HTML'de SVG snippet'imizi tekrar burada bulabilirsiniz:

<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 dosyayı geçerli bir bağımsız SVG (ve dolayısıyla XML) yapmak için bazı değişiklikler yapmamız gerekiyor. Hangisi olduğunu tahmin edebilir misiniz?

<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ı. İkinci ekleme, "çizgi" olarak adlandırılan, <feColorMatrix> etiketinin öğeyi hem açtığını hem de kapattığını belirten eğik çizgidir. Bu son değişiklik aslında gerekli değildir (bunun yerine açık </feColorMatrix> kapatma etiketini kullanabiliriz), ancak hem XML hem de HTML'deki SVG bu /> kısaltmasını desteklediğinden bu kısaltmadan yararlanabiliriz.

Bu değişikliklerle birlikte, nihayet bunu geçerli bir SVG dosyası olarak kaydedebilir ve HTML belgemizdeki CSS filter mülk değerinden bu dosyaya işaret edebiliriz:

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

Artık dokümana SVG eklememiz gerekmiyor. Bu çok daha iyi. Ancak artık ayrı bir dosyaya ihtiyacımız var. Bu yine de bir bağımlılıktır. Bunu bir şekilde ortadan kaldırabilir miyiz?

Aslında bir dosyaya ihtiyacımız yok. Veri URL'si kullanarak dosyanın tamamını bir URL içinde kodlayabiliriz. Bunu yapmak için, daha önce sahip olduğumuz SVG dosyasının içeriğini alıp data: ön ekini ekler ve uygun MIME türünü yapılandırırız. Böylece, aynı SVG dosyasını temsil eden geçerli bir veri URL'si elde ederiz:

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>

Bu sayede, dosyayı HTML belgemizde kullanmak için artık herhangi bir yerde depolamamıza veya diskten ya da ağ üzerinden yüklememize gerek kalmadı. Bu nedenle, daha önce yaptığımız gibi dosya adını belirtmek yerine artık veri URL'sini gösterebiliriz:

<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>

URL'nin sonunda, kullanmak istediğimiz filtrenin kimliğini belirtmeye devam ederiz. URL'deki SVG belgesini Base64 kodlamanız gerekmez. Bunu yapmak yalnızca okunabilirliği olumsuz etkiler ve dosya boyutunu artırır. Veri URL'sindeki satır sonu karakterlerinin CSS dize değişmez değerini sonlandırmaması için her satırın sonuna ters eğik çizgi ekledik.

Şu ana kadar yalnızca web teknolojisini kullanarak görme eksikliklerini nasıl simüle edeceğimizden bahsettik. İlginç bir şekilde, Blink Oluşturucu'daki nihai uygulamamız aslında oldukça benzer. Aynı tekniğe dayalı olarak belirli bir filtre tanımı içeren bir veri URL'si oluşturmak için eklediğimiz C++ yardımcı yardımcı programı aşağıda verilmiştir:

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;
}

İhtiyacımız olan tüm filtreleri oluşturmak için bu aracı şu şekilde kullanıyoruz:

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 yeniden icat etmek zorunda kalmadan SVG filtrelerinin tüm gücüne erişmemizi sağladığını unutmayın. Blink Oluşturucu özelliğini Web Platformu'ndan yararlanarak uyguluyoruz.

SVG filtrelerinin nasıl oluşturulacağını ve CSS filter mülk değerimizde kullanabileceğimiz veri URL'lerine nasıl dönüştürüleceğini öğrendik. Bu teknikle ilgili bir sorun var mı? Hedef sayfada veri URL'lerini engelleyen bir Content-Security-Policy olabileceğinden, veri URL'sinin her durumda yüklenmesine güvenemeyiz. Nihai Blink düzeyindeki uygulamamızda, yükleme sırasında bu "dahili" veri URL'leri için CSP'yi atlamak üzere özel özen gösterilir.

Uç durumlar hariç, iyi bir ilerleme kaydettik. Artık satır içi <svg>'ün aynı dokümanda bulunmasına bağlı olmadığımız için çözümümüzü tek bir bağımsız CSS filter mülk tanımı olarak etkili bir şekilde azalttık. Mükemmel! Şimdi de bu sorunu ortadan kaldıralım.

Belge içi CSS bağımlılığından kaçınma

Şimdiye kadar neler yaptığımızı özetlemek isteriz:

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

Yine de bu CSS filter özelliğine bağlıyız. Bu özellik, gerçek belgedeki bir filter özelliğini geçersiz kılabilir ve işleri bozabilir. Bu durum, DevTools'ta hesaplanmış stilleri incelerken de ortaya çıkar ve kafa karıştırıcı olur. Bu sorunları nasıl önleyebiliriz? Geliştiriciler tarafından programlı olarak gözlemlenemeyecek şekilde dokümana filtre eklemenin bir yolunu bulmamız gerekiyor.

Ortaya çıkan fikirlerden biri, filter gibi davranan ancak --internal-devtools-filter gibi farklı bir ada sahip yeni bir Chrome dahili CSS özelliği oluşturmaktı. Ardından, bu özelliğin hiçbir zaman DevTools'ta veya DOM'daki hesaplanmış stillerde görünmesini engellemek için özel mantık ekleyebiliriz. Hatta yalnızca ihtiyacımız olan tek öğede (kök öğe) çalıştığından emin olabiliriz. Ancak bu çözüm ideal olmazdı: filter ile zaten mevcut olan işlevi kopyalamış olurduk ve bu standart dışı özelliği gizlemek için ne kadar uğraşırsak uğraşalım web geliştiricileri bu özelliği bulup kullanmaya başlayabilir. Bu da Web Platformu için kötü olur. CSS stilini DOM'da gözlemlenebilir olmadan uygulamanın başka bir yoluna ihtiyacımız var. Bu konuda bir öneriniz var mı?

CSS spesifikasyonunda, kullanılan görsel biçimlendirme modelini tanıtan bir bölüm bulunur. Bu bölümdeki önemli kavramlardan biri görünüm alanı'dır. Bu, kullanıcıların web sayfasına danıştığı görsel görünümdür. Bununla yakından ilişkili bir kavram da ilk kapsayıcı blok'tur. Bu, yalnızca spesifikasyon düzeyinde bulunan, stillenebilir bir görüntü alanı <div> gibidir. Spesifikasyonda her yerde bu "görünüm alanı" kavramına atıfta bulunulur. Örneğin, içerik sığmadığında tarayıcı 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, uygulama ayrıntısı olarak Blink Oluşturucu'da da bulunur. Varsayılan görüntü alanı stillerini spesifikasyona 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ğrusu, ilk kapsayıcı bloğun) z-index, display, position ve overflow değerlerini işlediğini görmek için C++'yu veya Blink'in stil motorunun inceliklerini anlamanıza gerek yoktur. Bunların tümü CSS'den aşina olabileceğiniz kavramlardır. Yığın bağlamlarıyla ilgili, doğrudan CSS özelliğine çevrilmeyen başka sihirli özellikler de vardır. Ancak genel olarak bu viewport nesnesini, DOM'un parçası olmaması dışında DOM öğesi gibi Blink'ten CSS kullanılarak stillendirilebilecek bir öğe olarak düşünebilirsiniz.

Bu sayede tam olarak istediğimiz sonuçları elde ediyoruz. filter stillerimizi, gözlemlenebilir sayfa stillerini veya DOM'u herhangi bir şekilde etkilemeden, oluşturmayı görsel olarak etkileyen viewport nesnesine uygulayabiliriz.

Sonuç

Bu küçük yolculuğumuzu özetlemek gerekirse, C++ yerine web teknolojisini kullanarak bir prototip oluşturarak başladık ve ardından bu prototipin bazı bölümlerini Blink Oluşturucu'ya taşımaya başladık.

  • Öncelikle, veri URL'lerini satır içine ekleyerek prototipimizi daha bağımsız hale getirdik.
  • Ardından, bu dahili veri URL'lerini yüklemelerini özel olarak ele alarak CSP uyumlu hale getirdik.
  • Stilleri Blink'in dahili viewport alanına taşıyarak uygulamamızı DOM'dan bağımsız ve programatik olarak gözlemlenemez hale getirdik.

Bu uygulamanın benzersiz yanı, HTML/CSS/SVG prototipimizin nihai teknik tasarımı etkilemiş olmasıdır. Blink Oluşturucu'da bile Web Platformu'nu kullanmanın bir yolunu bulduk.

Daha fazla bilgi için tasarım önerimizi veya ilgili tüm yamalar için 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ı, en son DevTools özelliklerine erişmenize, en yeni web platformu API'lerini test etmenize ve sitenizdeki sorunları kullanıcılarınızdan önce bulmanıza yardımcı olur.

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

Yeni özellikler, güncellemeler veya Geliştirici Araçları ile ilgili başka herhangi bir konu hakkında konuşmak için aşağıdaki seçenekleri kullanın.