
Nama saya Vladimir, saya terlibat dalam antarmuka seluler di Yandex.Mail. Dalam aplikasi kami, sudah ada topik gelap, tetapi tidak cukup: kami bisa mengecat ulang antarmuka dan huruf-huruf sederhana. Tetapi surat-surat yang diformat tetap terang dan kontras dengan antarmuka gelap, yang bisa membuat mata saya lelah di malam hari.
Hari ini saya akan memberi tahu para pembaca Habr bagaimana kami memecahkan masalah ini. Anda akan belajar tentang dua metode sederhana yang tidak sesuai dengan kita, kemudian - tentang cara utama kita mengecat ulang halaman dan, akhirnya, tentang arah untuk iterasi berikutnya: mengecat ulang gambar. Meskipun tugas itu sendiri - mengecat halaman dengan pemformatan sewenang-wenang - bersifat spesifik, saya pikir pengalaman kami juga akan berguna bagi Anda.
Cara sederhana
Sebelum kita sampai ke "repainter" ajaib kami, kami mencoba dua opsi sederhana seperti gabus: menggantung gaya gelap tambahan atau filter CSS pada elemen. Mereka tidak cocok untuk kita, tetapi mungkin untuk beberapa kasus mereka akan lebih baik (karena itu hanya = keren).
Timpa gaya
Cara paling sederhana, secara logis memperluas tema gelap aplikasi itu sendiri dalam CSS: kita akan menggantung gaya gelap pada wadah untuk surat (dalam kasus umum, untuk konten orang lain yang perlu dicat ulang):
.message--dark { background-color: black; color: white; }
Tetapi jika elemen-elemen di dalam surat memiliki gaya mereka sendiri, mereka akan mendefinisikan ulang gaya root kami. Tidak !important
tidak akan membantu. Anda dapat memeras ide dengan memenggal warisan:
.message--dark * { background-color: black !important; color: white !important; border-color: #333 !important; }
Dalam hal ini, Anda tidak dapat melakukannya tanpa !important
, karena pemilih itu sendiri tidak terlalu spesifik. Selain itu, akan diperlukan untuk mendefinisikan ulang gaya inline (dan gaya inline dengan !important
tetap akan merangkak, tidak ada yang harus dilakukan).
Gaya kami agak canggung dan warna semuanya sama, jadi masalah lain muncul: mungkin desainer ingin mengatakan sesuatu dengan mengatur warna (prioritas elemen dan hal-hal desainer lainnya), tetapi kami mengambil dan membuang seluruh gagasan ini.

Jika Anda kurang menghargai desainer daripada saya, dan masih memutuskan untuk menggunakan metode ini, jangan lupa untuk menyelesaikan hal-hal sepele yang tidak terlihat:
box-shadow
- hanya warna yang tidak dapat didefinisikan ulang, Anda harus menghapus semua bayangan atau hidup dengan yang terang.- Warna elemen semantik - tautan, elemen input.
- SVG sebaris - alih-alih
background
mereka perlu mengatur fill
, dan bukannya stroke
color
, tapi ini tidak akurat, tergantung pada SVG mana - itu bisa dan sebaliknya.
Secara teknis, metode ini tidak buruk: ini adalah tiga baris kode (oke, tiga puluh untuk versi produksi dengan kasus sudut), kompatibilitas dengan semua browser di dunia, pemrosesan halaman dinamis di luar kotak dan tidak ada ikatan dengan cara menghubungkan gaya dalam dokumen asli. Bonus khusus adalah Anda dapat dengan mudah mengubah warna dalam gaya sehingga cocok dengan aplikasi utama (katakanlah, buat latar belakang #bbbbb8
bukan hitam).
Ngomong-ngomong, kami biasa mengecat ulang surat dengan cara ini, tetapi jika kami menemukan gaya apa pun di dalam surat itu, kami takut dan membiarkan lampu itu menyala.
Filter CSS
Opsi yang sangat cerdas dan elegan. Anda dapat mengecat ulang halaman dengan filter CSS:
.message--dark { filter: invert(100) hue-rotate(180deg); }
Setelah ini, foto-foto akan menjadi menyeramkan, tetapi tidak masalah - kami akan mengecatnya kembali:
.message-dark img { filter: invert(100) hue-rotate(180deg); }

Masih ada masalah dengan gambar konten yang diikat melalui background
(kami tahu bahwa lebih mudah untuk menyesuaikan rasio aspek, tetapi bagaimana dengan semantik?). Misalkan kita dapat menemukan semua elemen seperti itu, tandai secara eksplisit dan cat ulang.
Metode ini bagus karena mempertahankan rasio kecerahan dan kontras yang asli. Di sisi lain, ada banyak masalah, dan mereka lebih penting daripada keuntungan:

- Halaman gelap menjadi terang.
- Warna yang dihasilkan tidak dapat dikontrol - filter mana yang diterapkan untuk menyempurnakan latar belakang ke
#bbbbb8
perusahaan Anda? Teka-teki itu. - Setelah dua kali pengecatan ulang, gambar memudar.
- Semuanya melambat (terutama pada ponsel) - ini logis, sekarang alih-alih rendering sederhana yang dibutuhkan browser untuk mendorong pemrosesan gambar di setiap layar.
Metode ini cocok untuk huruf-huruf yang terdiri dari teks dengan nada netral, tetapi siapa aesthetes yang mendapatkan kotak masuk lengkap dari konten aneh seperti itu? Tetapi filter dapat mengecat ulang elemen yang isinya tidak dapat diakses - bingkai, komponen web, gambar.
Tema Responsif
Saatnya sihir! Dari kekurangan dua pendekatan pertama, kami mengumpulkan daftar periksa:
- Jadikan latar belakang gelap, cahaya teks, medium batas.
- Identifikasi halaman yang sudah gelap dan jangan dicat ulang.
- Pertahankan rasio kecerahan dan kontras yang asli.
- Berikan kemampuan untuk menyesuaikan warna.
- Biarkan nada seperti di awal.
Kita perlu mengubah warna gaya agar latar belakangnya gelap. Dan mengapa tidak melakukannya secara harfiah? Kami hanya mengambil semua gaya, mencari aturan yang terkait dengan warna ( color
, background
, border
, box-shadow
, sub-properti mereka), dan menggantinya dengan "gelap" - menggelapkan latar belakang, mencerahkan teks, menggelapkan batas kurang dari latar belakang, dll.
Metode ini memiliki satu keuntungan luar biasa yang akan menghangatkan jiwa setiap pengembang. Setiap properti dapat dikonfigurasi (ya, jelaskan secara langsung dengan kode!) Aturannya sendiri untuk konversi warna. Dengan imajinasi yang memadai, Anda dapat berintegrasi dengan tema eksternal apa saja, melakukan koreksi warna apa pun (misalnya, membuat warna terang atau abu-abu-cokelat-bukan tema gelap) dan bahkan menambahkan sedikit konteks - katakan, pegang batas lebar dan sempit secara berbeda.
Kerugian adalah standar untuk semuanya. Ya, kami menjalankan skrip, memecah enkapsulasi gaya dan parse CSS regexp. Yah, tidak seperti HTML, yang terakhir tidak begitu memalukan karena tata bahasa CSS (dari tingkat yang kita butuhkan) masih teratur.
Rencana pengecatan ulang adalah sebagai berikut:
- Kami menormalkan properti warisan gaya (
bgcolor
dan teman-teman), menggesernya ke style="..."
. - Temukan semua gaya sebaris.
- Dalam setiap gaya kami menemukan semua aturan warna (
background-color
, color
, box-shadow
, dll.). - Dari semua aturan warna, kami mendapatkan warna, kami menemukan konverter yang diinginkan (lebih gelap untuk latar belakang, lebih jelas untuk teks).
- Kami memanggil konverter.
- Menempatkan aturan yang dikonversi kembali ke CSS.
Pengikatan (normalisasi, pencarian gaya, parsing) cukup sederhana. Kami akan mencari tahu bagaimana tepatnya konverter ajaib kami bekerja.
Konversi HSL
"Peredupan warna" bukanlah tindakan yang sesederhana mungkin, terutama jika kita ingin mempertahankan nada (cyan menjadi biru tua, bukan oranye). Ini bisa dilakukan dalam RGB normal, tetapi bermasalah. Penggemar desain algoritmik tahu bahwa gradien di sana pun bengkok. Tetapi bekerja dengan warna dalam HSL adalah kesenangan murni: alih-alih Merah, Hijau dan Biru, yang tidak jelas apa yang harus dilakukan, kami memiliki tiga saluran lain:
- Hue hanyalah nada yang ingin kita pertahankan.
- Saturaion - saturasi, yang tidak terlalu penting bagi kita sekarang.
- Lightness - kecerahan yang akan kita ubah.
Ruang seperti itu direpresentasikan dalam bentuk silinder. Dan tugas kita adalah membalik silinder ini. Fungsi pemeringkatan warna melakukan sesuatu seperti (h, s, l) => [h, s, 1 - l]
.
Warna-warna yang semuanya bagus
Terkadang situasinya berhasil: desain eksklusif surat itu (atau sebagian darinya) sudah gelap. Dalam hal ini, Anda tidak perlu mengubah apa pun, lebih baik untuk hanya diam-diam bahagia - mungkin perancang memilih warna yang tidak lebih buruk daripada algoritma kami. Di HSL, lihat saja L - brightness. Jika lebih tinggi (untuk teks) atau lebih rendah (untuk latar belakang) ambang (yang, tentu saja, dapat disesuaikan), kami tidak melakukan apa pun.
Sirkus dinamis
Meskipun kami tidak membutuhkannya (terima kasih lagi, sanitizer, Anda menyelamatkan saya dari kegilaan!), Saya akan tetap memberi tahu Anda apa jenis pengaya yang dibutuhkan tema adaptif untuk menggelapkan halaman penuh, dan bukan hanya huruf statis bodoh dari tahun sembilan puluhan. Lebih tepatnya, ini adalah tugas bagi mereka yang menyukai aroma pemilih di pagi hari.
Gaya inline dinamis
Kasus paling sederhana yang memecah halaman gelap kami adalah mengubah gaya sebaris. Operasi sering, tetapi perbaikannya sederhana: tambahkan MutationObserver
dan cepat perbaiki gaya sebaris saat mengubah.
Gaya eksternal
Bekerja dengan gaya dari <link>
dari bagian dalam halaman agak menyakitkan karena asynchrony dan @import
, dan CORS tidak lagi menyenangkan. Tampaknya masalah ini dapat diselesaikan dengan sangat elegan melalui pekerja web (proxy untuk *.css
).
Gaya dinamis
Akhirnya, menyatukan semua masalah kami, kami ingat bahwa skrip umumnya dapat menambah, menghapus dan mengatur ulang (spesifisitas! Cascade!) <style>
dan <link>
, dan bahkan mengubah aturan dalam <style>
. Semuanya dipecahkan oleh MutationObserver
sama untuk elemen gaya, tetapi untuk setiap perubahan ada lebih banyak pemrosesan.
Variabel CSS
Putaran kegilaan baru datang ketika variabel CSS memasuki permainan. Kami tidak dapat mengaburkan variabel itu sendiri: bahkan jika kami berasumsi bahwa kami akan menebak dengan format bahwa variabel berisi warna (meskipun saya tidak akan menyarankan Anda untuk melakukan ini), tidak diketahui dalam peran apa yang akan bertemu dengan kita - latar belakang, teks, perbatasan, sekaligus? Selain itu, nilai-nilai variabel diwariskan, jadi kita perlu mempertimbangkan tidak hanya gaya, tetapi juga elemen yang mereka terapkan, dan semua ini dengan cepat meningkat dan meledak.
Jika variabel CSS sampai ke arus utama, kami memiliki masalah. Di sisi lain, pada saat itu, color()
sudah akan dimulai, yang memungkinkan untuk tidak mengubah warna dalam JS, tetapi cukup mengganti warna dengan color(var(--bg) lightness(-50%))
.
Ringkasan

Untuk kasus kami, ketika sanitizer hanya menyisakan gaya inline, peredupan adaptif pada tingkat CSS berfungsi dengan baik: memberikan kualitas peredupan terbaik, tidak merusak huruf dan bekerja relatif cepat dan mudah. Tidak yakin apakah opsi dengan semua isian untuk dinamika tidak sia-sia. Untungnya, jika Anda bekerja dengan konten yang dibuat pengguna dan tidak menulis browser, pembersih Anda juga harus melakukan hal yang sama.
Dalam praktiknya, mode adaptif harus digunakan bersama-sama dengan redefinisi gaya: gaya biasanya tidak diterapkan secara eksplisit ke elemen standar seperti <input>
atau <a>
, tetapi secara default mereka ringan.
Cara menggelapkan gambar
Pengecatan ulang gambar adalah masalah terpisah yang mengganggu saya secara pribadi. Ini menarik, dan saya akhirnya memiliki kesempatan untuk menggunakan frase "analisis spektral". Ada beberapa masalah umum dengan gambar dalam subjek yang gelap.
Pertama, gambarnya terlalu terang. Ini bekerja dengan cara yang sama seperti huruf yang tidak dicat yang dengannya semuanya dimulai. Seringkali (tetapi tidak harus) ini adalah foto biasa. Karena tata letak buletin tidak terlalu menyenangkan, banyak orang hanya mengekspor bagian surat yang sulit sebagai gambar, itu tidak mengecat ulang dan pada malam hari menyinari kesempurnaan saya. Gambar-gambar seperti itu perlu digelapkan, tetapi tidak terbalik - jika tidak, akan muncul negatif yang mengerikan.

Kedua, gambar gelap dengan transparansi nyata. Masalah ini sering ditemukan pada logo - mereka dirancang untuk latar belakang yang terang dan, ketika kita menggantinya dengan yang gelap, bergabung dengannya. Gambar seperti itu perlu dibalik.

Di suatu tempat di tengah ada gambar yang putih mewakili "latar belakang transparan", tapi sekarang mereka hanya berdiri di beberapa persegi panjang putih yang aneh. Di dunia yang ideal, kami akan mengganti latar belakang putih dengan yang transparan, tetapi jika Anda pernah bekerja dengan tongkat ajaib di editor foto, maka Anda tahu bahwa melakukan ini secara otomatis tidak begitu mudah.

Sangat menarik bahwa kadang-kadang gambar tidak memiliki arti sama sekali - ini adalah pelacakan piksel dan "pemegang format" dalam tata letak yang sangat buruk. Ini dapat dengan aman dibuat tidak terlihat (katakanlah, opacity: 0
).

Heuristik introspeksi
Untuk memutuskan apa yang harus dilakukan dengan gambar, kita perlu masuk dan menganalisis isinya - dan dengan cara yang sederhana dan cepat. Menurut serangkaian masalah kami, versi pertama dari algoritma tersebut tampak. Itu dia.
Kami menganggap piksel gelap, terang, dan transparan dalam gambar, dan tidak semua, tetapi secara selektif - optimalisasi yang jelas. Kami menentukan kecerahan keseluruhan gambar (terang, gelap, sedang) dan adanya transparansi. Balikkan gambar gelap dengan transparansi, cahaya tanpa transparansi - bisu, jangan sentuh sisanya.
Kegembiraan heuristik yang luar biasa ini berakhir ketika saya menemukan buletin amal dengan foto pelajaran di sekolah Afrika. Semuanya akan baik-baik saja, tetapi perancang memusatkannya, menambahkan piksel transparan di sepanjang tepinya. Kami tidak ingin menemukan diri kami di tengah-tengah cerita baru tentang pengenalan gambar yang ofensif, dan kami memutuskan untuk tidak melakukan pemrosesan gambar sama sekali dalam versi pertama.
Di masa depan, heuristik tambahan, yang saya sebut "analisis spektral", harus melindungi terhadap masalah seperti itu - kami menghitung jumlah warna yang berbeda dalam gambar dan membalikkan hanya jika ada beberapa. Kriteria yang sama dapat digunakan untuk mencari gambar cahaya grafis dan mengecatnya kembali - kedengarannya menggoda.

Ringkasan
Untuk topik gelap penuh dalam surat, kami tidak memiliki surat mengecat dengan gaya, dan kami menemukan cara untuk mengaturnya. Dua opsi sederhana dalam CSS murni - mendefinisikan ulang gaya dan filter CSS - tidak berfungsi: yang pertama terlalu sulit pada desain aslinya, yang kedua tidak berfungsi dengan baik. Hasilnya, kami menggunakan peredupan adaptif - kami mengurai gaya, mengganti warna dengan yang lebih cocok dan mengumpulkannya kembali. Sekarang kami sedang mengembangkan tema menjadi gambar - untuk ini kita perlu menganalisis isinya dan mengecat ulang hanya sedikit.
Jika Anda perlu mengecat ulang HTML khusus ke tema gelap, ingat tiga metode berikut:
- Overriding styles - Anda tetap membutuhkannya untuk aplikasi utama Anda, dengan murah dan marah, tetapi itu akan membunuh semua warna asli.
- Filter CSS memang keren, tetapi berfungsi begitu-begitu. Gunakan hanya untuk elemen buram (dalam hal akses) seperti bingkai atau komponen web.
- Gaya konversi - menggelapkan kualitas sangat tinggi, tetapi lebih rumit daripada metode lain.
Bahkan jika Anda tidak pernah melakukan ini, saya harap Anda bersenang-senang!
Tautan yang bermanfaat :
Jika Anda tertarik untuk membahas topik ini dengan hidup dan dalam konteks pengembangan untuk Android, maka kami mengundang Anda untuk mengunjungi kantor Yandex di Petersburg pada 18 April.
Baru-baru ini, kami berbicara tentang memecahkan masalah lain dari pengguna surat - masalah surat.