Utilities

The utility classes behind the elements: token colours, radius, breakpoints and sizes, with the usual layout and spacing classes.

On this page

Utilities are single-purpose classes for the one-off look: a margin, a colour, a grid. They come from UnoCSS, through the Wind preset the ui preset builds on, so the names are the familiar ones (flex, gap-4, p-3, text-sm, grid-cols-2, hidden). The preset changes only what ties them to the UI: colours, radius and breakpoints read the tokens, and each element's sizes are classes too.

Only what you use is built

The build scans the templates it is given and writes a rule for each class it finds, so the stylesheet holds only what the site uses. A class assembled in a template from pieces, like 'bg-' ~ $color, is never found. Write the whole name, or list the full names a template can produce.

Beating an element

The reset, the base and the element CSS sit in a layer before the utilities, and variants are written with :where(). A utility on an element therefore beats the element's own rule at the same specificity, with no !important:

{ui card, class: 'max-w-sm' /}
{ui button, title: 'Wide', class: 'w-full' /}

Colours

Every token is a colour. The fill is bg-{name}, text-{name}, border-{name} and the rest of the colour utilities. A pair names the text that sits on its fill, so text-primary-foreground goes on bg-primary. Because the tokens change with the theme, a utility written with them follows light, dark and any added theme, and there is no dark: variant to write.

Token colours

primary
raised
sunken
hover
muted
destructive
border

<div class="flex flex-wrap gap-2">
	<div class="rounded-md bg-primary px-3 py-2 text-sm text-primary-foreground">primary</div>
	<div class="rounded-md bg-raised px-3 py-2 text-sm text-foreground">raised</div>
	<div class="rounded-md bg-sunken px-3 py-2 text-sm text-foreground">sunken</div>
	<div class="rounded-md bg-hover px-3 py-2 text-sm text-foreground">hover</div>
	<div class="rounded-md px-3 py-2 text-sm text-muted">muted</div>
	<div class="rounded-md bg-destructive px-3 py-2 text-sm text-destructive-foreground">destructive</div>
	<div class="rounded-md border border-border px-3 py-2 text-sm">border</div>
</div>

The names are background, foreground, card, primary, secondary, muted, accent, destructive, success, warning and info, each with a -foreground pair, plus border, input and ring. Theming shows them live.

Alpha

A slash sets the opacity of any colour utility. It is the way to get a soft tint from a status colour.

Alpha

bg-primary/10
bg-success/15
bg-warning/15
bg-info/15
bg-destructive/15

<div class="flex flex-wrap gap-2">
	<div class="rounded-md bg-primary/10 px-3 py-2 text-sm text-primary">bg-primary/10</div>
	<div class="rounded-md bg-success/15 px-3 py-2 text-sm text-success">bg-success/15</div>
	<div class="rounded-md bg-warning/15 px-3 py-2 text-sm text-warning">bg-warning/15</div>
	<div class="rounded-md bg-info/15 px-3 py-2 text-sm text-info">bg-info/15</div>
	<div class="rounded-md bg-destructive/15 px-3 py-2 text-sm text-destructive">bg-destructive/15</div>
</div>

Radius

rounded-lg is --radius, rounded-md is 2px less and rounded-sm is 4px less. Changing --radius in a theme moves all three, and every element that rounds with the token. rounded-full and rounded-none stay fixed.

Radius

rounded-sm
rounded-md
rounded-lg
rounded-full

<div class="flex flex-wrap gap-2">
	<div class="border border-border px-3 py-2 text-sm rounded-sm">rounded-sm</div>
	<div class="border border-border px-3 py-2 text-sm rounded-md">rounded-md</div>
	<div class="border border-border px-3 py-2 text-sm rounded-lg">rounded-lg</div>
	<div class="border border-border px-3 py-2 text-sm rounded-full">rounded-full</div>
</div>

Breakpoints

A prefix applies a utility from that width up: sm:grid-cols-2 is two columns at 640px and wider. One scale serves every prefix, and xxl takes the place of 2xl.

Prefix From
xs: 480px
sm: 640px
md: 768px
lg: 1024px
xl: 1280px
xxl: 1536px

Start with the layout for the smallest screen and add prefixes as the space grows.

Columns by breakpoint

One column
Two from sm
Four from lg
Resize the window

<div class="grid grid-cols-1 gap-2 sm:grid-cols-2 lg:grid-cols-4">
	<div class="rounded-md bg-sunken p-3 text-sm">One column</div>
	<div class="rounded-md bg-sunken p-3 text-sm">Two from sm</div>
	<div class="rounded-md bg-sunken p-3 text-sm">Four from lg</div>
	<div class="rounded-md bg-sunken p-3 text-sm">Resize the window</div>
</div>

Layout and spacing

The rest are the standard utilities. A flex or grid container with a gap lays out its children, and elements nest inside them like any other tag.

Flex and gap

Project Updated today
Active

<div class="flex items-center justify-between gap-4 rounded-lg border border-border p-4">
	<div class="flex flex-col gap-1">
		<span class="font-semibold">Project</span>
		<span class="text-sm text-muted">Updated today</span>
	</div>
	<div class="flex gap-2">
		{ui badge, title: 'Active' /}
		{ui button, title: 'Open', variant: 'outline' /}
	</div>
</div>

Tags carry no margin of their own, so the base adds space above a block that follows another. Inside a container that sets a gap, put mt-0 on its blocks so the two do not add up.

Size classes

A size that changes by breakpoint is a class. Each element with sizes has {element}-{size} classes that take the same prefixes: lg:btn-lg is the large button from 1024px. The size param does the same without the prefix, so use the class only when the size depends on the screen.

Class Sizes
btn-{size} sm, lg, icon
avatar-{size} sm, lg
spinner-{size} sm, lg
status-{size} sm, lg
modal-{size} sm, lg

They are the variants and sizes rules from the overview, written as classes.

Prose

.prose is the one class of its own. Put it on a container of rendered markdown. It spaces and styles only the container's direct children, so a component placed among them keeps its own look:

<div class="prose">
	{$html|noescape}
	{ui alert, title: 'Heads up' /}
</div>