This system has six tokens in two families, based on axis and whether the space sits inside a box or between boxes. This site uses them throughout. Press i and hover anything to see them in action.
The six tokens
| Token | Axis | Internal / external | CSS property |
|---|---|---|---|
| inset | both | internal | padding |
| inset-block | vertical | internal | padding-block |
| inset-inline | horizontal | internal | padding-inline |
| gap | both | external | gap |
| gap-block | vertical | external | row-gap |
| gap-inline | horizontal | external | column-gap |
The Token name describes the design intent; the
CSS property column shows which property it sets. Each token type has a
scale from xs through 2xl, giving you tokens like spacing.inset.md and
spacing.gap-block.lg.
Reading the names
Every name has a prefix and an axis.
The prefix says where the space sits: inset is inside the box,
gap is between boxes.
The axis says which way it runs: block is vertical, inline is
horizontal. No axis means both.
So inset-block is vertical padding and gap-inline is the space
between two buttons side by side.
That’s the whole rule. It works across all six names, with nothing else to memorise.
Why this taxonomy
Most spacing systems use a flat scale: spacing-1 through
spacing-8. The person using it then has to remember which value belongs where.
That creates three problems:
- The name says nothing about its purpose. It’s easy to reach for the wrong value.
- Direction gets lost. Horizontal and vertical spacing share values even when they shouldn’t.
- Internal and external space get mixed together. Padding and gaps can drift apart.
Directional tokens put that reasoning in the name. The token tells you whether the space is inside a component or between components, and which way it runs.
A team can keep using a taxonomy like this long after the person who created it has gone, because the names carry the reasoning.
Each one in use
inset: padding on every side
Default padding inside cards, modals and containers. inset.xs suits a small
badge; inset.md is the default card padding; inset.lg gives modal
content more room.
inset-block: vertical padding
Vertical-only padding when you want to control vertical rhythm independently: section headers, card bodies and compact controls.
inset-inline: horizontal padding
Horizontal-only padding where content aligns vertically but needs space at the sides: inputs, buttons and navigation items.
gap: space on both axes
The gap shorthand on a grid or wrapping flex row, where the same value should
apply in both directions. Tag lists, card grids and anything that reflows.
gap-block: vertical space between siblings
Rows in a list, fields in a form, stacked cards and the space between sections of a page. One value applied uniformly down a stack.
gap-inline: horizontal space between siblings
Icons and labels, buttons in a group, columns in a grid and cards sitting side by side.
What this replaced
The first version of this system, built for Lantern’s Lux 4.0 in 2025, also had six tokens:
inset, insetBlock, insetInline, inline,
block and stack. Two things about it needed fixing.
First, the external tokens had no clear marker. insetBlock was internal,
block was external, and the only difference was the missing prefix. A rule you
have to learn rather than read is a weak rule.
Second, stack named a use case while the other five named geometry. It worked
for repeated items such as list rows, form fields and accordion rows, but sat oddly beside
names describing where the space goes. In practice, stack and block
were both external vertical space at different sizes. One concept with two names.
Calling the external family gap fixes both.
External space now has its own prefix, and stack and block become
gap-block, using a scale that combines their old values. The names changed; the
spacing didn’t.
gap also describes what the code actually does. Sibling spacing is handled with
flex and grid gap rather than margins. If a gap-block token is used for a margin,
that’s a sign the token may be being used in the wrong place.
Getting people to use them
A flat scale is easier to learn. spacing-4 asks nothing of you. Six directional
names ask you to learn two distinctions: inside a box or between boxes, and which way the
space runs.
That’s the real cost of this system, and it’s a teaching cost. On a small team you can cover it in a workshop and answer questions as they come up. The larger the team, the more people there are who never got the workshop and are guessing.
Which is why the naming rule matters more than the names. Prefix, then axis. Someone meeting
gap-inline for the first time can work out what it does without being told, and
that’s the only version of this that survives a handover. A system that needs a lesson before
anyone can use it is already dependent on the person who taught it.
There’s a second reason to care about that, and it’s newer. The thing that makes these names
easier for people to learn also makes them easier for AI to interpret. spacing-4
gives an AI a number and no reason to prefer it over spacing-3.
gap-block-md says the space is external, vertical and mid-scale.
Teams are handing more of this work across: laying out a component, applying spacing, matching what’s already there. Tokens are documentation. Names that carry their own reasoning will matter more rather than less. It’s the difference between a model guessing which of eight numbers you meant and a model reading the intent straight off the token.
Where it sits in the architecture
These are semantic tokens in a two-tier model. They point to primitive tokens: raw values like 4, 8, 16 and 24 that are rarely used directly.
Designers and developers use the semantic layer. The primitives stay underneath.
The important part is the intent. The token tells you what kind of space you’re adding, not just how much.
The axis names come from CSS logical properties: padding-inline,
row-gap and so on. inline and block adapt to the
writing direction, so the same tokens work in right-to-left layouts without a second set of
names.
There is one catch: the code using the tokens needs to use logical properties too. If it
still uses margin-left and padding-right, the direction won’t flip
with the tokens.
inset is the one name that doesn’t come directly from this CSS naming pattern. In
CSS, it means the
top, right, bottom and left offsets of a positioned element, not padding. The token community
adopted it for padding and it stuck. In this context, it’s clear enough to keep.
Held in reserve, not shipped: inset-block-start and its three siblings for
asymmetric padding. Real components do need them, section headers being the obvious example.
Four extra names on day one is a lot to add for something many teams may never need, so
they get added when a component genuinely asks for them.