Mengganti skema warna situs tanpa custom properties berarti mengubah nilai warna di belasan tempat sekaligus — dan satu tempat yang terlewat membuat tema tampak tidak konsisten. Custom properties, yang mekanisme dasarnya telah kita bahas pada Chapter Custom Properties
, menyelesaikan persoalan tersebut dengan cara menyimpan palet pada sekumpulan nama, lalu membiarkan setiap komponen membaca nilainya melalui var(). Chapter ini berfokus pada penerapan pola tersebut untuk theming: satu set variabel default, satu set penimpa per tema, dan satu atribut sebagai sakelar tema. Hasilnya, memindah seluruh halaman ke mode gelap hanya berarti mengubah satu atribut.
Palet pada :root, Penimpa per Tema #
Strategi dasarnya tiga langkah. Pertama, deklarasikan palet default pada selector :root sehingga variabel mewarisi ke seluruh dokumen. Kedua, tulis komponen yang membaca variabel tersebut — komponen tidak pernah menyebut warna secara langsung. Ketiga, tentukan blok tema yang menimpa variabel; tema menimpa nilai variabelnya, bukan properti tiap komponen:
:root {
--tema-latar: rgb(248, 250, 252);
--tema-teks: rgb(30, 41, 59);
--tema-aksen: rgb(2, 132, 199);
}
[data-tema="gelap"] {
--tema-latar: rgb(15, 23, 42);
--tema-teks: rgb(226, 232, 240);
--tema-aksen: rgb(56, 189, 248);
}Mari kita telaah kedua blok itu:
:root: selector yang menunjuk elemen<html>. Variabel yang didefinisikan di sini diwarisi oleh seluruh pohon dokumen, sehingga berfungsi sebagai tema default.[data-tema="gelap"]: attribute selector yang menimpa tiga variabel pada elemen pembawa atribut tersebut — beserta seluruh keturunannya, karena custom properties diwariskan.- Hanya variabel yang berubah: tidak ada satu pun properti komponen yang disentuh blok tema; perubahan merambat otomatis ke setiap pembaca
var().
Komponen Membaca, Atribut Memutuskan #
Dengan palet siap, komponen cukup memakai var(). Pada demo chapter ini, kartu membaca tiga variabel tema, dan dua kartu diberi atribut data-tema berbeda sehingga keduanya tampil dengan palet berbeda dari stylesheet yang sama:
.papan {
padding: 16px;
border-radius: 12px;
background-color: var(--tema-latar);
color: var(--tema-teks);
}
.papan .aksen {
color: var(--tema-aksen);
}Mekanisme pentingnya ada pada titik resolusi: nilai var(--tema-teks) dievaluasi pada elemen pemakai, bukan pada tempat variabel didefinisikan. Kartu di dalam wadah data-tema="gelap" membaca nilai milik wadah; kartu di luar wadah membaca nilai :root. Satu aturan .papan melayani semua tema — inilah bedanya dengan menulis dua set aturan komponen.
Perhatikan pula konvensi penamaannya: semua variabel tema memakai awalan --tema- sehingga blok penimpa terlihat utuh dan dapat dipindahkan dengan ringkas. Awalan semacam ini juga mencegah benturan dengan variabel milik komponen — --lebar-sidebar tidak boleh berubah karena tema berganti, sedangkan --tema-* memang dirancang untuk berubah.
<article class="papan" data-tema="terang">
<h2 class="aksen">Catatan Pagi</h2>
<p>Tema terang memakai palet default dari :root.</p>
</article>
<article class="papan" data-tema="gelap">
<h2 class="aksen">Catatan Malam</h2>
<p>Tema gelap menimpa tiga variabel pada wadahnya.</p>
</article>Hasil di Browser:
Papan pertama (data-tema="terang") berlatar putih kebiruan
(background-color: rgb(248, 250, 252)) dengan teks abu-abu gelap
(color: rgb(30, 41, 59)) dan judul biru (color: rgb(2, 132, 199)).
Papan kedua (data-tema="gelap") berlatar biru sangat gelap
(background-color: rgb(15, 23, 42)) dengan teks putih kebiruan
(color: rgb(226, 232, 240)) dan judul biru muda (color: rgb(56, 189, 248)).
Kedua papan memakai aturan .papan yang sama — hanya atribut yang berbeda.Melengkapi Sistem Tema #
Tiga pelengkap membuat pola ini siap dipakai pada proyek nyata. Pertama, warna sistem: media query @media (prefers-color-scheme: dark) dapat menjadi penimpa default ketika pengguna tidak memilih tema, dan fungsi light-dark() (Baseline 2024) menyediakan jalur ringkas untuk nilai ganda. Kedua, transisi antartema: secara default custom properties tidak dapat dianimasikan; deklarasikan nama variabelnya dengan @property
— Baseline 2024 — untuk memberi tipe dan membuat perubahan variabel dapat ditransisi. Ketiga, cakupan tema: karena variabel mewarisi, tema dapat dipasang pada bagian halaman tertentu saja, misalnya panel gelap di dalam halaman terang, tanpa JavaScript sama sekali.
Demo lengkap membawa data-expected pada kedua papan dan judul aksennya, memverifikasi bahwa warna terurai dari variabel tema yang berbeda pada setiap wadah.