Thinking

Directional spacing tokens

Spacing tokens should tell you what the space is doing, not just how much space there is.

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

TokenAxisInternal / externalCSS 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:

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.