CSS Custom Properties telah berkembang jauh melampaui penggantian warna sederhana. Panduan ini mengeksplorasi pola lanjutan yang menjadikan CSS Custom Properties salah satu fitur paling kuat dalam CSS modern.
Dasar-dasar Custom Properties
Custom Properties didefinisikan dengan prefiks -- dan diakses dengan fungsi var().
/* CSS Custom Properties — Fundamentals */
/* Define properties on :root for global scope */
:root {
--color-primary: #0066cc;
--color-primary-dark: #0052a3;
--spacing-unit: 8px;
--font-size-base: 1rem;
--border-radius: 4px;
}
/* Use with var() */
.button {
background-color: var(--color-primary);
padding: calc(var(--spacing-unit) * 1.5) calc(var(--spacing-unit) * 3);
border-radius: var(--border-radius);
font-size: var(--font-size-base);
}
/* Override at component scope (not global) */
.button.danger {
--color-primary: #cc0000; /* Only affects this button */
--color-primary-dark: #990000;
}
/* Fallback value (second argument to var) */
.card {
background: var(--card-bg, white);
color: var(--card-text, var(--color-text, #333)); /* nested fallback */
padding: var(--card-padding, var(--spacing-unit, 8px));
}Sistem Theming Design Token
Penggunaan paling kuat dari Custom Properties adalah membangun sistem design token lengkap yang mendukung beberapa tema tanpa JavaScript.
/* Multi-Theme System with CSS Custom Properties */
/* Light theme (default) */
:root {
--color-bg: #ffffff;
--color-surface: #f8f9fa;
--color-border: #e2e8f0;
--color-text-primary: #1a202c;
--color-text-secondary: #718096;
--color-accent: #3182ce;
--color-accent-hover: #2c5282;
--shadow-sm: 0 1px 3px rgba(0, 0, 0, 0.1);
--shadow-md: 0 4px 6px rgba(0, 0, 0, 0.1);
--transition-fast: 150ms ease;
}
/* Dark theme — override only what changes */
[data-theme="dark"] {
--color-bg: #1a202c;
--color-surface: #2d3748;
--color-border: #4a5568;
--color-text-primary: #f7fafc;
--color-text-secondary: #a0aec0;
--color-accent: #63b3ed;
--color-accent-hover: #90cdf4;
--shadow-sm: 0 1px 3px rgba(0, 0, 0, 0.4);
--shadow-md: 0 4px 6px rgba(0, 0, 0, 0.4);
}
/* High-contrast theme */
[data-theme="high-contrast"] {
--color-bg: #000000;
--color-text-primary: #ffffff;
--color-accent: #ffff00;
--color-border: #ffffff;
}
/* System preference auto-detection */
@media (prefers-color-scheme: dark) {
:root:not([data-theme="light"]) {
--color-bg: #1a202c;
--color-text-primary: #f7fafc;
/* ... more overrides */
}
}
/* Components use semantic tokens — never raw colors */
.card {
background: var(--color-surface);
border: 1px solid var(--color-border);
color: var(--color-text-primary);
box-shadow: var(--shadow-md);
transition: box-shadow var(--transition-fast);
}Varian Komponen dengan Custom Properties
Custom Properties memungkinkan sistem varian komponen yang lebih fleksibel dari pendekatan kelas utilitas tradisional.
/* Component Variant System */
/* Button component with custom property API */
.btn {
/* Default values (the component's "API") */
--btn-bg: var(--color-accent);
--btn-bg-hover: var(--color-accent-hover);
--btn-color: white;
--btn-border: transparent;
--btn-shadow: var(--shadow-sm);
--btn-size: 1rem;
--btn-padding-y: 0.5em;
--btn-padding-x: 1em;
--btn-radius: var(--border-radius);
background: var(--btn-bg);
color: var(--btn-color);
border: 1px solid var(--btn-border);
box-shadow: var(--btn-shadow);
font-size: var(--btn-size);
padding: var(--btn-padding-y) var(--btn-padding-x);
border-radius: var(--btn-radius);
transition: background var(--transition-fast);
}
.btn:hover {
background: var(--btn-bg-hover);
}
/* Variants — only override what changes */
.btn-danger {
--btn-bg: #e53e3e;
--btn-bg-hover: #c53030;
}
.btn-outline {
--btn-bg: transparent;
--btn-bg-hover: var(--color-accent);
--btn-color: var(--color-accent);
--btn-border: var(--color-accent);
}
.btn-sm {
--btn-size: 0.875rem;
--btn-padding-y: 0.375em;
--btn-padding-x: 0.75em;
}
.btn-lg {
--btn-size: 1.125rem;
--btn-padding-y: 0.75em;
--btn-padding-x: 1.5em;
}
/* Caller overrides — no need for a new variant class */
.special-hero .btn {
--btn-bg: gold;
--btn-color: black;
--btn-radius: 50px;
}Interoperabilitas JavaScript
Custom Properties menjembatani CSS dan JavaScript dengan mulus.
// JavaScript <-> CSS Custom Properties
// 1. Read a custom property value
const root = document.documentElement;
const primaryColor = getComputedStyle(root)
.getPropertyValue('--color-primary')
.trim();
console.log(primaryColor); // '#0066cc'
// 2. Set a custom property from JavaScript
root.style.setProperty('--color-primary', '#ff6600');
// 3. Remove a custom property
root.style.removeProperty('--color-primary');
// 4. Theme switcher
function setTheme(theme) {
document.documentElement.dataset.theme = theme;
localStorage.setItem('theme', theme);
}
// 5. Animate with custom properties (requires @property)
// CSS:
// @property --progress {
// syntax: '<number>';
// initial-value: 0;
// inherits: false;
// }
// .progress-bar {
// width: calc(var(--progress) * 1%);
// transition: --progress 1s ease;
// }
// JavaScript:
function animateProgress(el, from, to) {
el.style.setProperty('--progress', from);
requestAnimationFrame(() => {
el.style.setProperty('--progress', to);
});
}
// 6. Reactive properties with ResizeObserver
const observer = new ResizeObserver(entries => {
for (const entry of entries) {
const { width, height } = entry.contentRect;
entry.target.style.setProperty('--el-width', `${width}px`);
entry.target.style.setProperty('--el-height', `${height}px`);
}
});
observer.observe(document.querySelector('.responsive-component'));Pertanyaan yang Sering Diajukan
Apakah CSS Custom Properties sama dengan variabel SASS?
Tidak, keduanya berbeda secara fundamental. Variabel SASS dikompilasi saat build. CSS Custom Properties ada saat runtime.
Bisakah CSS Custom Properties dianimasikan?
Ya, dengan @property (Houdini).
Apa nilai fallback di var()?
Argumen kedua var() adalah fallback yang digunakan ketika properti tidak terdefinisi.
Apakah berfungsi di semua browser modern?
Ya, dukungan penuh sejak 2017.