Theming
A theme in Fusion is a CSS class that provides values for a predefined set of design tokens. The tokens are split into three separate contracts; primitive, semantic, and component. These contracts serve two purposes:
- They provide a way to consume the values of the tokens.
- They provide a way to define the values of the tokens.
The contracts define which tokens are available as CSS variables, but not what their values are. For each contract, you will need a corresponding theme that provides the values of the tokens.
Consuming Tokens
You can use the tokens defined in the contracts to style your components. For example, you can use the tokens.semantic contract to style your components with the values of the tokens.
import { tokens } from "@codedazur/fusion/style";
import { style } from "@vanilla-extract/css";
export const myStyle = style({
backgroundColor: tokens.semantic.colors.primary.base,
color: tokens.semantic.colors.primary.onBase,
});Now, the backgroundColor of the element that receives this style will be set to var(--semantic-colors-primary-base) and the value of this variable will be provided by whichever theme is active.
This page focuses on defining and applying themes, but you can find more information about consuming tokens in the Tokens guide.
If you have wrapped your application in the App component, the necessary CSS variables wil already be available, but if not, the style above will not have any effect. So, let’s see how to apply a theme.
Applying Themes
Themes can be applied simply by providing them to the className of any element. That element will then provide its entire subtree (including itself) with the appropriate CSS variables.
import { darkTheme } from "@codedazur/fusion/style";
import { classNames } from "@codedazur/fusion-ui";
import { myStyle } from "./my-component.css";
function MyComponent() {
return (
<div className={classNames(darkTheme, myStyle)}>
<h1>Hello, world!</h1>
</div>
);
}Now, the element will have its colors set to the primary color values defined in the darkTheme.
The darkTheme above only provides values for the semantic tokens. If you want to apply a primitive and/or component theme as well, you can do that in the same way.
<div className={classNames(primitiveTheme, darkTheme, componentTheme)}>In fact, that’s exactly what the App component does.
Creating Themes
You can create a theme using Vanilla Extract by providing it with one of the three contracts, and a set of values that are compatible with the contract. You can provide every single value, or you could use a tool like deepmerge-ts to override parts of an existing theme.
import { merge } from "deepmerge-ts";
import { createTheme } from "@vanilla-extract/css";
import { tokens, darkValues } from "@codeadzur/fusion-ui/style";
export const mySemanticTheme = createTheme(
tokens.semantic,
merge(darkValues, {
colors: {
primary: {
base: tokens.primitive.colors.amber[500],
},
},
}),
);If you want to be able to extend your theme in the future, you can choose to export the values separately. Take care to satisfy the Partial<SemanticTokens> interface to keep everything type-safe.
import { merge } from "deepmerge-ts";
import { createTheme } from "@vanilla-extract/css";
import { SemanticTokens, tokens, darkValues } from "@codeadzur/fusion-ui/style";
export const mySemanticValues = merge(darkValues, {
colors: {
primary: {
base: tokens.primitive.colors.amber[500],
},
},
} as const satisfies Partial<SemanticTokens>);
export const mySemanticTheme = createTheme(tokens.semantic, mySemanticValues);