Utilities
The small set of classes everything else is built from. There are deliberately few of them — a scale you can hold in your head is a scale that stays consistent.
Spacing
a four-pixel step, and nothing between
p-1
4px
p-2
8px
p-3
12px
p-4
16px
p-5
20px
p-6
24px
p-8
32px
Type
eleven steps, and every size on every page is one of them
display · 72px
the error pages
4xl · 42px
the hero figure
3xl · 32px
a large figure
2xl · 26px
one per page
xl · 24px
a figure on a card
lg · 18px
a section
md · 16px
a card, an email body
base · 14px
prose
sm · 13px
tables, dense controls
xs · 12px
meta
2xs · 11px
uppercase labels
The steps are theme tokens in src/css/app.css. Email templates cannot read a token, so they write the same values in pixels. npm run design fails on any size that is not a step — this page used to say six, and the product was using thirty-three.
Borders and corners
one line colour, one radius
border-line
border-brass
dashed
rounded-full
Everything in Coffer is 3px except an avatar, which is a circle. A single radius is what makes a set of components look like one product.
Numbers
monospaced and column-aligned, always
Proportional — the digits drift
- 1,240.00
- 18,240.00
- 9.75
- 128,950.00
Monospaced and tabular — they line up
- 1,240.00
- 18,240.00
- 9.75
- 128,950.00
Every figure in this template is font-mono tabular-nums and right-aligned. In a ledger, a column that does not line up is a column nobody can add up by eye.
Hiding things
three ways, and they are not the same
hiddenGone for everyone, including a screen reader. Use it when the thing is genuinely not there.sr-onlyOff the screen, still read aloud. This is how an icon button gets a name.aria-hiddenOn the screen, not read aloud. For decoration that would only be noise — a chevron, a dot, an avatar beside a name already written out.x-cloakHidden until Alpine has booted, so nothing flashes on the way in.