Advanced CSS Custom Properties Patterns
Beyond simple variable declarations, CSS custom properties enable powerful runtime theming, component APIs, and JavaScript integration patterns.
1. Component API Pattern
Design your components with a CSS "API" — intentionally exposed variables that consumers can override.
/* The component defines its defaults */
.button {
--btn-bg: var(--color-primary, #6c63ff);
--btn-color: white;
--btn-radius: var(--radius-md, 8px);
--btn-padding: 0.75rem 1.5rem;
background: var(--btn-bg);
color: var(--btn-color);
border-radius: var(--radius);
padding: var(--btn-padding);
border: none;
cursor: pointer;
transition: filter 0.2s;
}
.button:hover { filter: brightness(1.12); }
/* Consumers customize without touching the component rules */
.button--danger { --btn-bg: #e53e3e; }
.button--ghost { --btn-bg: transparent; --btn-color: var(--color-primary); }
.button--large { --btn-padding: 1rem 2.5rem; }
2. Theming with Data Attributes
Switch entire themes by changing a single attribute on :root or a section.
:root, [data-theme="light"] {
--bg: #ffffff;
--text: #111111;
--card: #f7f7f7;
--border: #e0e0e0;
}
[data-theme="dark"] {
--bg: #0f0f0f;
--text: #f0f0f0;
--card: #1c1c1e;
--border: #333333;
}
/* Works for scoped sections too! */
.hero[data-theme="dark"] {
--bg: #0a0a0a;
--text: #ffffff;
}
3. JavaScript + CSS Variables Bridge
CSS custom properties are readable and writable from JavaScript — creating a clean JS/CSS bridge.
// Read a variable
const primary = getComputedStyle(document.documentElement)
.getPropertyValue('--color-primary').trim();
// Write / update a variable at runtime
document.documentElement.style.setProperty('--color-primary', '#00ff87');
// Update per-element
const card = document.querySelector('.card');
card.style.setProperty('--card-bg', 'rgba(0, 255, 135, 0.1)');
Use cases:
- Theme toggle (dark / light mode at button click)
- User preference persistence (save to
localStorage, restore on load) - Parallax effects (update
--scroll-yon scroll for CSS-driven parallax) - Mouse tracking (update
--mouse-x/--mouse-yfor glow effects)
4. Parallax with CSS Variables
window.addEventListener('scroll', () => {
document.documentElement.style.setProperty('--scroll-y', `${window.scrollY}px`);
});
.parallax-bg {
transform: translateY(calc(var(--scroll-y) * 0.4));
}
5. Mouse-Tracking Glow Effect
document.addEventListener('mousemove', (e) => {
document.documentElement.style.setProperty('--mouse-x', `${e.clientX}px`);
document.documentElement.style.setProperty('--mouse-y', `${e.clientY}px`);
});
.glow-card::before {
content: '';
position: absolute;
inset: 0;
background: radial-gradient(
400px at var(--mouse-x) var(--mouse-y),
rgba(108, 99, 255, 0.15),
transparent 70%
);
border-radius: inherit;
pointer-events: none;
}
6. @property: Typed CSS Variables (Modern)
@property lets you define typed, animatable custom properties:
@property --gradient-angle {
syntax: '<angle>';
initial-value: 0deg;
inherits: false;
}
.rotating-gradient {
background: conic-gradient(from var(--gradient-angle), #6c63ff, #00ff87, #6c63ff);
animation: rotate 4s linear infinite;
}
@keyframes rotate {
to { --gradient-angle: 360deg; }
}