How spacing tells people what belongs together.
Before anyone reads a word on your screen, they have already sorted it into groups. Nobody decides to. The eye clusters whatever sits close together and separates whatever sits apart, and it does this before reading begins. So spacing isn’t decoration you add once the layout works. It’s the first thing the reader takes in, and when it says something different from your content, the spacing wins.
Distance is a sentence the reader can’t skip
Max Wertheimer described this in 1923 as one of the Gestalt principles of perceptual organisation: elements that are near each other are seen as a group. Put twelve dots in a row at even spacing and you see a line. Nudge them into pairs and you see six pairs, even though nothing about the dots changed. In the classic demonstrations, proximity tends to beat similarity too: pair a red dot with a blue one, and the pairs hold together even though the colours say otherwise.
For interface work, that means every gap on the screen is making a claim: these two things are related, or they aren’t. Most layouts make those claims by accident.
The one rule: inside is tighter than outside
The rule that does most of the work fits in one line: the space inside a group must be clearly smaller than the space between groups. A label sits closer to its own field than to the field above it. A heading sits closer to the section it introduces than to the section it ends. An icon sits closer to its own text than to the next button.
Here is the version of this bug that shows up in almost every settings screen. Each field is a label, then a 12px gap, then a 44px input, then a 12px gap before the next label:
Display name
12px
[ input ]
12px
Email
12px
[ input ]
Every label is exactly equidistant between two inputs, so the eye has no way to tell which one it names. People work it out, but they pay for it on every field, and the screen reads as a list of ten loose things instead of five pairs. Change only the numbers, 6px from label to its input and 24px from input to the next label, and the same content becomes five obvious units. That’s a 4:1 ratio, and nothing else about the screen changed.
Headings have the same problem in a different spot. A section heading with equal margin above and below floats between the two sections and belongs to neither. Give it two or three times as much space above as below, 48px above and 16px below for example, and it attaches to the content it introduces.
Why equal spacing looks right in the design file
Equal spacing feels disciplined. The inspector shows the same number in every gap and the mockup looks tidy. But tidy and readable are different properties. A screen where every gap is 16px has no hierarchy of distance at all, which forces the reader to rebuild the structure from the words. That’s slower, and on a screen they’re skimming, it means they don’t rebuild it at all.
The same trap hides in spacing scales. A scale of 4, 8, 12, 16, 24, 32, 48 is a good set of allowed values, but neighbouring steps are too close to signal different relationships. A 12px gap next to a 16px gap is a difference most people won’t see unless the two sit side by side. In practice, a screen needs three or four tiers of distance, each roughly double the one below:
- Tight, 4–8px: things that are one unit. An icon and its label, a label and its field, a price and its billing period.
- Related, 12–16px: separate items in the same group. Rows in a list, fields in one section, buttons in one toolbar.
- Section, 32–48px: one group to the next.
- Page, 64px and up: major regions, such as the hero to the first content block.
Dense tools compress every tier; what matters is the ratio between them. If you can’t say which tier a gap belongs to, the reader can’t either.
Boxes change the maths
Proximity isn’t the only grouping signal. A later addition to the Gestalt list, common region, says that things inside a shared boundary group together regardless of distance. That’s why cards work: the fill or border does the grouping, so the gap between two cards can be smaller than the padding inside them and the layout still reads.
The two signals can also fight. Strip the borders off a set of cards for a flatter look and keep the old spacing, and the content of neighbouring cards drifts together, because the boundary that was doing the grouping is gone and the distances were never set up to replace it. Pick which signal carries each level and let it do the job alone. The same goes for divider lines: if a list needs a rule between every row to stay legible, the spacing isn’t doing its work, and the rule is a patch. (When you do want rules, getting a 1px line to render as one pixel is its own problem.)
The gap you coded isn’t the gap people see
Two things make the rendered distance differ from the number in your stylesheet.
First, text boxes carry invisible space. A 16px label at a line height of 1.5 sits in a 24px box, with 4px of leading above and below the glyphs. A 24px heading at 1.2 carries only 2.4px on each side. So an 8px gap from a heading and an 8px gap from body text don’t look the same, and the visible gap from the bottom of a word to the next element is always bigger than what you typed. The fix is to judge gaps from ink to ink, or to trim the extra space off the first and last lines, which the line height post walks through.
Second, shapes carry visual weight that their bounding boxes don’t report. An icon with transparent padding inside its frame sits visibly further from its label than the measured gap says. That’s the same mismatch behind why centred things look off-centre, and the answer is the same too: trust what you see over the number in the inspector.
Let the parent own the spacing
In code, the usual root cause is that every component brings its own outer margin. The card has a bottom margin, the heading has a top margin, the button has a margin on all sides, and the gap between any two of them is a sum of decisions nobody made together. In CSS, margin collapsing makes that sum even harder to predict.
The cleaner pattern is for reusable components to carry no outer margin at all, and for the
container that arranges them to set the distance between them. In CSS that’s
gap on a flex or grid parent. In Flutter it’s explicit spacers placed by the
parent Column rather than Padding baked into each child widget. Then
a gap only exists where somebody decided which tier it belongs to.
The same rule on a store screenshot
App Store and Play screenshots are tiny layouts that get a glance, which makes grouping by distance matter more, not less. The usual frame has a headline, a smaller supporting line, and a device. If the supporting line sits as far from the headline as it does from the device, it reads as a caption for the phone instead of the second half of the message. Keep the headline and its supporting line tight, one unit, and give the pair clear air before the device.
None of this needs a designer’s eye. It needs one decision per gap: same group or different group. Make the answer visible in the numbers, and the reader gets the structure before they get to the words.