Custom property adalah mekanisme variabel milik CSS, yaitu nilai yang diberi nama lalu digunakan kembali pada beberapa tempat di dalam stylesheet. Topik ini membahas deklarasi custom property, fungsi var() beserta nilai cadangannya, lingkup pewarisan yang membentuk varian komponen, pengubahan nilai melalui class dan atribut data, tema terang dan gelap melalui prefers-color-scheme, serta pola token warna yang memisahkan nilai primitif dari token semantik. Setelah topik ini dibaca, satu palet warna dapat diganti dari satu tempat tanpa menyunting aturan satu per satu.
Deklarasi custom property dan fungsi var()#
Custom property dideklarasikan sebagaimana properti biasa, tetapi namanya diawali dua tanda hubung dan nilainya dapat berupa warna, panjang, maupun teks apa pun yang sah. Nilai yang tersimpan dipanggil melalui fungsi var() di dalam deklarasi properti lain. Deklarasi kumpulan variabel biasanya ditulis pada selector :root, yaitu pseudo-class yang menunjuk elemen html, sehingga nilai variabelnya tersedia untuk seluruh dokumen. Satu perubahan nilai variabel akan merambat ke setiap properti yang memanggilnya.
:root {
--warna-aksen: #1d4ed8;
}
.tautan {
color: var(--warna-aksen);
}
.tombol {
border: 1px solid var(--warna-aksen, #334155);
}Fungsi var() menerima satu argumen wajib berupa nama variabel dan satu argumen opsional berupa nilai cadangan (fallback). Nilai cadangan digunakan apabila variabel yang disebut tidak terdefinisi, sehingga deklarasi border pada contoh di atas tetap mendapat warna apabila --warna-aksen belum dideklarasikan. Nama variabel membedakan huruf besar dan huruf kecil, sehingga --warna dan --Warna merupakan dua variabel yang berbeda. Konvensi penamaan yang lazim adalah huruf kecil dengan tanda hubung, misalnya --warna-aksen, agar ejaan nama tetap seragam di seluruh proyek.
Lingkup dan pewarisan#
Custom property mengikuti aturan inheritance sebagaimana properti color. Nilai yang dideklarasikan pada suatu elemen diwarisi oleh seluruh keturunannya, sedangkan deklarasi pada elemen keturunan menimpa nilai warisan untuk lingkup elemen tersebut beserta keturunannya. Deklarasi pada :root menghasilkan variabel global, sedangkan deklarasi pada selector komponen menghasilkan variabel yang berlaku lokal.
.kartu {
--kartu-tepi: #cbd5e1;
border: 1px solid var(--kartu-tepi);
}
.kartu--unggulan {
--kartu-tepi: #1d4ed8;
}Selector .kartu--unggulan pada contoh di atas tidak mendeklarasikan ulang properti border. Class tersebut hanya mengganti nilai --kartu-tepi, lalu deklarasi border yang memanggil variabel itu ikut berubah pada kartu yang membawa class tersebut. Pola ini memangkas duplikasi deklarasi karena satu perubahan nilai merambat ke setiap properti yang menggunakannya, sehingga varian komponen cukup dinyatakan sebagai penggantian nilai variabel.
Mengubah nilai melalui class dan atribut data#
Karena nilainya dapat diganti per selector, custom property menjadi titik kendali yang praktis untuk keadaan dan tema. Penggantian nilai dilakukan melalui class maupun atribut data, misalnya atribut data-tema pada elemen html. Selector atribut menangkap perubahan tersebut tanpa menyentuh satu aturan komponen pun. Pola ini memisahkan dua urusan yang berbeda, yaitu penandaan keadaan pada markah dan penjabaran keadaan menjadi gaya. Komponen yang menaruh data-status pada dirinya juga mendapat manfaat yang sama, karena varian kartu dan butir daftar dapat diatur dari satu selector tanpa class tambahan.
:root[data-tema="gelap"] {
--warna-latar: #0f172a;
--warna-teks: #e2e8f0;
}Dengan pola tersebut, kode yang mengubah atribut hanya menandai keadaan pada elemen akar, sedangkan seluruh konsekuensi tampilannya ditulis dalam CSS. Komponen apa pun yang menggunakan --warna-latar dan --warna-teks ikut berubah tanpa penyuntingan tambahan. Penamaan tema yang konsisten, misalnya terang dan gelap, membuat pemetaan antara markah dan gayanya mudah diikuti.
Tema terang dan gelap dengan prefers-color-scheme#
Fitur media prefers-color-scheme membawa preferensi tema yang dipilih pengguna pada sistem operasi atau browser ke dalam CSS. Fitur ini mengenal dua nilai utama, yaitu light untuk tema terang dan dark untuk tema gelap. Apabila token warna sudah dipisahkan dari penggunanya, penyesuaian tema cukup dilakukan dengan mendeklarasikan ulang nilai token di dalam media query.
:root {
--warna-latar: #f8fafc;
--warna-teks: #0f172a;
}
@media (prefers-color-scheme: dark) {
:root {
--warna-latar: #0f172a;
--warna-teks: #f8fafc;
}
}Deklarasi di dalam media query hanya berlaku ketika sistem pengguna memilih tema gelap, sedangkan nilai di luar menjadi nilai bawaan tema terang. Urutan penulisan menentukan hasilnya karena blok dark harus ditulis setelah nilai bawaan agar dapat menggantikannya pada kondisi yang cocok. Tidak ada JavaScript yang diperlukan untuk skema ini karena seluruh perpindahannya dikerjakan browser.
Demo berikut menerapkan pola tersebut pada sebuah panel pengaturan. Tekan tombolnya untuk menukar tema, lalu amati bahwa warna latar halaman, panel, teks, dan tombolnya berubah bersamaan. Skrip demo hanya menambahkan atau menghapus satu class bernama gelap pada elemen akar, sedangkan penukaran warnanya sepenuhnya dikerjakan oleh custom property.
Kode sumber demo
<!DOCTYPE html>
<html lang="id">
<head>
<meta charset="UTF-8">
<title>Panel pengaturan dengan tema terang dan gelap</title>
<link rel="stylesheet" href="index.css">
</head>
<body>
<section class="panel">
<h2>Pengaturan tampilan</h2>
<p>Warna panel ini seluruhnya mengikuti custom property yang dideklarasikan pada elemen akar.</p>
<button id="tombol-tema" class="tombol-tema" type="button">Ganti tema</button>
</section>
<script src="index.js"></script>
<style>
/* Demo custom property: panel dengan tema terang dan gelap */
:root {
--warna-latar: #f1f5f9;
--warna-panel: #ffffff;
--warna-teks: #1e293b;
--warna-teks-lembut: #475569;
--warna-aksen: #1d4ed8;
--warna-tepi: #cbd5e1;
}
/* satu class pada elemen akar menukar seluruh nilai token */
:root.gelap {
--warna-latar: #0f172a;
--warna-panel: #1e293b;
--warna-teks: #e2e8f0;
--warna-teks-lembut: #94a3b8;
--warna-aksen: #93c5fd;
--warna-tepi: #334155;
}
body {
margin: 0;
min-height: 100vh;
display: flex;
align-items: center;
justify-content: center;
background: var(--warna-latar);
color: var(--warna-teks);
font-family: sans-serif;
transition: background 0.2s, color 0.2s;
}
.panel {
width: 280px;
padding: 20px;
background: var(--warna-panel);
border: 1px solid var(--warna-tepi);
border-radius: 8px;
}
.panel h2 {
margin: 0 0 8px;
font-size: 16px;
}
.panel p {
margin: 0 0 16px;
font-size: 13px;
color: var(--warna-teks-lembut);
}
.tombol-tema {
padding: 8px 14px;
font-size: 13px;
border: 1px solid var(--warna-aksen);
border-radius: 6px;
background: var(--warna-aksen);
color: var(--warna-panel);
cursor: pointer;
}
</style>
<style>
/* Demo custom property: panel dengan tema terang dan gelap */
:root {
--warna-latar: #f1f5f9;
--warna-panel: #ffffff;
--warna-teks: #1e293b;
--warna-teks-lembut: #475569;
--warna-aksen: #1d4ed8;
--warna-tepi: #cbd5e1;
}
/* satu class pada elemen akar menukar seluruh nilai token */
:root.gelap {
--warna-latar: #0f172a;
--warna-panel: #1e293b;
--warna-teks: #e2e8f0;
--warna-teks-lembut: #94a3b8;
--warna-aksen: #93c5fd;
--warna-tepi: #334155;
}
body {
margin: 0;
min-height: 100vh;
display: flex;
align-items: center;
justify-content: center;
background: var(--warna-latar);
color: var(--warna-teks);
font-family: sans-serif;
transition: background 0.2s, color 0.2s;
}
.panel {
width: 280px;
padding: 20px;
background: var(--warna-panel);
border: 1px solid var(--warna-tepi);
border-radius: 8px;
}
.panel h2 {
margin: 0 0 8px;
font-size: 16px;
}
.panel p {
margin: 0 0 16px;
font-size: 13px;
color: var(--warna-teks-lembut);
}
.tombol-tema {
padding: 8px 14px;
font-size: 13px;
border: 1px solid var(--warna-aksen);
border-radius: 6px;
background: var(--warna-aksen);
color: var(--warna-panel);
cursor: pointer;
}
</style>
<script>
// Demo custom property: JavaScript hanya menukar satu class pada elemen akar.
const tombol = document.getElementById('tombol-tema');
tombol.addEventListener('click', () => {
document.documentElement.classList.toggle('gelap');
});
</script>
<script>
// Demo custom property: JavaScript hanya menukar satu class pada elemen akar.
const tombol = document.getElementById('tombol-tema');
tombol.addEventListener('click', () => {
document.documentElement.classList.toggle('gelap');
});
</script></body>
</html>Class gelap pada demo dijabarkan sebagai penggantian token pada elemen akar, sebagaimana terlihat pada fragmen berikut. Nilai di dalamnya menggantikan nilai bawaan tema terang pada :root karena selector :root.gelap menunjuk elemen html yang membawa class tersebut. Perpindahan tema terjadi serentak karena seluruh komponen membaca token yang sama. Pola class semacam ini cocok untuk tombol pindah tema yang keputusannya disimpan, sedangkan media query lebih tepat untuk menghormati preferensi sistem tanpa kode tambahan.
:root.gelap {
--warna-latar: #0f172a;
--warna-panel: #1e293b;
--warna-teks: #e2e8f0;
--warna-teks-lembut: #94a3b8;
--warna-aksen: #93c5fd;
--warna-tepi: #334155;
}Pola token warna: primitif dan semantik#
Pola token warna membagi variabel menjadi dua tingkat dengan tugas yang berbeda. Token primitif menyimpan nilai mentah palet dan namanya menggambarkan warna itu sendiri, misalnya --biru-600. Token semantik menyatakan peran warna pada antarmuka, misalnya --warna-latar dan --warna-teks, dan nilainya merujuk token primitif. Nama token primitif sebaiknya netral terhadap peran, misalnya memuat nama warna beserta tingkatannya, agar nilainya bebas dipetakan ke peran apa pun.
:root {
--biru-600: #1d4ed8;
--abu-800: #1e293b;
--slate-100: #f1f5f9;
--warna-latar: var(--slate-100);
--warna-panel: #ffffff;
--warna-aksen: var(--biru-600);
--warna-teks-kuat: var(--abu-800);
}Komponen dianjurkan hanya menggunakan token semantik agar keputusan peran warna terkumpul di satu tempat. Apabila palet diganti, penyuntingan cukup dilakukan pada token primitif atau pada pemetaan token semantiknya, sedangkan selector komponen tidak tersentuh. Tingkatan ini juga menjadikan tema terang dan gelap ringkas karena yang ditukar hanyalah nilai token semantik, bukan warna pada setiap komponen.
Kesalahan umum#
Kesalahan pertama adalah salah mengetik nama variabel. Deklarasi semacam itu tidak memunculkan pesan error apa pun, karena bila fungsi var() menyertakan nilai cadangan maka nilai cadangan digunakan secara diam-diam, sedangkan tanpa nilai cadangan properti kembali ke nilai warisan atau nilai awalnya. Akibatnya gaya terlihat hilang tanpa jejak. Perbaikannya, cocokkan ejaan nama variabel antara deklarasinya dan setiap pemanggilannya.
Kesalahan kedua adalah komponen yang memanggil token primitif secara langsung. Kebiasaan ini menyebarkan keputusan warna ke banyak selector, sehingga penggantian tema harus menyentuh berkas CSS di banyak tempat. Gunakan token primitif hanya pada deklarasi token semantik, dan biarkan komponen membaca token semantiknya.
Kesalahan ketiga adalah mendeklarasikan token di dalam selector yang terlalu sempit lalu mengharapkannya tersedia global. Variabel hanya berlaku pada elemen yang cocok dengan selector deklarasinya beserta keturunannya. Deklarasi token global ditempatkan pada :root, sedangkan token varian ditempatkan pada lingkup komponen yang membutuhkannya.
Ringkasan#
- Custom property dideklarasikan dengan nama berawalan dua tanda hubung dan dipanggil melalui fungsi var(), lengkap dengan nilai cadangan opsional.
- Nilai custom property diwariskan ke keturunan, sehingga deklarasi pada
:rootbersifat global dan deklarasi pada komponen bersifat lokal. - Tema terang dan gelap diperoleh dengan mengganti nilai token, baik melalui class, atribut data, maupun
@media (prefers-color-scheme: dark). - Pola token dua tingkat memisahkan warna mentah yang primitif dari peran warna yang semantik, sehingga penggantian tema tidak menyentuh komponen.
Referensi#
- Using CSS custom properties (variables) — MDN — Panduan resmi penggunaan custom property, termasuk nilai cadangan dan lingkupnya.
- var() — MDN — Dokumentasi fungsi var() beserta aturan argumen dan perilaku nilainya.
- prefers-color-scheme — MDN — Dokumentasi fitur media untuk tema terang dan gelap sesuai preferensi sistem.