Foundations
Spacing
Ten steps on a 4px base. The scale is linear at the bottom, where small differences are visible, and roughly geometric above — because past about 16px nobody can tell 20 from 24 without measuring.
The scale
4px base, doubling from 8. Every gap in the system is one of these.
The first four steps go up by 4px. After that they roughly double. That shape is deliberate: at small sizes a 4px difference is the difference between a label touching its input and not, so the scale needs resolution there. At large sizes the same 4px is invisible, and offering it only invites two engineers to pick differently for the same gap.
| Token | rem | px | Use |
|---|---|---|---|
| --ht-space-0 | 0 | 0px | Explicit zero, so a reset reads as a decision rather than an omission. |
| --ht-space-1 | 0.25rem | 4px | Icon to its label. Nothing else. |
| --ht-space-2 | 0.5rem | 8px | Inside a control — chip padding, badge gaps. |
| --ht-space-3 | 0.75rem | 12px | Between related controls; table cell padding. |
| --ht-space-4 | 1rem | 16px | The default gap. Panel padding, paragraph spacing. |
| --ht-space-5 | 1.5rem | 24px | Between a heading and its content. |
| --ht-space-6 | 2rem | 32px | Between subsections. |
| --ht-space-7 | 3rem | 48px | Above an h2 — the main rhythm of a docs page. |
| --ht-space-8 | 4rem | 64px | Between major page regions. |
| --ht-space-9 | 6rem | 96px | Page bottom padding, so the last line is not against the fold. |
Vertical rhythm
Documentation pages use three gaps and no others: 7 above a section heading, 5 between a heading and its first content, 4 between paragraphs. That is the entire vertical system on this site.
.page h2 { margin-top: var(--ht-space-7); }
.page h2 + * { margin-top: var(--ht-space-5); }
.page p + p { margin-top: var(--ht-space-4); }
/* Gaps live on the container, never as a margin on the child. */
.toolbar {
display: flex;
gap: var(--ht-space-3);
} When not to use the scale
- Optical alignment. Nudging an icon half a pixel to sit on a baseline is not spacing, it is drawing. Do it in the component and say so in a comment.
- Borders and hairlines. A 1px rule is not space-0 rounded up.
- Anything derived from content. A dropdown positioned under its trigger is computed, not spaced.