I've been theming components with CSS variables — and nothing else — for about two years now. No context providers, no runtime style objects, no theme libraries.
The whole approach in 12 lines
:root {
--bg: #fff;
--fg: #111;
}
[data-theme='dark'] {
--bg: #0d0d0d;
--fg: #f0f0f0;
}That's it. Toggle data-theme on the html element and every variable cascades. No
re-render, no flash, no client/server mismatch.
Tokens vs. semantic names
Two-tier naming is the trick that scales: --gray-9 is a token,
--fg is a semantic alias for it. Components only ever read the semantic names.