Pembangun menyukai kejayaan pantas. Apabila tiket menyatakan "tambah mod gelap," jalan yang paling kurang rintangan kelihatan jelas: tulis light.css, tulis dark.css, dan tukar antara keduanya. Ia terasa kemas. Ia siap dengan cepat. Untuk projek sampingan kecil dengan tiga komponen, ia mungkin masih boleh bertahan. Tetapi sebaik sahaja aplikasi anda berkembang melebihi segelintir modul, fail kedua itu bukan lagi satu aset, sebaliknya menjadi liabiliti yang perlu anda selenggara secara berulang.
Perangkap Dua Fail
Logiknya kelihatan munasabah pada pandangan pertama. Pengasingan tanggungjawab (separation of concerns), bukan? Perkara cerah di sini, perkara gelap di sana. Anda membuka dua buffer dalam editor anda. Anda menyalin gaya kad daripada fail cerah ke dalam fail gelap, menukar #ffffff kepada #1a1a1a, dan selesai.
Masalahnya bukan pada minggu pertama. Masalahnya adalah pada bulan keenam, apabila pereka meminta border radius yang sedikit berbeza pada butang utama, atau apabila pasukan produk mahukan keadaan amaran (warning state) baharu pada borang pembayaran. Anda mengemas kini helaian gaya cerah. Anda melihat sekilas helaian gaya gelap. Mungkin anda ingat untuk menyalin perubahan tersebut. Mungkin juga tidak. Jurang itulah tempat di mana kualiti mati. Anda bukan lagi menyelenggara satu antara muka. Anda menyelenggara dua antara muka selari yang kebetulan berkongsi rangka HTML yang sama.
Penyimpangan Tema (Theme Drift) Tidak Dapat Dielakkan
Jurang ini mempunyai nama yang mula dikenali oleh pasukan frontend: theme drift (penyimpangan tema). Ia berlaku apabila dua helaian gaya anda berkembang pada kelajuan yang berbeza. Pelarasan padding di sini. Pengubahsuaian bayang di sana. Fail gelap menjadi "adik-beradik" yang terabai. Atau lebih teruk, ia menjadi punca ketakutan. Pembangun mula mengelak daripada membuat perubahan kerana menyentuh satu tema bermakna perlu mencari dalam fail lain untuk menduplikasi kerja tersebut.
Beban kognitif meningkat dengan pantas. Anda mahu menulis CSS sekali sahaja. Sebaliknya, anda menulisnya dua kali, dan kini anda membayar "faedah" atas hutang tersebut setiap kali sistem reka bentuk berubah. Ikon tidak sejajar dalam mod gelap kerana seseorang telah mengemas kini flex gap dalam fail cerah dan terlupa untuk mencerminkannya. Focus rings hilang kerana peraturan kebolehcapaian baharu hanya dimasukkan ke dalam satu helaian sahaja. UI bukan sekadar kelihatan salah. Ia mula terasa rosak.
Gunakan Token Semantik
Penyelesaiannya bukanlah alat perbezaan (diff tool) yang lebih baik atau semakan kod yang lebih ketat. Penyelesaiannya adalah cara berfikir yang berbeza tentang warna. Berhenti menyusun gaya anda berdasarkan rupa fizikal dan mula menyusunnya berdasarkan tujuan. Di sinilah token semantik memainkan peranan.
Daripada menetapkan latar belakang putih pada kad, tetapkan ia sebagai latar belakang permukaan (surface background). Daripada memilih antara hitam dan putih pudar untuk teks, pilih warna teks. Komponen tersebut tidak tahu atau peduli sama ada pengguna lebih suka mod cerah atau gelap. Ia hanya meminta token yang sepadan dengan tugasnya.
Fikirkan tentang butang standard. Dalam dunia dua fail, .btn berada dalam helaian gaya cerah dengan latar belakang putih dan sempadan gelap. Kembarannya berada dalam helaian gaya gelap dengan latar belakang hampir hitam dan sempadan yang lebih cerah. Itu adalah dua kali ganda kod untuk satu butang. Dengan token, .btn mempunyai satu pengisytiharan: latar belakangnya ialah var(--color-surface-secondary) dan sempadannya ialah var(--color-border-default). Nilai-nilai itu sendiri berada di peringkat akar (root). Apabila laman web berada dalam mod cerah, --color-surface-secondary diselesaikan kepada sesuatu seperti #f8f9fa. Dalam mod gelap, token yang sama diselesaikan kepada #2d2d2d. Komponen butang tidak pernah berubah. Hanya data di bawahnya yang berubah.
Perbezaan antara struktur dan data ini halus tetapi berkuasa. Komponen kad anda mentakrifkan susun atur, jarak, tipografi, dan elevasi sekali sahaja. Lapisan tema anda mentakrifkan palet warna. Pengasingan itu adalah tujuan sebenar mengapa CSS custom properties dibina.
Bagaimana Seni Bina Berubah
Pendekatan ini secara asasnya menyusun semula cara anda menulis gaya.
Cara lama biasanya kelihatan seperti ini:
- Satu helaian gaya kad cerah yang mentakrifkan padding, radius, latar belakang, warna teks, dan bayang.
- Satu helaian gaya kad gelap yang mentakrifkan semula kebanyakan sifat yang sama hanya untuk menukar warna.
- Satu lapisan logik yang memutuskan helaian gaya mana yang perlu dimuatkan atau kelas mana yang perlu ditukar pada
body.
Cara baharu kelihatan seperti ini:
- Satu helaian gaya kad yang mentakrifkan susun atur dan menetapkan token semantik.
- Satu fail tema yang mentakrifkan maksud token tersebut dalam konteks cerah.
- Satu fail tema, atau sekadar satu blok dalam fail yang sama, yang mentakrifkan maksud token tersebut dalam konteks gelap.
- Satu pertukaran atribut tunggal yang mengubah lapisan nilai tanpa menyentuh lapisan komponen.
Anda mengekalkan tetapan yang stabil. Anda hanya menukar data. Apabila pereka ingin memperkenalkan tema ketiga, mungkin mod kontras tinggi atau varian biru tengah malam, anda tidak perlu menulis semula kad tersebut. Anda hanya menambah satu lagi tugasan pada peta token. Komponen tersebut kekal ringkas dan berfungsi dengan baik. Ia masih memerlukan warna permukaan. Tema akan memberitahunya warna permukaan mana yang perlu digunakan.
Pertukaran Atribut Data
Pelaksanaan boleh kekal ringkas dan mudah dibaca. Gunakan atribut data pada tag HTML anda, seperti data-theme="dark", dan biarkan definisi token anda berada di bawah skop tersebut.
Tetapkan nilai lalai anda pada :root untuk pengalaman mod cerah supaya halaman dipaparkan dengan betul sebelum JavaScript dijalankan. Kemudian, timpa nilai token di bawah [data-theme="dark"]. Skrip kecil akan memantau klik suis, mengemas kini atribut tersebut, dan setiap komponen pada halaman akan bertindak balas serta-merta. Tiada gangguan kelas (class thrashing) pada elemen individu. Tiada pengimportan helaian gaya (stylesheet) yang berasingan sepenuhnya semasa proses render. Pelayar sudah mempunyai pemboleh ubah tersebut dalam memori; ia hanya melukis semula dengan nilai baharu.
Ini memastikan kod anda bersih dari segi praktikal. Anda tidak perlu melakukan grep merentasi dua direktori untuk mencari setiap instans .card. Anda tidak perlu risau tentang "perang spesifisiti" (specificity wars) antara kelas tema yang bersaing pada nod yang sama. HTML anda kekal mudah dibaca. CSS anda kekal berpusat dan mudah dicari.
Ia Mengenai Nilai, Bukan Versi
Mod gelap adalah tentang nilai. Ia bukan versi kedua UI anda. Sudut kad anda tidak menjadi lebih bulat pada waktu malam. Grid anda tidak berubah menjadi bentuk yang berbeza. Skala tipografi anda tidak memerlukan ritma baharu. Hanya warna yang berubah, dan kadangkala bayang-bayang kelihatan sedikit lebih dalam. Menganggap mod gelap sebagai penukaran kulit (reskin) sepenuhnya adalah kejuruteraan berlebihan (over-engineering) yang akan mewujudkan mimpi ngeri penyelenggaraan.
Pasukan yang berjaya melakukan ini dengan betul melayan sistem reka bentuk mereka seperti pangkalan data. Komponen membuat pertanyaan (query) untuk sifat (properties) berdasarkan nama. Tema menyediakan rekod tersebut. Pertukaran daripada mod cerah ke gelap hanyalah perubahan parameter pertanyaan, bukannya penulisan semula skema.
Minda sebegitu yang menyelamatkan anda daripada penyimpangan tema (theme drift). Satu kad. Satu butang. Satu sumber kebenaran (source of truth) untuk jarak dan saiz. Palet warna berada di satu tempat, dipetakan secara logik, dan sedia untuk apa jua persekitaran yang lebih disukai pengguna.
Kesimpulan Utama
Jika anda menyelenggara dua fail CSS untuk mod cerah dan gelap, anda tidak sedang melakukan penentuan tema (theming). Anda sedang membuat klon. Beralihlah kepada token semantik, letakkan skopnya dengan atribut data pada tahap akar (root-level), dan biarkan komponen anda meminta peranan (roles) dan bukannya menetapkan rupa (appearances) secara keras (hardcoding). Proses penyusunan semula (refactor) awal memerlukan usaha, tetapi alternatifnya adalah permainan "whack-a-mole" yang tidak berkesudahan merentasi helaian gaya yang selari. Hidup ini terlalu singkat untuk menulis kad yang sama dua kali.
