Theming vs. variants: when to use each

Teams reach for theming when they need variants, and variants when they need theming. The confusion is expensive and usually shows up in production.

A button component with an intent prop that accepts "default" | "danger" | "warning" is a component with variants. A button component that renders red in one context and black in another because of a surrounding theme provider is using theming. These are different mechanisms, they solve different problems, and conflating them creates components that are hard to understand and harder to maintain.

The confusion is common because both mechanisms produce visual variation. But they produce it for different reasons, at different scopes, and with different implications for documentation and code.

What theming is for

Theming is a system-level mechanism. It changes how the entire component set renders based on a context: a brand, a color scheme, a product surface with distinct visual requirements. When dark mode switches the app from light tokens to dark tokens, that's theming. When a white-label product renders with a partner brand's colors, that's theming. The same component, the same intent, a different surface.

The key characteristic is scope. A theme is inherited. Everything inside a theme provider picks it up automatically. You don't have to tell each button "you're in dark mode now." The theme applies and components respond.

Theming is appropriate when the variation is about context, not intent. The button in dark mode is still semantically the same button. It's not communicating anything different to the user. It just lives in a different visual environment.

What variants are for

Variants are a component-level mechanism. They represent distinct semantic states that a consumer makes a choice about. When a developer passes variant="destructive" to a button, they're saying something: this action destroys data, use the visual treatment that communicates that. The variant is not inherited from context. It's a deliberate decision at the point of use.

Variants are appropriate when the visual difference carries meaning that the consumer of the component needs to be in control of. A primary button and a secondary button are variants. A filled button and an outlined button are variants. These distinctions matter to the product. They communicate hierarchy, urgency, role.

If your variants are encoding the same semantic distinction as your themes, you have a design problem upstream of the code: the component isn't clear about what it's communicating.

The failure mode: variants inside themes

The most expensive confusion is building variant behavior into a theme. This is what happens when a design says "buttons in the admin panel should be blue" and someone implements it by making a theme where the primary button token resolves to blue.

Now the primary button in the main app is black, and in the admin panel it's blue. From the user's perspective, these look like different button variants. From the code's perspective, they're the same component. From the documentation's perspective, it's nearly impossible to explain clearly, because the button's appearance in any given context depends on what theme is wrapped around it, and that information usually isn't available when you're looking at the component in isolation.

The distinction breaks down in practice because it's often a judgment call. But there's a clarifying question: does the visual difference correspond to a semantic difference the consumer should be in control of, or is it an automatic consequence of the context the component lives in? If the consumer needs to choose, it's a variant. If the component should just respond to its environment, it's a theme.

When component tokens make it murkier

Component-level tokens sit at the intersection of theming and variants, which is why they create so much confusion.

A button component that consumes a --button-primary-bg token is doing something between the two mechanisms: the theme can change the value of --button-primary-bg, which changes how the component renders. But the component is still applying the token in the context of its primary variant, not as a response to a theme override but as the designated semantic treatment for a specific variant.

This is a reasonable pattern when the goal is to give brand themes control over component appearance while keeping the semantic structure of variants intact. The button is still "primary" and still communicates hierarchy, but what "primary" looks like can vary by brand.

Where this breaks down: when every component gets a full suite of component tokens regardless of whether they're meaningfully configurable, and the tokens start overriding each other in ways that are hard to predict. If there's a --button-primary-bg token and a color-bg-primary semantic token and a brand-primary-color primitive token, and all three are in scope, someone has to understand the override chain to know which one wins. That's documentation debt and a support burden.

Making the call in practice

The practical heuristic: variants are declared in the component API and documented in the component docs. Theming is declared in a theme object or CSS variable layer and documented in a theming guide. If you can't tell which of those two places a visual behavior belongs in, you're probably looking at a design decision that hasn't been made yet.

A component's variant API should be stable. The set of variants a button supports shouldn't change because someone introduced a new theme. Themes should change the appearance of variants, not the existence of them.

The inverse is also true: if you find yourself adding variants to cover different brand expressions, a "primary-brand-a" variant alongside a "primary" variant, you're encoding brand context into the component API instead of into the theme. That approach scales to exactly as many brands as you have when you build it, and no further.

Greg Sargent
Greg SargentDirector of Design Systems, Spring Health

I write about design systems, accessibility, and the way AI is changing how we build software.

Published January 22, 2026

Be the first to rate this article.