Why custom properties and not a theme object
A JavaScript theme object requires a provider, which requires a client boundary, which drags server-safe components across it. It also means your design tokens live in JavaScript and are unavailable to plain CSS, so any hand-written stylesheet is disconnected from the system.
CSS custom properties have neither problem. They cascade, they work in any stylesheet, they are readable from devtools, and they need no provider at all. That is why VivekUI has no required provider: there is nothing for one to carry.
Setting your brand colours
Override the variables on `:root` in your own stylesheet, imported after the library's. That single declaration block is your theme. Because custom properties cascade, you can also scope a theme to a subtree by setting the variables on any wrapper element — useful for a marketing section that intentionally differs from the app shell.
This is genuinely all there is to it. There is no build step to regenerate, no config to restart the dev server for, and no theme object to keep in sync with your CSS. Change the value, the page updates.
Why your CSS always wins
Component classes are intentionally flat and low in specificity. A single class selector of your own beats them, which means overriding a component is a one-line change rather than an escalation into `!important` or three-deep selectors.
Compare this to the `sx` prop and its equivalents, where overriding often means fighting generated styles that were injected at runtime with higher specificity than yours. The flat-class approach trades some encapsulation for the ability to be overridden predictably, which for most product teams is the better trade.
Scoping a theme to part of the page
Because custom properties inherit, setting them on a container themes everything inside it. A dashboard with a dark sidebar and a light content area needs one variable block on the sidebar, not a second theme configuration.
This composes with dark mode rather than conflicting with it: the dark theme sets the same variable names to different values at the root, and a locally-scoped block still overrides them within its subtree. There is no precedence puzzle because it is just the cascade doing what the cascade does.
Responsive without props
Layout components are responsive by default rather than through breakpoint props. That is a deliberate constraint: encoding breakpoints in props means your component data carries layout decisions, and changing a breakpoint becomes a code change across every usage.
When you do need a real breakpoint, write a media query in your own CSS against your own class. That is one place where having a plain stylesheet rather than a styling runtime is straightforwardly simpler — a media query is a media query.
The trade-off, stated plainly
Custom properties cannot do everything a JavaScript theme can. You cannot compute a style from a prop at runtime, derive a palette programmatically at render time, or write conditional styles in JavaScript. If your design system genuinely needs that, a runtime CSS-in-JS library is the right tool and this approach will feel restrictive.
What you get in exchange is no styling runtime in the browser, no provider, working Server Components, and overrides that behave predictably. For most product work that is the better side of the trade, but it is a trade rather than a free win.
Key takeaways
- Custom properties need no provider, which is why server-safe components stay server-safe
- Your theme is a variable block on :root in a stylesheet imported after the library's
- Component classes are flat and low-specificity, so one class of yours wins without !important
- Scope a theme to any subtree by setting the variables on a wrapper — it is just the cascade
- Layout is responsive by default so breakpoints do not leak into component data
- Trade-off: you cannot compute styles from props at runtime the way a JS theme allows
Conclusion
Theming through CSS custom properties is less powerful than a JavaScript theme and considerably less machinery. No provider, no build step, no specificity fights, and it works in Server Components — which for most applications is the more useful set of properties.
Enjoyed this article?

Vivek Kumar Singh
Technical Expert · Full Stack Cloud Engineer · Tokyo, Japan