← Journal

DESIGN SYSTEMS · · 5 min read

A Fibonacci Spacing Scale You Can Actually Ship

The Crystal Design System spacing scale has ten tokens, and this walkthrough covers why the Fibonacci progression works, how to name and apply the tokens in CSS, and where to break the scale on purpose.

Most spacing scales fail the same way. They start tidy, then pick up a 12 here and a 20 there, until nobody can say what the system is.

The Crystal Design System uses ten spacing values: 0, 4, 8, 16, 24, 40, 64, 104, 168, 272. Here is why those numbers, how we name them, and where we break them on purpose.

Why a Fibonacci progression works

Strip out the 0 and the 4, and the rest is the Fibonacci sequence (1, 2, 3, 5, 8, 13, 21, 34) multiplied by 8. Each value is the sum of the two before it. The ratio between neighbors settles near 1.6 as the scale climbs.

That gives you two practical benefits.

Every step is visibly different. A linear 8-point scale gives you 40 and 48, which no one can tell apart on screen. Those two values become a recurring argument in design review. On this scale the choice after 40 is 64, and nobody mistakes one for the other.

Tokens add up to tokens. 16 + 24 = 40. 24 + 40 = 64. 64 + 104 = 168. When a padded element sits inside a gapped container, the combined offset usually lands back on the scale. Nested layouts stay on the system without anyone doing arithmetic.

The result is rhythm: small, tight steps inside components and large, confident steps between sections.

The two bookends

The 0 is a token because resets should be explicit. Writing a named zero in a stylesheet reads as a decision, where a bare 0 can read as an omission.

The 4 sits outside the sequence. It is a half step for the places where 8 is too loose: an icon next to its label, a caption under a thumbnail, a hairline offset. Treat it as a detail token, not a layout token.

Naming the tokens

Name tokens by step, not by pixel value. If a token called space-24 ever becomes 28px, the name is a lie. An index survives a rescale.

Use two layers. Primitives hold the raw scale. Semantic tokens say what the space is for, and components consume only those.

:root {
  /* Primitives */
  --ac-space-0: 0;
  --ac-space-1: 4px;
  --ac-space-2: 8px;
  --ac-space-3: 16px;
  --ac-space-4: 24px;
  --ac-space-5: 40px;
  --ac-space-6: 64px;
  --ac-space-7: 104px;
  --ac-space-8: 168px;
  --ac-space-9: 272px;

  /* Semantic */
  --ac-inline-icon-gap: var(--ac-space-1);
  --ac-card-padding: var(--ac-space-4);
  --ac-stack-gap: var(--ac-space-3);
  --ac-section-padding: var(--ac-space-8);
}

.card {
  padding: var(--ac-card-padding);
  display: grid;
  gap: var(--ac-stack-gap);
}

Now a redesign of card density is a one-line change, and a code review can ask a useful question: does this component use a semantic token, or did someone reach for a primitive?

Where the scale needs an exception

A scale that never bends gets worked around in secret. Better to decide where it bends. These are the cases we plan for:

  • Line height. Body text at 16px with a 1.65 line height produces 26.4px lines, which is nowhere on the scale. Type owns the rhythm inside a paragraph. Spacing tokens own the space between blocks.
  • Touch targets. Interactive elements need a comfortable minimum hit area, commonly 44 to 48px. Meet it with min-height on the control. Do not invent a token for it.
  • Large values on small screens. 272px of section padding does not survive a phone. Use clamp() with scale tokens as the bounds.
  • Optical alignment. Sometimes an icon needs a 1 to 2px nudge to look centered. Do it locally and leave a comment.
  • Border radius. Radii have their own scale (4, 8, 12, 16). Do not borrow spacing tokens for them, and do not add 12 to the spacing scale because radius uses it.

Here is the responsive case in practice:

.section {
  padding-block: clamp(var(--ac-space-7), 12vw, var(--ac-space-9));
}

On a narrow screen this holds at 104px. On a wide one it grows to 272px. Both ends are tokens, so the exception stays inside the system.

The rule for breaking the rule

Every exception gets a name or a comment, never a bare number. If the same exception shows up three times, either promote it to a semantic token or fix the design that keeps needing it. Exceptions that explain themselves stay cheap. Silent ones pile up until the scale means nothing.

Takeaway for design leads

You do not need to read the CSS to use this. There are ten spacing values, and every gap and padding in a design should come from that list.

In handoff, ask one question: which token is this? If the answer is a number off the list, there are only two outcomes. Either the design changes, or the system gets a deliberate conversation about whether it should. Both outcomes are fine. What you are removing is the third option, where an unexplained 22px ships and gets copied.

Fewer values mean faster decisions, consistent layouts across screens and teams, and an audit that takes minutes instead of days. The scale will not make a layout good, but it takes spacing off the list of things anyone has to argue about.