Do & Don't
The short list.
Do
- Keep chroma low outside the primary; lean on
muted,secondaryandaccentfor surfaces and fills. - Use
success,warning,infoanddestructivefor semantic state (an alert, a badge, a status cell). Reach forprimaryonly for the product's own emphasis. - Use
tabular-numson any number that sits in a column or changes value, so digits don't shift the layout. - Fill primary actions solid, outline secondary ones, and let the focus ring carry the accent.
- Style with Tailwind semantic utilities (
bg-primary,text-muted-foreground,border-border). - Use Phosphor for icons.
- Check both themes. Semantic utilities (
bg-card,text-muted-foreground,border-border) flip on their own — nodark:needed. - In dark, raise elevation by going lighter — a lighter surface with a hairline edge.
- Run
npm run check:contrastafter retuning any colour in either theme.
Don't
- Do not flood large areas with the primary or any saturated fill; the accent lives in lines, marks and buttons.
- Do not use
primaryto mean "success" or any other status. It's a different hue for a reason — the brand accent should never double as a status colour. - Do not use pure black or pure white — in either theme. The dark ground is a neutral charcoal; text is an off-white on the same neutral ramp.
- Do not stack heavy shadows; on light elevation is a hairline ring plus a soft shadow, and on dark a lighter surface plus a hairline light edge.
- Do not use 600 outside page titles and section headings — everywhere else (buttons, labels, tabs, chips, badges, table headers) tops out at 500.
- Do not hard-code a hex, a font name or a px value the theme already carries.
- Do not hard-code a colour and patch dark with a
dark:twin; use the semantic utility. Reservedark:for genuinely mode-specific cases. - Do not copy a light value into dark by hand; edit both blocks in
theme.css. - Do not edit the generated files in
components/ui; re-runnpx shadcn@latest addinstead.