Artikel ini menjelaskan alasan dan cara kami menerapkan simulasi kekurangan penglihatan warna di DevTools dan Blink Renderer.
Latar belakang: kontras warna buruk
Teks kontras rendah adalah masalah aksesibilitas yang paling umum dan dapat terdeteksi secara otomatis di web.
Menurut analisis aksesibilitas WebAIM terhadap 1 juta situs teratas, lebih dari 86% halaman beranda memiliki kontras rendah. Rata-rata, setiap halaman beranda memiliki 36 instance berbeda teks kontras rendah.
Menggunakan DevTools untuk menemukan, memahami, dan memperbaiki masalah kontras
Chrome DevTools dapat membantu developer dan desainer meningkatkan kontras dan memilih skema warna yang lebih mudah diakses untuk aplikasi web:
- Tooltip Mode Inspeksi yang muncul di bagian atas halaman web menampilkan rasio kontras untuk elemen teks.
- Pemilih warna DevTools menunjukkan rasio kontras yang buruk untuk elemen teks, menampilkan garis kontras yang direkomendasikan untuk membantu memilih warna yang lebih baik secara manual, dan bahkan dapat menyarankan warna yang dapat diakses.
- Panel CSS Overview dan laporan audit Aksesibilitas Lighthouse mencantumkan elemen teks kontras rendah yang ditemukan di halaman Anda.
Baru-baru ini kami menambahkan alat baru ke daftar ini, dan alat ini sedikit berbeda dari yang lain. Alat di atas terutama berfokus pada menampilkan informasi rasio kontras dan memberi Anda opsi untuk memperbaikinya. Kami menyadari bahwa DevTools masih belum memiliki cara bagi developer untuk mendapatkan pemahaman yang lebih mendalam tentang ruang masalah ini. Untuk mengatasi hal ini, kami menerapkan simulasi kekurangan daya penglihatan di tab Rendering DevTools.
Di Puppeteer, page.emulateVisionDeficiency(type)
API baru memungkinkan Anda mengaktifkan simulasi ini secara terprogram.
Gangguan penglihatan warna
Sekitar 1 dari 20 orang mengalami kekurangan penglihatan warna (juga dikenal dengan istilah yang kurang akurat yaitu "buta warna"). Gangguan seperti itu mempersulit cara membedakan warna, yang dapat meningkatkan masalah kontras.
Sebagai developer dengan penglihatan normal, Anda mungkin melihat DevTools menampilkan rasio kontras yang buruk untuk pasangan warna yang terlihat bagus bagi Anda secara visual. Hal ini terjadi karena formula rasio kontras memperhitungkan kekurangan penglihatan warna ini. Anda mungkin masih dapat membaca teks yang memiliki kontras rendah. Namun, penderita gangguan penglihatan tidak memiliki hak istimewa tersebut.
Dengan memungkinkan desainer dan developer menyimulasikan efek kekurangan penglihatan ini di aplikasi web mereka sendiri, kami ingin memberikan bagian yang hilang: DevTools tidak hanya dapat membantu Anda menemukan dan memperbaiki masalah kontras, tetapi sekarang Anda juga dapat memahami masalah tersebut.
Simulasi kekurangan penglihatan warna dengan HTML, CSS, SVG, dan C++
Sebelum mempelajari penerapan Blink Renderer untuk fitur kita, sebaiknya pahami cara Anda menerapkan fungsi yang setara menggunakan teknologi web.
Anda dapat menganggap setiap simulasi kekurangan penglihatan warna ini sebagai overlay yang menutupi seluruh halaman. Platform Web memiliki cara untuk melakukannya: filter CSS. Dengan properti filter
CSS, Anda dapat menggunakan beberapa fungsi filter standar, seperti blur
, contrast
, grayscale
, hue-rotate
, dan banyak lagi. Untuk kontrol yang lebih besar, properti filter
juga menerima URL yang dapat mengarah ke definisi filter SVG kustom:
<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>
Contoh di atas menggunakan definisi filter kustom berdasarkan matriks warna. Secara konsep, nilai warna [Red, Green, Blue, Alpha]
setiap piksel dikalikan dengan matriks untuk membuat warna baru [R′, G′, B′, A′]
.
Setiap baris dalam matriks berisi 5 nilai: pengganda untuk (dari kiri ke kanan) R, G, B, dan A, serta nilai kelima untuk nilai pergeseran konstan. Ada 4 baris: baris pertama matriks digunakan untuk menghitung nilai Merah baru, baris kedua Hijau, baris ketiga Biru, dan baris terakhir Alfa.
Anda mungkin bertanya-tanya dari mana asal angka yang tepat dalam contoh kami. Apa yang membuat matriks warna ini menjadi perkiraan yang baik untuk deuteranopia? Jawabannya adalah: sains. Nilai ini didasarkan pada model simulasi kekurangan penglihatan warna yang akurat secara fisiologis oleh Machado, Oliveira, dan Fernandes.
Bagaimanapun, kita memiliki filter SVG ini, dan sekarang kita dapat menerapkannya ke elemen arbitrer di halaman menggunakan CSS. Kita dapat mengulangi pola yang sama untuk kekurangan penglihatan lainnya. Berikut adalah demo tampilannya:
Jika ingin, kita dapat mem-build fitur DevTools sebagai berikut: saat pengguna mengemulasi kekurangan penglihatan di UI DevTools, kita memasukkan filter SVG ke dalam dokumen yang diperiksa, lalu menerapkan gaya filter pada elemen root. Namun, ada beberapa masalah dengan pendekatan tersebut:
- Halaman mungkin sudah memiliki filter pada elemen root-nya, yang kemudian dapat diganti oleh kode kita.
- Halaman mungkin sudah memiliki elemen dengan
id="deuteranopia"
, yang bertentangan dengan definisi filter kami. - Halaman mungkin mengandalkan struktur DOM tertentu, dan dengan menyisipkan
<svg>
ke dalam DOM, kita mungkin melanggar asumsi ini.
Selain kasus ekstrem, masalah utama dengan pendekatan ini adalah kita akan membuat perubahan yang dapat diamati secara terprogram pada halaman. Jika pengguna DevTools memeriksa DOM, mereka mungkin tiba-tiba melihat elemen <svg>
yang tidak pernah ditambahkan, atau filter
CSS yang tidak pernah ditulis. Itu akan membingungkan. Untuk menerapkan fungsi ini di DevTools, kita memerlukan solusi yang tidak memiliki kelemahan ini.
Mari kita lihat cara membuatnya tidak terlalu mengganggu. Ada dua bagian dalam solusi ini yang perlu kita sembunyikan: 1) gaya CSS dengan properti filter
, dan 2) definisi filter SVG, yang saat ini merupakan bagian dari DOM.
<!-- 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>
Menghindari dependensi SVG dalam dokumen
Mari kita mulai dengan bagian 2: bagaimana kita dapat menghindari penambahan SVG ke DOM? Salah satu caranya adalah dengan memindahkannya ke file SVG terpisah. Kita dapat menyalin <svg>…</svg>
dari HTML di atas dan menyimpannya sebagai filter.svg
—tetapi kita perlu melakukan beberapa perubahan terlebih dahulu. SVG inline di HTML mengikuti aturan penguraian HTML. Artinya, Anda dapat melakukan hal-hal seperti menghapus tanda kutip di sekitar nilai atribut dalam beberapa kasus. Namun, SVG dalam file terpisah seharusnya berupa XML yang valid—dan penguraian XML jauh lebih ketat daripada HTML. Berikut cuplikan SVG-in-HTML kami lagi:
<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>
Untuk membuat SVG mandiri yang valid ini (dan juga XML), kita perlu melakukan beberapa perubahan. Dapatkah Anda menebak?
<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>
Perubahan pertama adalah deklarasi namespace XML di bagian atas. Penambahan kedua adalah yang disebut “solidus” — garis miring yang menunjukkan tag <feColorMatrix>
yang membuka dan menutup elemen. Perubahan terakhir ini sebenarnya tidak diperlukan (kita cukup menggunakan tag penutup </feColorMatrix>
eksplisit), tetapi karena XML dan SVG-in-HTML mendukung singkatan />
ini, sebaiknya kita menggunakannya.
Dengan perubahan tersebut, kita akhirnya dapat menyimpannya sebagai file SVG yang valid, dan mengarahkannya dari nilai properti filter
CSS dalam dokumen HTML:
<style>
:root {
filter: url(filters.svg#deuteranopia);
}
</style>
Hore, kita tidak perlu lagi memasukkan SVG ke dalam dokumen. Hasilnya sudah jauh lebih baik. Namun… sekarang kita bergantung pada file terpisah. Itu masih merupakan dependensi. Bisakah kita menghilangkannya?
Ternyata, kita tidak benar-benar memerlukan file. Kita dapat mengenkode seluruh file dalam URL menggunakan URL data. Untuk melakukannya, kita mengambil konten file SVG yang kita miliki sebelumnya, menambahkan awalan data:
, mengonfigurasi jenis MIME yang sesuai, dan kita memiliki URL data yang valid yang mewakili file SVG yang sama:
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>
Manfaatnya adalah sekarang, kita tidak perlu lagi menyimpan file di mana pun, atau memuat file dari disk atau melalui jaringan hanya untuk menggunakannya dalam dokumen HTML. Jadi, alih-alih merujuk ke nama file seperti yang kita lakukan sebelumnya, sekarang kita dapat mengarahkan ke URL data:
<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>
Di akhir URL, kita tetap menentukan ID filter yang ingin digunakan, seperti sebelumnya. Perhatikan bahwa Anda tidak perlu mengenkode dokumen SVG dalam URL dengan Base64—tindakan tersebut hanya akan mengurangi keterbacaan dan meningkatkan ukuran file. Kami menambahkan garis miring terbalik di akhir setiap baris untuk memastikan karakter baris baru di URL data tidak mengakhiri literal string CSS.
Sejauh ini, kita hanya membahas cara menyimulasikan kekurangan penglihatan menggunakan teknologi web. Menariknya, implementasi akhir kita di Blink Renderer sebenarnya cukup mirip. Berikut adalah utilitas helper C++ yang telah kami tambahkan untuk membuat URL data dengan definisi filter tertentu, berdasarkan teknik yang sama:
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;
}
Berikut cara menggunakannya untuk membuat semua filter yang diperlukan:
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 "";
}
}
Perhatikan bahwa teknik ini memberi kita akses ke kemampuan penuh filter SVG tanpa harus menerapkan ulang apa pun atau menemukan kembali roda apa pun. Kami menerapkan fitur Blink Renderer, tetapi kami melakukannya dengan memanfaatkan Platform Web.
Baik, jadi kita telah mengetahui cara membuat filter SVG dan mengubahnya menjadi URL data yang dapat kita gunakan dalam nilai properti filter
CSS. Dapatkah Anda memikirkan masalah dengan teknik ini? Ternyata, kita tidak dapat benar-benar mengandalkan URL data yang dimuat dalam semua kasus, karena halaman target mungkin memiliki Content-Security-Policy
yang memblokir URL data. Implementasi tingkat Blink akhir kami memerlukan perhatian khusus untuk mengabaikan CSP untuk URL data “internal” ini selama pemuatan.
Selain kasus ekstrem, kami telah mencapai beberapa kemajuan yang baik. Karena tidak lagi bergantung pada <svg>
inline yang ada dalam dokumen yang sama, kami telah secara efektif mengurangi solusi menjadi hanya satu definisi properti filter
CSS mandiri. Bagus! Sekarang, mari kita hapus juga.
Menghindari dependensi CSS dalam dokumen
Sebagai ringkasan, berikut adalah tahapan yang telah kita lalui sejauh ini:
<style>
:root {
filter: url('data:…');
}
</style>
Kita masih bergantung pada properti filter
CSS ini, yang mungkin mengganti filter
dalam dokumen sebenarnya dan merusaknya. Hal ini juga akan muncul saat memeriksa gaya terkomputasi di DevTools, yang akan membingungkan. Bagaimana cara menghindari masalah ini? Kita perlu menemukan cara untuk menambahkan filter ke dokumen tanpa dapat diamati secara terprogram oleh developer.
Salah satu ide yang muncul adalah membuat properti CSS internal Chrome baru yang berperilaku seperti filter
, tetapi memiliki nama yang berbeda, seperti --internal-devtools-filter
. Kemudian, kita dapat menambahkan logika khusus untuk memastikan properti ini tidak pernah muncul di DevTools atau dalam gaya yang dihitung di DOM. Kita bahkan dapat memastikannya hanya berfungsi pada satu elemen yang kita perlukan: elemen root. Namun, solusi ini tidak akan ideal: kita akan menduplikasi fungsi yang sudah ada dengan filter
, dan meskipun kita berusaha keras untuk menyembunyikan properti non-standar ini, developer web masih dapat mengetahuinya dan mulai menggunakannya, yang akan berdampak buruk bagi Platform Web. Kita memerlukan beberapa cara lain untuk menerapkan gaya CSS tanpa dapat diamati di DOM. Apakah Anda tahu siapa yang sebaiknya saya hubungi?
Spesifikasi CSS memiliki bagian yang memperkenalkan model pemformatan visual yang digunakannya, dan salah satu konsep utamanya adalah area pandang. Ini adalah tampilan visual yang digunakan pengguna untuk melihat halaman web. Konsep yang terkait erat adalah blok penampung awal, yang mirip dengan <div>
area pandang yang dapat ditata gayanya yang hanya ada di tingkat spesifikasi. Spesifikasi ini mengacu pada konsep “area pandang” ini di mana-mana. Misalnya, Anda tahu cara browser menampilkan scrollbar saat konten tidak muat? Semuanya ditentukan dalam spesifikasi CSS, berdasarkan “area pandang” ini.
viewport
ini juga ada dalam Blink Renderer, sebagai detail implementasi. Berikut kode yang menerapkan gaya area pandang default sesuai dengan spesifikasi:
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;
}
Anda tidak perlu memahami C++ atau kerumitan mesin Gaya Blink untuk melihat bahwa kode ini menangani z-index
, display
, position
, dan overflow
area pandang (atau lebih tepatnya: blok penampung awal). Semua konsep tersebut mungkin sudah Anda pahami dari CSS. Ada beberapa hal ajaib lainnya yang terkait dengan konteks penumpukan, yang tidak secara langsung diterjemahkan ke properti CSS, tetapi secara keseluruhan, Anda dapat menganggap objek viewport
ini sebagai sesuatu yang dapat diberi gaya menggunakan CSS dari dalam Blink, seperti elemen DOM—kecuali bukan bagian dari DOM.
Dengan begitu, kita akan mendapatkan apa yang kita inginkan. Kita dapat menerapkan gaya filter
ke objek viewport
, yang secara visual memengaruhi rendering, tanpa mengganggu gaya halaman yang dapat diamati atau DOM dengan cara apa pun.
Kesimpulan
Untuk merangkum perjalanan kecil kita di sini, kita mulai dengan membuat prototipe menggunakan teknologi web, bukan C++, lalu mulai memindahkan bagian-bagiannya ke Blink Renderer.
- Pertama-tama, kami membuat prototipe menjadi lebih mandiri dengan menyisipkan URL data.
- Kemudian, kami membuat URL data internal tersebut kompatibel dengan CSP, dengan kasus khusus pemuatan.
- Kami membuat penerapan kami tidak bergantung pada DOM dan tidak dapat diamati secara terprogram dengan memindahkan gaya ke
viewport
internal Blink.
Hal yang unik dari implementasi ini adalah prototipe HTML/CSS/SVG kita akhirnya memengaruhi desain teknis akhir. Kami menemukan cara untuk menggunakan Platform Web, bahkan dalam Perender Blink.
Untuk mengetahui latar belakang selengkapnya, lihat proposal desain kami atau bug pelacakan Chromium yang mereferensikan semua patch terkait.
Mendownload saluran pratinjau
Sebaiknya gunakan Chrome Canary, Dev, atau Beta sebagai browser pengembangan default Anda. Saluran pratinjau ini memberi Anda akses ke fitur DevTools terbaru, memungkinkan Anda menguji API platform web canggih, dan membantu Anda menemukan masalah di situs sebelum pengguna melakukannya.
Hubungi tim Chrome DevTools
Gunakan opsi berikut untuk membahas fitur baru, update, atau hal lain yang terkait dengan DevTools.
- Kirim masukan dan permintaan fitur kepada kami di crbug.com.
- Laporkan masalah DevTools menggunakan Opsi lainnya > Bantuan > Laporkan masalah DevTools di DevTools.
- Tweet ke @ChromeDevTools.
- Berikan komentar di video YouTube Yang baru di DevTools atau video YouTube Tips DevTools.