Patterns Ux
Gestalt: Spacing Is Syntax
Reading level
The label that belonged to the wrong field
A sign-up form had a CSS reset that applied margin-bottom: 16px to every element. Every label, every input, every field group — all spaced 16px apart. In a user test, 30% of participants initially associated the "Password" label with the email field above it, not the password field below it. They were the same distance from both.
The fix took 10 minutes: change label margin-bottom to 4px (tight to its field) and field margin-bottom to 24px (spacious gap before the next label). Nothing else changed. Label-field association accuracy jumped to 98%.
Gestalt proximity principle: elements that are physically close to each other are perceived as belonging to the same group. In forms, the critical ratio is: distance from label to its field must be less than distance from field to the next label. When those two distances are equal, the label appears unanchored — equidistant from two possible "owners."
Proximity is a pre-attentive feature — it's processed before conscious awareness, in parallel with the visual scene. A correctly-spaced form is parsed without cognitive effort. An equally-spaced form requires serial attention to disambiguate label ownership. The former is design; the latter is a puzzle the user didn't ask to solve.
The spacing ratio rule
The rule: label-to-field distance should be ≤50% of field-to-next-label distance. The absolute values don't matter — the ratio does.
/* ❌ Equal spacing — label ownership is ambiguous */
.form-field {
margin-bottom: 16px; /* applies to both labels and inputs */
}
/* ✅ Ratio spacing — label belongs to field below it */
.form-label {
margin-bottom: 4px; /* label tight to its input */
}
.form-input {
margin-bottom: 24px; /* wide gap before next label */
}
/* Ratio: 4/24 = 0.17 — well under the 0.5 threshold */
Three contexts where equal spacing creates the worst grouping problems:
- Multi-field form groups: the email/password pairing. If password label is equidistant from email input and password input, 30% of users misread it.
- Settings panels: a label, toggle, and description text. The description should be visually attached to its toggle, not floating between toggles.
- Data tables with row headers: the header should be visually closer to the cells in its row than to the header above or below it.
The diagnostic: take a screenshot of your UI and blur it until the text is illegible. If you can still identify which labels belong to which fields — the spacing is correct. If it's ambiguous, fix the ratio.
Spacing tokens in a design system should encode grouping semantics, not just size values. A well-designed spacing scale maps token names to use cases:
/* Semantically named spacing for grouping */
:root {
--space-in-group: 4px; /* label → its field */
--space-between: 20px; /* field → next group's label */
--space-section-gap: 40px; /* between major form sections */
}
/* Ratio: 4/20 = 0.2 — correct grouping signal */
/* Usage is semantic, not arbitrary */
When developers pick spacing values from --space-2, --space-4 without a semantic contract, they can easily produce --space-4 (16px) everywhere — equal spacing by accident. The semantic token layer prevents this by making the intent explicit.
The label that floated between two fields
During a user test for a new sign-up form, the facilitator watched a participant pause at the password field for almost eight seconds. "Which label is for which?" the user muttered. The form had a CSS reset that applied margin-bottom: 16px to every element — every label, every input, every wrapper. Everything was evenly spaced. The "Password" label sat exactly halfway between the email input above it and the password input below it.
The user guessed correctly — but that pause happened on every form field. By the last field, three users in the test had given up. The form looked clean. The spacing felt balanced. But users couldn't parse it without consciously working at it.
The technical cause was a CSS reset pattern common in component libraries: applying a uniform spacing variable (--space-4: 16px) to all child elements of a form. This flattened the visual hierarchy completely. The label-to-field distance equaled the field-to-next-label distance. At the Gestalt level, the label had two equally probable "owners" — the field above it and the field below it — because proximity to both was identical. The parser in the human visual system resolved the ambiguity by falling through to serial reading, which is slower and more error-prone.
The form had passed a design review and a visual QA pass. No one caught it because designers and engineers reviewing the form knew which labels belonged to which fields — the ambiguity is invisible to people with full context. It only surfaces under test with users who don't know the form. This is a systematic gap: spacing problems that only manifest for uninitiated users won't appear in code review or in QA by people who built the form.
The 10-minute fix that hit 98% label accuracy
The fix was two CSS changes. Label margin-bottom went from 16px to 4px — tight against its field. Input margin-bottom went from 0 to 24px — wide gap before the next label. Nothing else changed. No visual redesign. No new components. The form looked almost identical, but the spacing now told a story: "this label belongs to the thing directly below it."
Re-running the same user test, label-field association accuracy went from 70% to 98%. The eight-second pauses disappeared. Users moved through the form without stopping to orient themselves — the structure was pre-attentively obvious.
The implementation change also involved updating the design tokens. Instead of --space-4 (16px) applied uniformly, the team introduced two new semantic tokens: --space-in-group: 4px (for label-to-field within a group) and --space-between-groups: 24px (for field-to-next-label between groups). The ratio of 4/24 = 0.17 is well below the 0.5 threshold where ambiguity begins. The token rename was intentional — it encoded the grouping purpose directly in the name, preventing future developers from accidentally collapsing the distinction.
The design system team added a Storybook story called "Form group spacing — blur test." It rendered a form with both patterns and provided instructions: "Squint until text is illegible. Can you still identify label/field groups?" The blur test became part of the design review checklist for any new form component. At scale, this kind of embedded linting in the design process is more reliable than post-hoc review — it makes the correct spacing the obvious default and the wrong spacing visibly wrong.
Pattern at a glance
Annotated example: label-to-field spacing ratio
❌ EQUAL SPACING (8px everywhere)
"Password" floats equidistant — ambiguous ownership
✅ RATIO SPACING (4px / 20px)
Label clearly anchored to field below it
Try it: equal spacing vs ratio spacing
Both forms have the same labels and fields. The "Equal" mode uses 16px spacing between every element. The "Ratio" mode uses 4px label-to-field and 24px field-to-next-label. Try to quickly identify which label belongs to which field in each mode — the equal version requires a conscious parse.
Try the blur test: squint at both forms until the text is blurry. In the ratio version, you can still identify the groupings by the spacing patterns. In the equal version, every element appears as an isolated row with no group membership.
Notice the compound effect: the ratio form also applies a subtle horizontal indent to field descriptions, attaching them visually to their parent toggle. This is the same proximity principle applied on the horizontal axis — proximity → grouping → comprehension.
Showing: Ratio — 4px / 24px
CSS spacing tokens that enforce grouping semantics
The core pattern: two CSS variables that make label-to-field spacing always less than field-to-next-label spacing. Apply them once in your form stylesheet and every form inherits correct proximity ratios automatically.
:root {
--space-intra-group: 4px; /* label → its input */
--space-inter-group: 24px; /* input → next label */
}
.form-label {
display: block;
margin-bottom: var(--space-intra-group);
}
.form-input {
display: block;
width: 100%;
margin-bottom: var(--space-inter-group);
}
Key pitfall: applying a uniform margin-bottom from a CSS reset (e.g. * { margin-bottom: 16px }) collapses this ratio to 1:1 and makes labels equidistant from adjacent fields. Always override with form-specific spacing after any reset.
For component-based systems (React, Vue, Svelte), enforce the ratio inside a <FormGroup> wrapper component rather than relying on consumers to use the right tokens. Three implementation notes:
- Use gap, not margin, in flex/grid form layouts.
gapon the container gives you consistent inter-item spacing without margin collapse surprises. Combine with a tightmargin-bottomon labels. - Horizontal axis matters too. Description text below a toggle should be indented (padding-left: 24px) to appear visually attached to its parent control — proximity works on both axes.
- The blur test is your QA check. Screenshot the form, apply a CSS blur filter in DevTools (
filter: blur(4px)on the form), and verify groupings are still readable. If they're ambiguous blurred, they'll be slow to parse at full resolution.
/* FormGroup component styles */
.form-group {
display: flex;
flex-direction: column;
gap: var(--space-inter-group); /* space BETWEEN groups */
}
.form-group__field {
display: flex;
flex-direction: column;
gap: var(--space-intra-group); /* label tight to input */
}
In a design system, spacing token names should encode grouping semantics — not just numeric values. A scale of --space-1 through --space-8 is a menu of sizes; --space-intra-group and --space-inter-group are a contract. When designers and developers use the same semantic name for the same intent, the ratio is preserved even when absolute values change (e.g. a density update that reduces all spacing by 20% preserves the ratio as long as both tokens scale together). Add a Storybook story with the blur-test instructions embedded as a comment in the story file — it becomes discoverable documentation. For lint tooling, a Stylelint rule that warns when margin-bottom is set to the same value on both .form-label and .form-input within the same rule block will catch the equal-spacing accident at authoring time.
References
Remember
Key takeaways
-
Label-to-field spacing should be ≤50% of field-to-next-label spacing. The ratio communicates ownership — a label belongs to the element it's closest to.The blur test: squint at your form until text is illegible. If you can still identify label/field groupings from spacing alone, the proximity is correct. If groupings are ambiguous — fix the ratio.Semantic spacing tokens prevent equal-spacing accidents. Map token names to use cases (--space-in-group, --space-between-groups, --space-section) rather than abstract sizes — the semantic layer encodes the grouping intent.
-
The problem is common because CSS resets apply equal margin-bottom to all elements. Always override with form-specific spacing that differentiates in-group distance from between-group distance.Proximity applies on both axes — use horizontal indent (padding-left) to attach description text to its parent control, just as vertical spacing attaches labels to their fields.Proximity is pre-attentive — it's processed before conscious reading. Correct spacing means users parse structure without effort; incorrect spacing means every form interaction requires a parsing step the user didn't budget for.
Keep going
Finish this takeaway, then continue the track — Casey saved your spot locally.
Sign in with email to sync progress across devices (beta).
Inside the Casebook
New cases every few weeks — patterns from production UI engineering. Double opt-in, easy unsubscribe.
No spam. Unsubscribe anytime. Emails sent via Buttondown.
RSS feed