Spacing & Alignment
10 minSpacing & Alignment
The fastest way to distinguish a polished interface from a rushed one is to look at the spacing. Not the broad layout decisions—those are usually fine—but the micro-spacing: the gap between a card's edge and its content, the padding around a nested element, the alignment of icons inside buttons. These decisions are invisible when right and jarring when wrong.
The 4px grid
Every spacing value in a production interface should be a multiple of 4. Not 5, not 7, not "whatever looks right." Multiples of 4: 4, 8, 12, 16, 20, 24, 32, 40, 48, 64.
Why 4? Because it divides cleanly into most screen densities, scales predictably, and eliminates the "is this 13px or 14px?" ambiguity that plagues freeform spacing. When every gap is a 4px multiple, elements snap into alignment across the entire viewport without manual adjustment.
Most design systems encode this as a spacing scale:
--space-1: 4px;
--space-2: 8px;
--space-3: 12px;
--space-4: 16px;
--space-6: 24px;
--space-8: 32px;
Tailwind does this natively—p-2 is 8px, p-4 is 16px, gap-3 is 12px. The system is already built. Use it consistently and the 4px grid emerges for free.
Concentric border radius
When a rounded element is nested inside another rounded element, the corners must agree mathematically. The rule:
Outer radius = Inner radius + Padding
A card with border-radius: 16px and padding: 8px containing an inner panel with border-radius: 8px looks correct because 16 = 8 + 8. The arcs run parallel—the inner curve sits exactly inside the outer curve at a constant distance. Set the inner radius to 16px—the same as the outer—and the inner corners pinch toward the outer corners, leaving a visible seam where the curves don't agree.
Encode the rule in CSS variables so it survives refactoring:
.card {
--card-radius: 16px;
--card-padding: 12px;
border-radius: var(--card-radius);
padding: var(--card-padding);
}
.card .inner {
border-radius: calc(var(--card-radius) - var(--card-padding));
}
If the math produces a negative number—outer is 8px, padding is 16px—use 0. The inner element is far enough inside that corner curvature doesn't matter.
Optical centering
Elements don't sit where geometry says they should. A play button (right-pointing triangle) centred mathematically inside a circle looks shifted left, because all the triangle's visual mass sits on the right side of its bounding box. The fix: nudge it ~10% rightward until it looks centred.
This principle applies to icon-only buttons, asymmetric letterforms (a capital Q has a tail that makes it look too high when centred), and icon-plus-text buttons where the padding on the icon side should be slightly less than the text side:
.button {
padding-left: 16px;
padding-right: 20px; /* slightly more on the text side */
}
The cleanest fix for icon centring is to adjust the SVG's bounding box so the visual centre matches the geometric centre. Lucide, Phosphor, and Heroicons all do this. If the icon set is external, CSS transform: translateX(2px) is the escape hatch.
The rule of thumb: trust your eye over your grid. The grid is the starting point. Optical adjustment is the finishing step. If something looks 1px off after snapping to the grid—it probably is.
Common mistakes
- Same radius inside and out. The most common concentric bug. The inner element pushes against the outer corner.
- Non-4px spacing values. A 14px gap next to a 16px gap creates a subtle rhythm break the eye detects but can't name.
- Symmetric padding on icon-plus-text buttons for asymmetric icons. The icon looks shifted; users notice without articulating why.
- Centring bounding boxes, not visual mass. Figma's "Align centre" tool centres boxes, not perception. For triangles, chevrons, and asymmetric shapes, manual adjustment is required.
- Forgetting that border-width counts. A 1px border eats into the corner. For precise systems, subtract
border-widthfrom the inner radius too.