
Dynamic UI Theming with CSS Custom Properties
Unlike SASS or LESS preprocessor variables that compile into static values at build time, native CSS Custom Properties (--variable-name) live directly in the browser DOM and participate in the CSS cascade. This allows you to centralize your color palette and spacing tokens, scope overrides to specific containers, and switch between light and dark themes dynamically with a single attribute change.
Example
/* Declare global tokens on the root element */
:root {
--bg-surface: #ffffff;
--text-primary: #333333;
--accent-color: #04ae92;
}
/* Override tokens when dark theme is active */
[data-theme="dark"] {
--bg-surface: #082840;
--text-primary: #f2fbf9;
--accent-color: #60e1cc;
}
Input : Root CSS Custom Properties and a theme toggle button
Output : Instant runtime theme switching without duplicating component selectors.
Implementation
:root {
--surface-bg: #ffffff;
--surface-border: #dedede;
--text-main: #333333;
--brand-primary: #60e1cc;
--brand-dark: #04ae92;
}
[data-theme="dark"] {
--surface-bg: #082840;
--surface-border: #16466b;
--text-main: #f4f7f9;
--brand-primary: #60e1cc;
--brand-dark: #60e1cc;
}
body {
background-color: var(--surface-bg);
color: var(--text-main);
transition: background-color 200ms ease, color 200ms ease;
}
.article-card {
border: 1px solid var(--surface-border);
padding: 24px;
}
.article-card a {
color: var(--brand-dark);
}
function toggleTheme() {
var root = document.documentElement;
var current = root.getAttribute("data-theme");
var nextTheme = current === "dark" ? "light" : "dark";
root.setAttribute("data-theme", nextTheme);
localStorage.setItem("preferred-theme", nextTheme);
}
Main Point: Define semantic token names (--surface-bg, --text-main) rather than literal color names (--white) so components never need to know which theme is active.
Credits
Photo by Lucas Benjamin