Skip to content
omaris

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.

background
card
primary
primary-container
secondary-container
tertiary
tertiary-container
muted
destructive
success
warning
info
<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-lowest
surface-container-low
surface-container
surface-container-high
surface-container-highest
surface-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-1
shadow-2
shadow-3
shadow-4
shadow-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

TokenUse
ease-standardAnything that stays on screen.
ease-standard-decelerateSomething entering.
ease-standard-accelerateSomething leaving.
ease-emphasizedThe one the eye should follow.
ease-emphasized-decelerateA large entrance.
ease-emphasized-accelerateA 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.