Expressive

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.

primary
secondary
tertiary
neutral
neutralVariant

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.

primarytext: on-primary
primary-containertext: on-primary-container
secondarytext: on-secondary
secondary-containertext: on-secondary-container
tertiarytext: on-tertiary
tertiary-containertext: on-tertiary-container
errortext: on-error
error-containertext: on-error-container
  • 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.

container-lowest
container-low
container
container-high
container-highest

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 contrast

Using 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" } } } });