Layout
Radius, spacing and page width — the parts of the system that are conventions rather than code.
Radius carries hierarchy
Three steps, and the step tells you what kind of thing you are looking at: the bigger the radius, the bigger the thing. A button inside a card is 8px inside 12px, and that nesting reads correctly. Swap them and it looks wrong before you can say why.
rounded4pxBadges and status pills — small enough that more would look soft.rounded-lg8pxControls: buttons, inputs, selects, filters.rounded-xl12pxSurfaces: cards, dialogs, the border around a table.rounded-full—Avatars and the switch track only.Why radius is not a token
It would have been easy to add --radius-sm/md/lg and feel systematic. It would also have been the wrong call.
Tokenising radius in Tailwind means redefining what rounded-lg and rounded-xl mean for every consumer — and Hana already uses both, on hundreds of elements, for things Azalea knows nothing about. Adopting the design system would have silently reshaped a working app, which is exactly the outcome the “tokens lifted verbatim” rule exists to prevent.
Colour earns a token because it changes with the theme and must be swappable. Radius does not change; it just needs to be consistent. A documented convention buys that at no risk. Revisit if the three steps ever stop being enough.
Spacing
Tailwind's scale, used at five steps. Anything outside them is usually a sign the layout wants restructuring rather than a new value.
gap-1.5 / 6pxInside a control — icon to label, badge to count.gap-3 / 12pxBetween controls on one row.p-5 / 20pxCard padding.py-3 / 12pxA row in a DataList.mb-10 / 40pxBetween page sections.Page width
Operational tools, not marketing pages — the constraint is scannability, not immersion.
max-w-3xlfor a detail page — a run, a deployment. Long lines are harder to scan than short ones.max-w-5xlfor a list or a dashboard, where rows carry several columns.- Never full-bleed. A table on a 32-inch monitor with no max width is unreadable, and the person reading it is the one who has to scan across it.
Motion
Transitions are for state changes a person caused — a hover, a toggle. Nothing animates on its own except a Skeleton, which is signalling that something is happening.
Azalea ships a global prefers-reduced-motion reset in tokens.css: every animation and transition collapses to nothing when the OS asks. It is a global reset rather than a per-component opt-in because a system that leaves this to each component leaves it out of the next one — and nothing here animates in a way that carries meaning when it stops.