CSS Custom Properties zijn ver voorbij eenvoudige kleurwisseling gegaan. Deze gids verkent de geavanceerde patronen die CSS Custom Properties tot een van de krachtigste functies in modern CSS maken.
Basisprincipes van Custom Properties
Custom Properties worden gedefinieerd met het -- voorvoegsel en benaderd met de var() functie.
/* 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));
}Design token theming-systemen
Het krachtigste gebruik van Custom Properties is het bouwen van een compleet design token systeem.
/* 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);
}Componentvarianten met Custom Properties
Custom Properties maken flexibelere componentvariant-systemen mogelijk.
/* 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;
}JavaScript-interoperabiliteit
Custom Properties verbinden CSS en JavaScript naadloos.
// 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'));Veelgestelde vragen
Zijn CSS Custom Properties hetzelfde als SASS-variabelen?
Nee, ze zijn fundamenteel anders. SASS-variabelen worden gecompileerd tijdens de build.
Kunnen CSS Custom Properties geanimeerd worden?
Ja, met @property (Houdini).
Wat is de fallbackwaarde in var()?
Het tweede argument van var() is een fallback wanneer de eigenschap niet is gedefinieerd.
Werken ze in alle moderne browsers?
Ja, volledige browserondersteuning sinds 2017.