# Pick a spacing base and stop arguing > Use a 4pt base with an 8pt rhythm, name space by the job it does, and density modes cost you nothing. - Source: https://flashdsm.com/blog/spacing-scale-4pt-8pt - Published: 2026-09-11 - Author: Flash DSM Team - Track: Fundamentals - Reading time: 5 min read --- ## 4pt or 8pt A spacing base is the unit every spacing value is a multiple of. The two common choices are 4 and 8, and the fight between them is mostly about how dense your UI is. An 8pt base (Material Design's grid is built on 8dp) gives you fewer, bolder options: 8, 16, 24, 32. That's great for marketing pages and roomy layouts. It hurts in dense product UI. The gap between an icon and its label wants to be 4 or 6. A compact table row wants 12px of vertical padding. On a strict 8pt grid neither exists, so people quietly type 10 and the grid is gone by Friday. A 4pt base keeps the small steps and still lets everything above 16 move in 8s. That's our default recommendation: 4pt base, 8pt rhythm at larger sizes. Why multiples of 4 at all? Screens render at 1x, 1.5x, 2x and 3x. 4px at 1.5x is 6 device pixels, a whole number. 5px at 1.5x is 7.5, and half pixels make edges blurry. A base of 4 stays crisp at every common density. ## A scale that holds up Here's a scale on a 4pt base. The token names are multiples of the 4px base, so `--space-4` is 16px wherever you meet it. | Token | px | rem | Typical use | |---|---|---|---| | `--space-0` | 0 | 0 | resets | | `--space-0-5` | 2 | 0.125 | hairline offsets, focus ring gap | | `--space-1` | 4 | 0.25 | icon to label | | `--space-2` | 8 | 0.5 | padding inside compact controls, related items | | `--space-3` | 12 | 0.75 | list item padding, button vertical padding | | `--space-4` | 16 | 1 | default component padding | | `--space-6` | 24 | 1.5 | card padding, between form groups | | `--space-8` | 32 | 2 | between content groups | | `--space-12` | 48 | 3 | between page sections | | `--space-16` | 64 | 4 | major sections | | `--space-24` | 96 | 6 | page-level breaks, marketing | 2px is the one step that breaks the "multiple of 4" rule. You need it for a focus ring offset or a one-off optical nudge, and it's better as a named token than as a stray `2px` in a component. ## Why it isn't linear Look at the steps: 2, 4, 8, 12, 16, 24, 32, 48, 64, 96. Each one is 1.33 to 2 times the one before. A linear scale (4, 8, 12, 16, 20, 24, 28, 32...) would give you evenly spaced numbers, and that's the problem. People see spacing differences relatively. Going from 4px to 8px doubles the gap and everyone notices. Going from 60px to 64px is the same 4px and nobody can tell. A linear scale hands you a dozen options at the large end that look identical, so designers pick among them at random and pages drift. A scale where every step is visibly bigger than the last means each choice actually says something. What it says is grouping. Small gaps mean "these belong together", big gaps mean "this is a new thing". A scale with clear steps lets space carry that structure, so you need fewer dividers and boxes. ## Spacing tokens aren't layout The spacing scale covers space inside and between components: padding, gaps, the distance between a heading and its paragraph. Layout is a different set of decisions: grid columns, gutters, page margins, the maximum content width, breakpoints. Layout values often depend on the viewport and can reference the scale, but they're their own tokens: ```css :root { --layout-max-width: 72rem; --layout-gutter: clamp(1rem, 4vw, 2rem); --layout-page-margin: var(--space-6); } ``` Keep them apart. Once the container width lands in the spacing scale as `--space-max: 1200px`, the scale stops meaning anything. ## Inset, stack and inline Scale tokens like `--space-4` are primitives. Components shouldn't use them directly, for the same reason they shouldn't use `--blue-600`: the name says what the value is, not what it's for. The semantic layer most systems borrow comes from Nathan Curtis at EightShapes, and it names space by the job it does: - **Inset:** padding inside a container, on all sides. A *squish* inset has less vertical than horizontal padding (buttons, chips). A *stretch* inset has more (roomy touch targets). - **Stack:** vertical space between items stacked on top of each other. - **Inline:** horizontal space between items in a row. ```css :root { --space-inset-sm: var(--space-2); /* 8px */ --space-inset-md: var(--space-4); /* 16px */ --space-inset-squish-md: var(--space-2) var(--space-4); /* 8px 16px */ --space-stack-sm: var(--space-2); /* 8px */ --space-stack-md: var(--space-4); /* 16px */ --space-stack-lg: var(--space-8); /* 32px */ --space-inline-xs: var(--space-1); /* 4px */ --space-inline-sm: var(--space-2); /* 8px */ } .card { padding: var(--space-inset-md); } .button { padding: var(--space-inset-squish-md); } .form { display: flex; flex-direction: column; gap: var(--space-stack-md); } .toolbar { display: flex; gap: var(--space-inline-sm); } ``` Notice the stacks use `gap` on the parent instead of margins on the children. The container owns the space between its children. A component with its own outer margin is a component that looks wrong the second time you place it. ## Density modes Once components point at semantic spacing tokens, density is a remap. A comfortable default for most screens, a compact mode for data tables and admin tools, and not one component changes: ```css [data-density="compact"] { --space-inset-md: var(--space-3); /* 12px */ --space-inset-squish-md: var(--space-1) var(--space-3); /* 4px 12px */ --space-stack-md: var(--space-2); /* 8px */ } ``` Set the attribute on a single table or on the whole app. Because custom properties inherit, everything inside picks up the compact values. > [!WARNING] > Compact must not mean small targets. WCAG 2.2 criterion 2.5.8 (AA) asks for pointer targets of at least 24 by 24 CSS pixels, and 44px is the safer floor on touch. Shrink the padding around text, not the clickable area. Density is partly a type question too. A compact mode often drops body text a step. See [type scales for real content](/blog/type-scales-for-real-content). ## How to stop the argument Spacing debates are almost always 10px versus 12px, and they never end because both are defensible. You won't win it on taste. Decide once and make the decision easy to follow. 1. Pick the base. Use 4, or 8 if your product genuinely has no dense UI. 2. Write the scale down and freeze it. An off-scale value needs a reason in the pull request. 3. Components use semantic tokens (inset, stack, inline). Primitives live only in the token file. 4. Make raw values easy to spot in review. 5. If a design really needs 20px in three places, add a token after a short conversation. Don't type it inline. For step 4, a grep is enough to start: ```bash # padding, margin or gap set in raw pixels grep -rnE "(padding|margin|gap)[a-z-]*:[[:space:]]*[0-9]+px" src/ ``` In Flash, spacing lives under [[Foundations > Spacing]], alongside radius and the rest. If a step is missing, ask Ask Flash ({{⌘K}}) for it: the suggested prompt "Add a spacing step between 16 and 24" comes back as a patch card you **Apply** or **Discard**. The "Shuffle density" quick action tries a tighter or looser spacing scale across the system, and Copy code can derive a Density variant under "Also derive".