Bayangkan sebuah situs yang warna birunya muncul di empat puluh tempat berbeda — tombol, tautan, garis tepi, focus ring, sampai header. Suatu hari warna brand-nya diubah sedikit. Tanpa variabel, kamu harus menyisir seluruh stylesheet, mengganti kode hex yang sama berulang-ulang, sambil deg-degan takut ada satu yang kelewat. Dengan variabel CSS, kamu cukup mengubahnya di satu titik, dan seluruh situs ikut menyesuaikan.
Variabel CSS — nama resminya custom property — memungkinkan kamu memberi nama pada sebuah nilai, menyimpannya sekali, lalu memakainya di mana saja. Fitur ini sudah tertanam langsung di dalam bahasanya (tidak perlu build step, tidak perlu preprocessor), bekerja secara langsung di browser, dan membuka hal-hal yang dulu mustahil di CSS biasa — seperti pergantian tema dan nilai yang bisa menyesuaikan diri dengan halaman di sekitarnya. Yuk kita kenalan dulu.
Mendeklarasikan variabel
Custom property itu sebenarnya cuma property yang namanya diawali dua tanda hubung (--). Kamu mendeklarasikannya seperti deklarasi CSS biasa, di dalam sebuah rule:
:root {
--brand-color: #00b8e6;
--spacing: 16px;
--max-width: 1200px;
}
Selector :root itu menunjuk ke bagian paling atas dokumen (elemen <html>), jadi apa pun yang kamu deklarasikan di situ bisa diakses dari seluruh halaman. Nama variabelnya bebas kamu tentukan — --brand-color, --spacing, apa saja yang mudah dibaca. Awalan dua tanda hubung itulah yang memberi tahu CSS, “ini custom property, bukan salah ketik.”
Custom property itu case-sensitive
Berbeda dengan nama property CSS biasa, nama custom property bersifat case-sensitive. --brand-color dan --Brand-Color itu dua variabel yang berbeda. Pilih satu konvensi — huruf kecil dengan tanda hubung adalah yang paling umum — lalu pakai konsisten di mana-mana, supaya kamu tidak pernah tersandung satu huruf kapital yang nyasar.
Membaca variabel dengan var()
Sekadar mendeklarasikan variabel itu belum melakukan apa-apa. Untuk benar-benar memakai nilai yang tersimpan, kamu membacanya lewat fungsi var():
.button {
background-color: var(--brand-color);
padding: var(--spacing);
}
.container {
max-width: var(--max-width);
}
Di mana pun var(--brand-color) muncul, CSS menggantinya dengan #00b8e6. Sekarang warna itu hidup tepat di satu tempat saja. Ubah --brand-color di bagian atas, maka setiap rule yang membacanya langsung ikut berubah — tombolnya, dan apa pun yang menunjuk ke variabel yang sama. Satu sumber kebenaran seperti inilah alasan utama variabel benar-benar berguna.
Nilai fallback
Kadang kamu membaca variabel yang mungkin saja belum di-set. var() menerima argumen kedua — sebuah fallback yang dipakai ketika variabelnya tidak ada atau tidak valid:
.card {
/* kalau --card-bg belum didefinisikan, pakai white */
background-color: var(--card-bg, white);
padding: var(--card-padding, 20px);
}
Kalau --card-bg ada, nilainya yang menang. Kalau tidak ada, kamu mendapat white, bukan deklarasi yang rusak. Fallback membuat CSS-mu lebih tahan banting, terutama untuk komponen yang bisa dipakai ulang dan ditempatkan di halaman yang ternyata belum mendefinisikan variabel tertentu.
Scope: di mana sebuah variabel berlaku
Nah, di sinilah variabel mulai benar-benar bertenaga. Custom property itu belum tentu global — ia berlaku pada elemen tempat kamu mendeklarasikannya, lalu diwariskan ke seluruh keturunan elemen tersebut. Deklarasikan di :root dan praktisnya ia menjadi global. Deklarasikan di elemen yang lebih kecil dan ia hanya berlaku di dalam cabang elemen itu:
:root {
--text-color: #333; /* nilai bawaan global */
}
.alert {
--text-color: #b00020; /* ditimpa, tapi hanya di dalam .alert */
color: var(--text-color);
}
p {
color: var(--text-color); /* pakai #333, kecuali berada di dalam .alert */
}
Sebuah <p> di area terbuka membaca nilai global #333. Sebuah <p> di dalam .alert membaca #b00020, karena deklarasi yang lebih dekat menang untuk cabang tersebut. Scope ini mengikuti aturan pewarisan yang biasa — definisi terdekat ke atas pada pohon elemen itulah yang berlaku. Mekanisme ini membuat sebuah komponen bisa membawa kumpulan nilainya sendiri tanpa membocorkannya ke seluruh halaman.
Pikirkan sebagai pewarisan, bukan variabel di JavaScript
Variabel CSS bukan variabel pemrograman yang kamu set sekali secara global. Ia adalah nilai yang mengalir turun mengikuti pohon elemen, persis seperti color atau font-size. var(--x) yang sama bisa menghasilkan nilai berbeda pada elemen berbeda, tergantung apa yang dideklarasikan di atas masing-masing elemen. Begitu hal ini klik di kepalamu, scope tidak lagi terasa membingungkan.
Fitur andalannya: theming
Gabungkan scope tadi dengan satu trik tambahan, dan kamu mendapat pergantian tema nyaris cuma-cuma. Definisikan semua warnamu sebagai variabel di :root, lalu timpa nilainya ketika sebuah class atau atribut tertentu hadir:
:root {
--bg: #ffffff;
--text: #1a1a1a;
--accent: #00b8e6;
}
[data-theme="dark"] {
--bg: #0d1117;
--text: #e6edf3;
--accent: #4fd6f5;
}
body {
background-color: var(--bg);
color: var(--text);
}
Setiap rule membaca variabel, tidak pernah membaca warna mentahnya langsung. Untuk membalik seluruh situs ke mode gelap, kamu tidak perlu menyentuh satu pun rule komponen — cukup tambahkan data-theme="dark" pada elemen <html> atau <body> (biasanya dengan satu baris JavaScript), dan semua variabel langsung berganti ke nilai gelapnya sekaligus. Persis seperti inilah cara kerja tombol mode gelap di situs-situs modern, dan hal ini nyaris mustahil dilakukan dengan rapi tanpa custom property.
Variabel dan JavaScript
Karena custom property bersifat hidup di browser, JavaScript bisa membaca dan menulisnya saat program berjalan — sesuatu yang tidak akan pernah bisa dilakukan variabel milik preprocessor. Kamu menetapkan variabel dari script lewat setProperty:
// membaca sebuah variabel
const styles = getComputedStyle(document.documentElement);
const accent = styles.getPropertyValue('--accent');
// mengubah variabel — setiap rule yang memakainya langsung ikut berubah
document.documentElement.style.setProperty('--accent', '#ff6b6b');
Ubah satu variabel dari JavaScript, dan halaman langsung digambar ulang dengan nilai baru di setiap tempat ia dipakai — tanpa perlu menyentuh elemen satu per satu. Beginilah cara kamu membuat pemilih tema yang langsung berubah, warna aksen yang bisa disetel pengguna, atau slider yang mengubah jarak di seluruh layout, semuanya digerakkan oleh satu property. Kalau kamu butuh penyegaran soal cara mengambil elemen dari halaman lebih dulu, panduan JavaScript dan DOM membahas document.documentElement dan kawan-kawannya.
Contoh nyata
Sekarang kita rangkai semua kepingnya jadi sebuah komponen kecil yang sepenuhnya digerakkan variabel — mudah ditema, mudah disetel:
:root {
--card-radius: 12px;
--card-pad: 20px;
--card-bg: #ffffff;
--card-border: #e2e8f0;
--brand: #00b8e6;
}
.card {
background-color: var(--card-bg);
border: 1px solid var(--card-border);
border-radius: var(--card-radius);
padding: var(--card-pad);
}
.card__button {
background-color: var(--brand);
border-radius: calc(var(--card-radius) / 2);
padding: calc(var(--card-pad) / 2);
}
Perhatikan bagaimana tombolnya memakai ulang variabel milik card — bahkan menghitungnya dengan calc(), sehingga radius dan padding-nya tetap proporsional terhadap card. Setel --card-pad sekali, maka card dan tombolnya menyesuaikan ukuran bersama-sama. Hubungan antar-nilai semacam ini jelas tidak bisa kamu ungkapkan dengan angka yang ditulis mati. Kalau kamu ingin mendalami calc() dan kawan-kawannya, lihat panduan Fungsi calc(), clamp() dan min/max CSS.
Variabel tidak berfungsi di dalam kondisi media query
Kamu boleh memakai var() untuk nilai property, tapi tidak di dalam kondisi sebuah media query — @media (min-width: var(--bp)) tidak bekerja. Angka breakpoint di sebuah media query harus berupa nilai literal. Variabel bersinar untuk nilai yang kamu terapkan; pengujian query-nya sendiri tetap butuh angka sungguhan.
Beberapa kebiasaan baik
Custom property memang sederhana, tapi sedikit kedisiplinan akan membuatnya tetap rapi:
- Taruh variabel global di
:root. Simpan palet warna, skala spacing, dan ukuran-ukuran penting di satu tempat di bagian atas, supaya gampang dicari dan diubah. - Beri nama berdasarkan makna, bukan tampilan.
--brandlebih awet ketimbang--blue. Kalau warna brand berubah jadi hijau, variabel bernama--blueyang berisi hijau itu seperti ejekan kecil tiap hari. - Andalkan fallback untuk komponen yang dipakai ulang.
var(--x, nilai-default)membuat sebuah komponen tetap tampil meski ditempatkan di halaman yang lupa mendefinisikan--x. - Gunakan scope dengan sengaja. Timpa variabel pada elemen induk sebuah komponen untuk mengubah tema komponen itu saja, tanpa mengusik yang lain.
Kalau kamu sedang menyusun sebuah palet dan ingin awalan untuk deklarasinya, sebuah generator variabel CSS bisa menyiapkan blok :root berisi custom property untuk kamu tempel lalu sunting.
Penutup
Variabel CSS mengubah nilai yang berserakan dan berulang menjadi satu sumber kebenaran — lalu melangkah lebih jauh dari preprocessor mana pun karena ia hidup dan bisa berubah langsung di browser:
- Deklarasikan custom property dengan
--nama(case-sensitive), biasanya di:rootagar jangkauannya global. - Baca dengan
var(--nama), dan tambahkan fallback:var(--nama, default). - Variabel itu diwariskan dan ber-scope — timpa satu pada elemen yang lebih kecil untuk memengaruhi cabang itu saja.
- Timpa variabel pada sebuah class atau atribut
data-untuk mengganti seluruh tema (terang/gelap) dalam satu gerakan. - JavaScript bisa membaca dan menetapkannya secara langsung lewat
getPropertyValuedansetProperty, sehingga halaman langsung diperbarui.
Begitu kamu nyaman di sini, CSS-mu jadi jauh lebih mudah dirawat dan diganti temanya. Berikutnya kita akan melihat fungsi-fungsi yang membuat nilai jadi dinamis — calc(), clamp(), dan min()/max() — yang berpadu sangat apik dengan variabel yang baru saja kamu pelajari.