Apply Gestalt perceptual principles — proximity, similarity, continuity, closure, and figure-ground — to create UIs where users instantly understand relationships between elements without explicit visual separators.
Gestalt psychology describes how the human brain perceives visual patterns. In the 1920s, German psychologists identified a set of perceptual laws that explain why humans see groups, patterns, and relationships in visual fields rather than collections of individual dots and lines.
For UI developers, Gestalt principles are practical: they explain why certain CSS decisions work visually and others do not. The brain applies these perceptual laws automatically and instantaneously — before conscious analysis — which is why violating them produces interfaces that feel 'wrong' without users being able to explain why.
This lesson covers the five most important Gestalt principles for UI design and their direct CSS implementations.
Proximity is the most powerful Gestalt law for UI: elements close together are perceived as a group, even without borders or backgrounds.
Proximity in navigation:
1/* Navigation items: tight proximity within group, large gap between groups */
2.nav-group {
3 display: flex;
4 flex-direction: column;
5 gap: var(--space-1); /* 4px — items in same group feel connected */
6}
7
8.nav-section-gap {
9 /* Large gap between navigation sections = different groups */
10 margin-top: var(--space-6); /* 24px — 6× the item gap = clearly different */
11}
12
13/* This proximity difference communicates: these 3 items are one group,
14 these 2 items are a different group — without any borders */Similarity: elements that look alike are perceived as part of the same category.
1/* All clickable links look the same — Similarity groups them as 'links' */
2a {
3 color: var(--color-action);
4 text-decoration: underline;
5 text-decoration-color: transparent;
6 transition: text-decoration-color var(--transition-base);
7}
8a:hover {
9 text-decoration-color: var(--color-action);
10}
11/* Every link in the product looks identical = users know what links are */
12
13/* All primary buttons look the same */
14.btn-primary {
15 background-color: var(--color-action);
16 color: white;
17 font-weight: 600;
18 border-radius: var(--radius-md);
19}
20/* Every primary button is identical = users know what primary actions are */
21
22/* VIOLATION: mixing link styles breaks Similarity */
23.nav-link { color: #374151; } /* gray — not link-like */
24.footer-link { color: #2563eb; } /* blue — link-like */
25.body-link { color: #7c3aed; } /* purple — brand link? */
26/* Three different link appearances = users do not know which are clickable */Similarity principle in design systems:
When to break Similarity intentionally:
Continuity: eyes naturally follow lines, curves, and paths. Elements arranged along a continuous line are perceived as related and belonging together.
Continuity in progress indicators:
1/* Progress steps: the line between them creates continuity */
2.steps {
3 display: flex;
4 align-items: center;
5 gap: 0; /* No gap — the connecting line must flow continuously */
6}
7
8.step {
9 display: flex;
10 align-items: center;
11 flex: 1;
12}
13
14.step-indicator {
15 display: flex;
16 align-items: center;
17 justify-content: center;
18 width: 2rem;
19 height: 2rem;
20 border-radius: 50%;
21 background-color: var(--color-action);
22 color: white;
23 font-size: 0.875rem;
24 font-weight: 600;
25 flex-shrink: 0;
26 z-index: 1; /* above the connecting line */
27}
28
29.step-connector {
30 flex: 1;
31 height: 2px; /* the continuous line */
32 background-color: var(--color-border);
33 position: relative;
34}
35
36.step-connector.completed {
37 background-color: var(--color-action); /* line fills as steps complete */
38}
39
40/* Continuity effect: eye follows the horizontal line through all steps */Continuity in scrolling interfaces:
1/* Infinite scroll: elements flow continuously downward */
2.feed {
3 display: flex;
4 flex-direction: column;
5 /* No gaps between posts = continuous reading flow */
6}
7
8.post:not(:last-child) {
9 border-bottom: 1px solid var(--color-border);
10 /* The divider interrupts continuity just enough to separate posts */
11 /* Without it: all posts flow together — too continuous */
12}
13
14/* Continuity in timeline layouts */
15.timeline {
16 position: relative;
17 padding-left: var(--space-8);
18}
19
20.timeline::before {
21 content: '';
22 position: absolute;
23 left: 0.75rem; /* 12px from left */
24 top: 0;
25 bottom: 0;
26 width: 2px;
27 background-color: var(--color-border);
28 /* Vertical line creates continuity through all timeline items */
29}
30
31.timeline-item {
32 position: relative;
33 padding-bottom: var(--space-6);
34}
35
36.timeline-dot {
37 position: absolute;
38 left: -1.875rem; /* positions dot on the vertical line */
39 top: 0.25rem;
40 width: 0.75rem;
41 height: 0.75rem;
42 background-color: var(--color-action);
43 border-radius: 50%;
44 border: 2px solid white; /* creates 'bead on string' appearance */
45}Closure: the brain completes incomplete shapes. This is why icons with breaks in their outline are still recognizable.
Closure in UI:
1/* Cards without all four borders — Closure completes the shape */
2.card-top-only {
3 border-top: 3px solid var(--color-action);
4 /* No other borders — brain perceives a complete card shape */
5 background-color: white;
6 padding: var(--space-4);
7 box-shadow: 0 1px 3px rgb(0 0 0 / 0.1); /* subtle elevation = closed shape */
8}
9
10/* Three-sided input group — tab closure */
11.input-group {
12 display: flex;
13 border: 1px solid var(--color-border);
14 border-radius: var(--radius-md);
15 overflow: hidden; /* clips children to the shape */
16}
17
18.input-group .input-field {
19 border: none; /* no individual borders — closure provides the boundary */
20 flex: 1;
21 padding: var(--space-3) var(--space-4);
22}
23
24.input-group .input-addon {
25 border-left: 1px solid var(--color-border); /* separator, not a border */
26 background-color: var(--color-bg-surface);
27 padding: var(--space-3) var(--space-4);
28 display: flex;
29 align-items: center;
30}Figure-Ground: the distinction between foreground (figure) and background (ground). Modals, tooltips, and dropdowns work because they establish themselves as foreground against background.
1/* Modal: classic figure-ground with backdrop */
2.modal-backdrop {
3 position: fixed;
4 inset: 0;
5 background-color: rgb(0 0 0 / 0.5); /* ground: dims the background */
6 display: grid;
7 place-items: center;
8}
9
10.modal-panel {
11 background-color: white; /* figure: full opacity = foreground */
12 border-radius: var(--radius-xl);
13 box-shadow: 0 20px 60px rgb(0 0 0 / 0.2); /* elevation = figure above ground */
14 /* User attention: the dimmed background tells the eye to focus on modal */
15}
16
17/* Tooltip: figure on top of page */
18.tooltip {
19 background-color: #1f2937; /* dark figure against lighter page */
20 color: white;
21 border-radius: var(--radius-sm);
22 padding: var(--space-1) var(--space-2);
23 font-size: var(--text-sm);
24 z-index: 1000; /* z-index enforces figure above ground */
25 box-shadow: 0 4px 8px rgb(0 0 0 / 0.15);
26}
27
28/* Dropdown: must establish itself as foreground */
29.dropdown {
30 background-color: white;
31 border: 1px solid var(--color-border);
32 border-radius: var(--radius-lg);
33 box-shadow: 0 8px 24px rgb(0 0 0 / 0.12); /* shadows = elevation = figure */
34 position: absolute;
35 z-index: 100;
36}Gestalt violations produce interfaces that feel 'off' without users being able to say why. Recognizing them and knowing the minimum CSS fix prevents redesigns.
Violation 1: Form labels too far from inputs (Proximity broken)
1/* BAD: Label and input equidistant from each other and from neighboring groups */
2.form { display: flex; flex-direction: column; gap: 1rem; }
3.label { /* same gap to everything above and below */ }
4
5/* FIX: Label closer to input than inputs are to each other */
6.form-group {
7 display: flex;
8 flex-direction: column;
9 gap: var(--space-1); /* 4px label-to-input */
10}
11.form { display: flex; flex-direction: column; gap: var(--space-6); } /* 24px between groups */Violation 2: Different action types look the same (Similarity broken)
1/* BAD: Nav link and primary button look too similar */
2.nav-link { color: #2563eb; font-weight: 500; }
3.btn-primary { color: #2563eb; font-weight: 500; background: transparent; }
4/* User cannot tell which is a link and which is a button */
5
6/* FIX: Clear visual distinction between interactive types */
7.nav-link { color: #374151; font-weight: 400; text-decoration: none; }
8.btn-primary { background: #2563eb; color: white; font-weight: 600; padding: 0.75rem 1.5rem; }
9/* Different shape, color, and weight = clearly different types */Violation 3: Dropdown has no figure-ground separation
1/* BAD: Dropdown on white page, white background, no shadow */
2.dropdown { background: white; border: 1px solid #e5e7eb; }
3/* Looks flat — appears to be part of the page, not above it */
4
5/* FIX: Elevation creates figure status */
6.dropdown {
7 background: white;
8 border: 1px solid var(--color-border);
9 box-shadow: 0 8px 24px rgb(0 0 0 / 0.12), 0 2px 8px rgb(0 0 0 / 0.06);
10 /* Layered shadows = strong elevation = clear figure */
11}Violation 4: Progress steps disconnect (Continuity broken)
1/* BAD: Steps with gap, no connecting line */
2.steps { display: flex; gap: 2rem; } /* gap breaks the visual line */
3
4/* FIX: Connecting line creates continuity */
5.step-connector { flex: 1; height: 2px; background: var(--color-border); }
6/* The continuous horizontal line creates the path eye follows */A checkout step indicator and form section that deliberately applies all five Gestalt principles — proximity, similarity, continuity, closure, and figure-ground.
1/* CHECKOUT FLOW: Five Gestalt principles applied */
2
3/* ====== CONTINUITY: Progress indicator ====== */
4.checkout-steps {
5 display: flex;
6 align-items: center;
7 padding-block: var(--space-6);
8 /* No gaps — continuity flows through all steps */
9}
10
11.step-item {
12 display: flex;
13 align-items: center;
14 flex: 1;
15}
16
17.step-circle {
18 width: 2.25rem;
19 height: 2.25rem;
20 border-radius: 50%;
21 background-color: var(--color-bg-surface);
22 border: 2px solid var(--color-border);
23 display: flex;
24 align-items: center;
25 justify-content: center;
26 font-size: 0.875rem;
27 font-weight: 600;
28 flex-shrink: 0;
29 position: relative;
30 z-index: 1;
31 transition: all var(--transition-base);
32}
33
34/* SIMILARITY: All active steps look identical */
35.step-item.active .step-circle,
36.step-item.completed .step-circle {
37 background-color: var(--color-action); /* same color = same meaning */
38 border-color: var(--color-action);
39 color: white;
40}
41
42.step-line {
43 flex: 1;
44 height: 2px;
45 background-color: var(--color-border);
46 /* Continuity: the line creates an eye path through all steps */
47}
48
49.step-item.completed + .step-item .step-line,
50.step-item.completed .step-line {
51 background-color: var(--color-action); /* filled line = completed path */
52}
53
54/* ====== FIGURE-GROUND: Payment section card ====== */
55.payment-section {
56 background-color: white; /* figure: bright white */
57 border: 1px solid var(--color-border);
58 border-radius: var(--radius-lg);
59 box-shadow:
60 0 1px 3px rgb(0 0 0 / 0.05),
61 0 4px 12px rgb(0 0 0 / 0.08); /* elevation = figure above ground */
62 padding: var(--space-6);
63 margin-top: var(--space-8);
64}
65
66/* ====== CLOSURE: Input groups ====== */
67.card-input-group {
68 display: flex;
69 border: 1px solid var(--color-border);
70 border-radius: var(--radius-md);
71 overflow: hidden; /* closure: clips children to shape */
72}
73
74.card-input-group input {
75 border: none; /* no individual borders — closure provides boundary */
76 flex: 1;
77 padding: var(--space-3) var(--space-4);
78 font-size: var(--text-md);
79 outline: none;
80}
81
82.card-input-group .expiry {
83 border-left: 1px solid var(--color-border);
84 border-right: 1px solid var(--color-border);
85 width: 5rem;
86 text-align: center;
87}
88
89.card-input-group .cvv {
90 width: 4rem;
91 text-align: center;
92}
93
94/* Focus extends to the group border (figure-ground) */
95.card-input-group:focus-within {
96 outline: 2px solid var(--color-action);
97 outline-offset: 2px; /* focus ring around the entire closure shape */
98}
99
100/* ====== PROXIMITY: Form groups ====== */
101.form-row {
102 display: flex;
103 flex-direction: column;
104 gap: var(--space-1); /* 4px: label very close to input (same group) */
105 margin-bottom: var(--space-5); /* 20px: rows far apart (different groups) */
106}
107
108.form-label {
109 font-size: var(--text-sm);
110 font-weight: 500;
111 color: var(--color-text-muted);
112 /* Proximity + Similarity: all labels look identical → user learns 'this is a label' */
113}
114
115.form-input {
116 padding: var(--space-3) var(--space-4);
117 border: 1.5px solid var(--color-border);
118 border-radius: var(--radius-md);
119 font-size: var(--text-md);
120 width: 100%;
121 transition: border-color var(--transition-base), box-shadow var(--transition-base);
122}
123
124/* SIMILARITY: All inputs' focus states are identical */
125.form-input:focus {
126 outline: none;
127 border-color: var(--color-action);
128 box-shadow: 0 0 0 3px rgb(37 99 235 / 0.15);
129 /* Every input responds identically to focus = Similarity enforced */
130}The checkout flow applies all five Gestalt principles deliberately. Continuity: the horizontal line connecting progress steps creates an eye path that guides users forward. Similarity: all active/completed steps share identical visual treatment (blue circle, white number), and all form inputs share identical focus styling. Proximity: label-to-input gap is 4px; input-group-to-input-group gap is 20px — the 5:1 ratio makes groupings immediately clear. Closure: the card input group uses overflow: hidden to clip all inputs into a single unified shape. Figure-Ground: the payment section card uses box-shadow to elevate it above the page, establishing foreground status.
The next lesson covers Design Psychology and User Perception — how cognitive biases, attention mechanisms, and mental models influence how users interpret and respond to interface designs.
Your progress is saved in this browser, on this device only.