Foundations
Color
You choose one seed color. Expressive derives five tonal palettes from it, then assigns every color role in light and dark from those palettes.
From seed to palettes
Expressive uses Google's material-color-utilities, the same algorithm behind Material You. It works in HCT, a color space built so that tone (lightness) is perceptually even. Each palette keeps one hue and chroma and varies tone from 0 (black) to 100 (white). The palettes below come from the seed in Customize.
Color roles
Components never reference palettes or hex values. They use roles, and every role has a matching on- role for content placed on it, so text always has enough contrast.
- primary: key actions and the active state of controls.
- secondary: less prominent elements like filter chips and tonal buttons.
- tertiary: contrasting accents that balance primary and secondary.
- error: errors and destructive actions.
- *-container: softer fills for components like FABs and selected chips.
Surfaces
Material builds depth from surface tone more than shadows. Five container levels step from the lowest to the highest emphasis: a navigation rail on surface-container, a dialog on surface-container-high.
Schemes
The variant decides how the seed is interpreted. expressive (the default) shifts hues for livelier secondary and tertiary colors; tonal-spot is the calmer classic Material look; monochrome removes chroma entirely. Try them in Customize.
npx @grenishrai/expressive theme --variant tonal-spot
npx @grenishrai/expressive theme --contrast 0.5 # higher contrastUsing color
<div className="bg-primary-container text-on-primary-container" />
<p className="text-on-surface-variant" />
<div style={{ borderColor: "var(--ex-color-outline-variant)" }} />Every role is available as bg-*, text-*, border-* and the other Tailwind color utilities, and as --ex-color-* variables. To override one role, pass it to createTheme:
createTheme({ colors: { seed: "#6750A4", overrides: { light: { primary: "#4F378B" } } } });