UI/UX Principles Every Developer Should Know — Spacing, Typography, and Visual Hierarchy
W
Writer
Published: 16 Jun 2026
12 min read
The five design rules that explain 80% of the quality gap between amateur and professional UIs — the 8-point spacing grid, modular type scale, WCAG contrast, white space, and visual weight.
Design Is Not Taste — It Is Rules
Most developers avoid design because they believe it requires innate taste or years of art school training. It does not. The difference between a UI that looks amateur and one that looks professional is almost entirely explained by a handful of concrete, learnable rules — consistent spacing, correct type scale, adequate contrast, and deliberate visual hierarchy. Break these rules and the UI looks off, even if the viewer cannot explain why. Follow them and the UI looks intentional.
This guide covers the five most impactful UI principles for developers — the 8-point spacing grid, typographic scale and hierarchy, colour contrast and accessibility, white space, and visual weight. Each section includes before/after code and explains exactly why the change works. No design background required.
Principle 1 — The 8-Point Spacing Grid
The single most impactful change you can make to any UI is committing to a spacing system. The 8-point grid means all spacing and sizing values are multiples of 8 — 8, 16, 24, 32, 40, 48, 64, 80, 96. Some teams use a 4-point base for finer control: 4, 8, 12, 16, 20, 24, 32, 40, 48.
Why does this work? Consistency eliminates visual noise. When spacing is arbitrary (padding: 13px here, margin: 17px there), the eye detects the inconsistency even without consciously measuring. When every gap is from the same set of values, the layout reads as intentional — because it is.
Tailwind's default spacing scale (1 = 4px, 2 = 8px, 4 = 16px, 6 = 24px, 8 = 32px) is a 4-point grid. Using Tailwind classes automatically keeps you on the grid — as long as you do not reach for arbitrary values like p-[13px]. Stick to the named scale: p-2, p-4, p-6, gap-2, gap-4, and so on.
Tailwind Class
Value
Use For
p-1 / gap-1
4px
Icon internal padding, tight badges
p-2 / gap-2
8px
Small component internal spacing
p-3 / gap-3
12px
Button padding (sm)
p-4 / gap-4
16px
Standard card padding, form gaps
p-6 / gap-6
24px
Section padding, card body
p-8 / gap-8
32px
Large section padding
p-12 / gap-12
48px
Page section spacing
p-16 / gap-16
64px
Hero sections, major layout gaps
Principle 2 — Typographic Scale and Hierarchy
Typography is 90% of UI design — most interfaces are predominantly text. Two problems destroy typographic quality in developer-built UIs: using too many font sizes (every element has its own arbitrary size), and insufficient contrast between heading and body text (everything is similarly sized and weighted).
The Modular Scale
A modular scale generates harmonious font sizes by multiplying a base size by a fixed ratio. The most usable ratios for UI text are 1.25 (Major Third) and 1.333 (Perfect Fourth). With a 16px base and 1.25 ratio: 12.8px → 16px → 20px → 25px → 31px → 39px. Round to the nearest even number for cleanliness.
Step
Raw Value (×1.25)
Rounded
Use
-1
12.8px
12px
Caption, legal text
0 (base)
16px
16px
Body text
+1
20px
20px
Body large, lead text
+2
25px
24px
H4, card titles
+3
31px
30px
H3
+4
39px
36px
H2
+5
49px
48px
H1
+6
61px
60px
Display / hero
html
Before: Poor Typography Hierarchy
<!-- Everything is too similar in size — no clear hierarchy --><div><pstyle="font-size: 18px; font-weight: 600;">Dashboard</p><pstyle="font-size: 15px;">Welcome back, Arjun</p><pstyle="font-size: 14px;">You have 3 pending tasks</p><pstyle="font-size: 14px; font-weight: 500;">View all tasks</p></div>
tsx
After: Clear Typographic Hierarchy
/* Clear size and weight contrast between each level */
<div className="flex flex-col gap-1">
<h1className="text-3xl font-bold text-neutral-900 tracking-tight">
Dashboard
</h1><pclassName="text-body-lg text-neutral-500">
Welcome back, Arjun
</p><pclassName="text-body text-neutral-600 mt-2">
You have <spanclassName="font-semibold text-neutral-900">3 pending tasks</span></p>
</div>
The Three-Weight Rule
Most UIs need only three font weights: Regular (400) for body text, Medium (500) or Semibold (600) for labels and secondary headings, and Bold (700) for primary headings. Using more than three weights creates visual noise rather than hierarchy. Using fewer (everything in 400) creates a flat, unreadable interface.
Principle 3 — Colour Contrast and Accessibility
WCAG (Web Content Accessibility Guidelines) defines minimum contrast ratios for readable text. Level AA — the legal standard in most countries including under India's GIGW guidelines — requires a 4.5:1 ratio for normal text and 3:1 for large text (18px+ or 14px+ bold). Most developer-built UIs fail this standard because grey-on-white text looks fine on a bright monitor and terrible on a phone in sunlight.
Text Colour
Background
Contrast Ratio
WCAG AA Pass?
#9CA3AF (gray-400)
#FFFFFF
2.5:1
Fail
#6B7280 (gray-500)
#FFFFFF
4.6:1
Pass (barely)
#4B5563 (gray-600)
#FFFFFF
7.0:1
Pass
#374151 (gray-700)
#FFFFFF
10.4:1
Pass
#111827 (gray-900)
#FFFFFF
16.7:1
Pass
#3B82F6 (blue-500)
#FFFFFF
3.0:1
Fail for small text
#1D4ED8 (blue-700)
#FFFFFF
7.3:1
Pass
Check Contrast Before Committing to a Colour
Use the WebAIM Contrast Checker (webaim.org/resources/contrastchecker/) or the Figma A11y plugin before finalising any text colour. The Chrome DevTools accessibility panel also shows the contrast ratio for any selected element. Make this a habit — retrofitting contrast is expensive.
tsx
Before vs After: Muted Text That Passes Contrast
{/* BEFORE: gray-400 on white = 2.5:1 — WCAG FAIL */}
<p className="text-gray-400">Last updated 2 hours ago</p>
{/* AFTER: gray-500 on white = 4.6:1 — WCAG AA PASS */}
<p className="text-gray-500">Last updated 2 hours ago</p>
{/* On dark backgrounds — white text passes, but light blue may not */}
{/* BEFORE: blue-300 on gray-900 = 3.8:1 — FAIL for normal text */}
<span className="bg-gray-900 text-blue-300">Active</span>
{/* AFTER: blue-200 on gray-900 = 5.4:1 — PASS */}
<span className="bg-gray-900 text-blue-200">Active</span>
Colour is Not Your Only Signal
8% of men have some form of colour vision deficiency. Never use colour as the only indicator of state. Error states need an icon or label in addition to red colour. Success states need a checkmark in addition to green. Required fields need an asterisk in addition to a coloured border.
tsx
Accessible Error State
{/* BAD: colour only — invisible to red-green colour blind users */}
<input className="border-red-500" />
{/* GOOD: colour + icon + text */}
<div>
<inputclassName="border-red-500 focus:ring-red-500"aria-invalid="true"aria-describedby="email-error"
/><pid="email-error"className="text-red-600 text-sm mt-1 flex items-center gap-1"><AlertCircleclassName="h-4 w-4 flex-shrink-0" />
Please enter a valid email address
</p>
</div>
Principle 4 — White Space
The most common mistake in developer-built UIs is not enough white space. Empty space is not wasted space — it is breathing room that makes content scannable, groups related elements, and communicates visual hierarchy. The instinct to fill every pixel usually results in a cluttered, anxious-looking interface.
The Law of Proximity
Elements that are close together are perceived as related. Elements that have space between them are perceived as separate. Use this deliberately: put more space between sections than between items within a section. The gap between a label and its input should be smaller than the gap between two form fields.
<form className="flex flex-col gap-6"> {/* large gap between fields */}
{/* Name row — two fields grouped together */}
<div className="grid grid-cols-2 gap-4">
<divclassName="flex flex-col gap-1.5"> {/* tight gap: label → input */}
<labelclassName="text-label text-neutral-700">First Name</label><inputclassName="..." /></div><divclassName="flex flex-col gap-1.5"><labelclassName="text-label text-neutral-700">Last Name</label><inputclassName="..." /></div>
</div>
<divclassName="flex flex-col gap-1.5"><labelclassName="text-label text-neutral-700">Email</label><inputclassName="..." /></div><divclassName="flex flex-col gap-1.5"><labelclassName="text-label text-neutral-700">Phone</label><inputclassName="..." /></div>
{/* Extra space before action — separates content from action */}
<button className="mt-2 ...">Submit</button>
</form>
Page-Level White Space
The container max-width and horizontal padding dramatically affect perceived quality. A layout that stretches to the full browser width at 1440px looks amateur. A layout capped at 1280px with 24–48px horizontal padding looks intentional. Give your content room to breathe.
tsx
Page Layout with Breathing Room
{/* Container with responsive padding and max-width */}
<main className="min-h-screen bg-neutral-50">
<divclassName="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8 py-12">
{/* Section spacing: large vertical gaps between sections */}
<sectionclassName="mb-16"><h2className="text-h2 text-neutral-900 mb-2">Section Title</h2><pclassName="text-body-lg text-neutral-500 mb-8 max-w-2xl">
Descriptive subtitle that does not stretch full width —
long lines are hard to read (aim for 60–75 characters per line)
</p>
{/* Content */}
</section><sectionclassName="mb-16">
{/* Next section */}
</section></div>
</main>
Principle 5 — Visual Weight and Hierarchy
Every element on a page has visual weight — how much it draws the eye. Weight comes from size, colour, contrast, and density. The primary action on a page (the CTA button, the key number) should have the most visual weight. Supporting information should have less. Every element competing for the same weight creates a flat, unreadable page.
The Three-Level Hierarchy
Structure every screen around three levels of visual weight: Primary (the one thing the user should notice first), Secondary (supporting context), and Tertiary (available but not demanding attention). If you cannot identify the primary element on a screen, neither can the user.
tsx
Three-Level Hierarchy in a Stats Card
<div className="bg-white rounded-xl shadow-sm border border-neutral-200 p-6">
{/* TERTIARY: label — muted, small, not demanding attention */}
<p className="text-label text-neutral-500 uppercase tracking-wide">
MonthlyRevenue
</p>
{/* PRIMARY: the number — large, bold, high contrast, eye goes here first */}
<p className="text-4xl font-bold text-neutral-900 mt-1">
₹2,84,500
</p>
{/* SECONDARY: context — meaningful but supports the primary */}
<div className="flex items-center gap-1.5 mt-2">
<TrendingUpclassName="h-4 w-4 text-success" /><spanclassName="text-body-sm font-medium text-success">+12.4%</span><spanclassName="text-body-sm text-neutral-400">vs last month</span>
</div>
</div>
Button Hierarchy
Every page should have at most one primary button. When every action is the same visual weight (all solid filled buttons), the user has no signal about which action matters most. Use filled buttons for the primary action, outlined for secondary, and ghost for tertiary.
✓All spacing values are multiples of 4 or 8 — no arbitrary px values
✓Font sizes come from a defined scale — no arbitrary sizes
✓At most three font weights used in the entire UI
✓Body text colour is at least gray-600 on white — check with contrast tool
✓Muted/hint text is at least gray-500 on white (4.6:1 ratio)
✓Error and status states use icon/text in addition to colour
✓Related elements have less space between them than unrelated elements
✓Content has a max-width container — nothing stretches to full browser width
✓Each screen has a clear primary element with the most visual weight
✓At most one primary (filled) button per action group
I am not a designer — will following these rules make my UI look professional?
Yes — the rules in this guide address the specific mistakes that make developer UIs look amateur. Consistent spacing, adequate contrast, and clear hierarchy explain 80% of the visual quality difference between an amateur and a professional UI. The remaining 20% is taste and craft that comes with practice.
What line length should body text be?
60–75 characters per line is the research-backed optimal reading range. In practice, cap paragraph containers at max-w-prose (65 characters in Tailwind) or max-w-2xl. Full-width paragraphs at 1440px are exhausting to read — the eye loses its place returning to the next line.
Should I use system fonts or import a custom font?
System fonts (font-family: system-ui) load instantly with zero performance cost and look native on each platform. For a branded product, Inter is the best choice — it is a professionally designed UI font, free, and renders excellently at all sizes. Load it via Google Fonts with display=swap to avoid blocking render.
How do I check WCAG contrast ratio in code?
In Chrome DevTools: inspect an element → Accessibility panel → the contrast ratio is shown next to the text colour. The browser also shows the ratio in the colour picker when you click a colour value. For automated checking, add axe-core to your Storybook setup — it flags contrast failures in your component stories.
Key Takeaways
The 8-point grid (all spacing multiples of 8, or 4 for finer control) eliminates visual noise — use Tailwind's named scale, never arbitrary px values
Use a modular type scale (base 16px × 1.25 ratio) with at most three font weights — arbitrary sizes and uniform weight both destroy hierarchy
Body text must be at least gray-600 on white to pass WCAG AA — gray-400 is decorative, not readable
Never use colour as the only signal for state — always pair with an icon or label
Law of Proximity: elements within a group should have less space between them than elements between groups
Every screen needs one primary element — if everything has equal visual weight, nothing does