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.
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:
- Web sayfasının üst kısmında görünen İnceleme Modu ipucunda, metin öğelerinin kontrast oranı gösterilir.
- Geliştirici Araçları renk seçici, metin öğeleri için kötü kontrast oranlarını belirtir, daha iyi renkleri manuel olarak seçmenize yardımcı olmak için önerilen kontrast çizgisini gösterir ve hatta erişilebilir renkler önerebilir.
- Hem CSS'ye Genel Bakış paneli hem de Lighthouse Erişilebilirlik denetim raporu, sayfanızda bulunan düşük kontrastlı metin öğelerini listeler.
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.
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.
- crbug.com adresinden bize geri bildirim ve özellik isteği gönderin.
- Geliştirici Araçları'nda Diğer seçenekler > Yardım > Geliştirici Araçları sorunu bildir'i kullanarak bir Geliştirici Araçları sorununu bildirin.
- @ChromeDevTools hesabına tweet gönderin.
- Geliştirici Araçları'ndaki yenilikler veya Geliştirici Araçları'yla ilgili ipuçları konulu YouTube videolarına yorum bırakın.