Foundations
Design tokens
The colour roles, surface tiers, elevation and shape scale — and why never to use a raw value.
Every value a component uses is a token. Tokens follow the theme; raw values do not, which is why bg-[#6750A4] looks fine until someone changes the hue or turns on high contrast.
Colour roles
Each accent role comes as a set of four: the role, its foreground, its container, and the container's foreground. Use the pair — bg-primary with text-primary-foreground — and contrast takes care of itself in both themes.
Colour roles
Each swatch is painted with the class written on it.
backgroundcardprimaryprimary-containersecondary-containertertiarytertiary-containermuteddestructivesuccesswarninginfo<script lang="ts"> const ROLES = [ ['bg-background text-foreground', 'background'], ['bg-card text-card-foreground', 'card'], ['bg-primary text-primary-foreground', 'primary'], ['bg-primary-container text-primary-container-foreground', 'primary-container'], ['bg-secondary-container text-secondary-container-foreground', 'secondary-container'], ['bg-tertiary text-tertiary-foreground', 'tertiary'], ['bg-tertiary-container text-tertiary-container-foreground', 'tertiary-container'], ['bg-muted text-muted-foreground', 'muted'], ['bg-destructive text-destructive-foreground', 'destructive'], ['bg-success text-success-foreground', 'success'], ['bg-warning text-warning-foreground', 'warning'], ['bg-info text-info-foreground', 'info'] ];</script><div class="grid w-full grid-cols-[repeat(auto-fill,minmax(11rem,1fr))] gap-3"> {#each ROLES as [cls, name] (name)} <div class="flex min-h-16 flex-col justify-end rounded-shape-md border border-border p-3 {cls}"> <code class="text-label-md">{name}</code> </div> {/each}</div> The seven accent roles are primary, secondary, tertiary, destructive, success, warning and info. Components that take a tone prop accept exactly these.
Surface tiers
Material's elevation ladder, as backgrounds. A higher tier reads as closer to the reader without a shadow.
Surface tiers
The elevation ladder, as background tiers. Higher reads as closer.
surface-container-lowestsurface-container-lowsurface-containersurface-container-highsurface-container-highestsurface-variant<script lang="ts"> // Written out in full: Tailwind scans source text, so a class assembled at // runtime (`bg-{tier}`) would never be generated. const TIERS = [ ['bg-surface-container-lowest', 'surface-container-lowest'], ['bg-surface-container-low', 'surface-container-low'], ['bg-surface-container', 'surface-container'], ['bg-surface-container-high', 'surface-container-high'], ['bg-surface-container-highest', 'surface-container-highest'], ['bg-surface-variant', 'surface-variant'] ];</script><div class="grid w-full grid-cols-[repeat(auto-fill,minmax(11rem,1fr))] gap-3"> {#each TIERS as [cls, name] (name)} <div class="flex min-h-16 flex-col justify-end rounded-shape-md border border-border p-3 {cls}"> <code class="text-label-md text-foreground">{name}</code> </div> {/each}</div> Elevation
Elevation
One utility works in both themes — the shadow reads its colour at the point of use.
shadow-1shadow-2shadow-3shadow-4shadow-5<div class="grid w-full grid-cols-[repeat(auto-fill,minmax(8rem,1fr))] gap-4"> <div class="flex min-h-20 items-end rounded-shape-md bg-card p-3 shadow-1"> <code class="text-label-md text-foreground">shadow-1</code> </div> <div class="flex min-h-20 items-end rounded-shape-md bg-card p-3 shadow-2"> <code class="text-label-md text-foreground">shadow-2</code> </div> <div class="flex min-h-20 items-end rounded-shape-md bg-card p-3 shadow-3"> <code class="text-label-md text-foreground">shadow-3</code> </div> <div class="flex min-h-20 items-end rounded-shape-md bg-card p-3 shadow-4"> <code class="text-label-md text-foreground">shadow-4</code> </div> <div class="flex min-h-20 items-end rounded-shape-md bg-card p-3 shadow-5"> <code class="text-label-md text-foreground">shadow-5</code> </div></div> One utility works in both themes: the shadow reads its colour at the point of use rather than baking it in.
Shape
Two scales. rounded-* follows the theme's radius setting and is what most components use. rounded-shape-* is Material's own fixed ladder — none, xs, sm, md, lg, xl, 2xl at 0, 4, 8, 12, 16, 28 and 48px — for the places the spec is specific.
Both follow the same roundness knob, so turning corners up or down moves everything together.
Motion
| Token | Use |
|---|---|
ease-standard | Anything that stays on screen. |
ease-standard-decelerate | Something entering. |
ease-standard-accelerate | Something leaving. |
ease-emphasized | The one the eye should follow. |
ease-emphasized-decelerate | A large entrance. |
ease-emphasized-accelerate | A large exit. |
Always pair a transition with motion-reduce:transition-none. The variant honours the OS setting and the theme store's motion, so a user who turns motion off in your app's own settings gets what they asked for.
State layers
Material's interaction tint, as opacities rather than colours: --state-hover, --state-focus, --state-pressed, --state-dragged, --state-disabled-content, --state-disabled-surface. High contrast raises them. If you are building a pressable element of your own, stateLayer from omaris is the class string omaris's own controls use.
Adding a role
A new colour role has to exist in three places or it will be wrong in one of them: :root, .dark, and the @theme inline block that turns it into a utility. There is no fourth place, and skipping the dark one is how a token ends up invisible at night.