Apply the Repetition design principle to create visual cohesion — repeat colors, typography, spacing, border radius, and component patterns consistently to build a design language that users recognize and trust.
Repetition creates cohesion. When the same visual elements — colors, fonts, spacing, border radius, icons — appear consistently throughout an interface, the design feels intentional, polished, and trustworthy. When these elements vary randomly, the design feels inconsistent and unfinished, even if individual elements are well-designed.
For developers, repetition is a CSS architecture problem as much as a design problem. When CSS values are hardcoded rather than expressed as tokens, inconsistencies accumulate. Every time a developer hardcodes border-radius: 6px in one component and border-radius: 8px in another, they break repetition. Design tokens (CSS custom properties) enforce repetition by providing a single source of truth for every repeated value.
Not everything repeats — only the elements that form the visual language of the interface. These are the elements that should appear identically wherever they are used.
Elements that should repeat consistently:
Repetition failures and their CSS causes:
1/* FAILURE: Each component invents its own border radius */
2.button { border-radius: 6px; }
3.card { border-radius: 8px; }
4.input { border-radius: 4px; }
5.badge { border-radius: 999px; }
6.modal { border-radius: 10px; }
7/* 5 different values = no visual language */
8
9/* SUCCESS: Consistent radius scale from design tokens */
10:root {
11 --radius-sm: 0.25rem; /* 4px — badges, chips */
12 --radius-md: 0.5rem; /* 8px — buttons, inputs */
13 --radius-lg: 0.75rem; /* 12px — cards, dropdowns */
14 --radius-xl: 1rem; /* 16px — modals, large panels */
15 --radius-full: 9999px; /* pill shapes */
16}
17
18.button { border-radius: var(--radius-md); }
19.card { border-radius: var(--radius-lg); }
20.input { border-radius: var(--radius-md); }
21.badge { border-radius: var(--radius-full); }
22.modal { border-radius: var(--radius-xl); }A spacing system based on a consistent multiplier (4px or 8px) creates visual rhythm that feels harmonious even to users who cannot articulate why.
Why 4px works:
Four-point spacing scale:
1:root {
2 --space-1: 0.25rem; /* 4px */
3 --space-2: 0.5rem; /* 8px */
4 --space-3: 0.75rem; /* 12px */
5 --space-4: 1rem; /* 16px */
6 --space-5: 1.25rem; /* 20px */
7 --space-6: 1.5rem; /* 24px */
8 --space-8: 2rem; /* 32px */
9 --space-10: 2.5rem; /* 40px */
10 --space-12: 3rem; /* 48px */
11 --space-16: 4rem; /* 64px */
12 --space-20: 5rem; /* 80px */
13 --space-24: 6rem; /* 96px */
14}Mapping spacing scale to component types:
1/* Internal component padding — repeats consistently */
2.badge { padding: var(--space-1) var(--space-2); } /* 4px 8px */
3.btn-sm { padding: var(--space-2) var(--space-3); } /* 8px 12px */
4.btn-md { padding: var(--space-3) var(--space-4); } /* 12px 16px */
5.btn-lg { padding: var(--space-4) var(--space-6); } /* 16px 24px */
6.card { padding: var(--space-4); } /* 16px all sides */
7.modal { padding: var(--space-6); } /* 24px all sides */
8
9/* Component gaps — repeats consistently */
10.card-body { gap: var(--space-2); } /* 8px between card sections */
11.form-group { gap: var(--space-4); } /* 16px between form groups */
12.section { gap: var(--space-12); } /* 48px between page sections */Spacing repetition violations:
1/* BAD: Random spacing values break rhythm */
2.card { padding: 15px; } /* 15px — not on the scale */
3.btn { padding: 10px 18px; } /* 10px and 18px — off scale */
4.section { margin-top: 55px; } /* 55px — not a 4px multiple */
5
6/* GOOD: All values from the 4px scale */
7.card { padding: var(--space-4); } /* 16px */
8.btn { padding: var(--space-3) var(--space-4); } /* 12px 16px */
9.section { margin-top: var(--space-12); } /* 48px */Repeating the same hover, focus, and active patterns across all interactive elements creates a predictable, learnable interface. Users learn the interaction vocabulary once and apply it everywhere.
Consistent hover pattern:
1/* All interactive elements use the SAME hover transition */
2:root {
3 --transition-speed: 150ms;
4 --transition-ease: cubic-bezier(0, 0, 0.2, 1); /* ease-out */
5}
6
7/* Button hover */
8.btn { transition: background-color var(--transition-speed) var(--transition-ease); }
9.btn:hover { background-color: var(--color-action-hover); }
10
11/* Card hover */
12.card { transition: box-shadow var(--transition-speed) var(--transition-ease); }
13.card:hover { box-shadow: 0 8px 24px rgb(0 0 0 / 0.1); }
14
15/* Link hover */
16a { transition: color var(--transition-speed) var(--transition-ease); }
17a:hover { color: var(--color-action-hover); }
18
19/* Same duration and easing everywhere = consistent feel */Consistent focus ring pattern:
1/* REPEAT the exact same focus ring on every interactive element */
2:root {
3 --focus-ring: 2px solid var(--color-border-focus, #2563eb);
4 --focus-offset: 2px;
5}
6
7/* Apply identically to all interactive elements */
8button:focus-visible,
9a:focus-visible,
10input:focus-visible,
11textarea:focus-visible,
12select:focus-visible,
13[tabindex]:focus-visible {
14 outline: var(--focus-ring);
15 outline-offset: var(--focus-offset);
16}Consistent icon usage:
1/* Icons should repeat consistent sizing */
2:root {
3 --icon-sm: 1rem; /* 16px — inline with small text */
4 --icon-md: 1.25rem; /* 20px — default icon size */
5 --icon-lg: 1.5rem; /* 24px — large icons */
6 --icon-xl: 2rem; /* 32px — hero icons */
7}
8
9/* Repeating the same icon style (stroke, not fill) everywhere */
10.icon {
11 width: var(--icon-md);
12 height: var(--icon-md);
13 fill: none;
14 stroke: currentColor;
15 stroke-width: 1.5;
16 stroke-linecap: round;
17 stroke-linejoin: round;
18 flex-shrink: 0;
19}CSS custom properties (design tokens) enforce repetition by making every repeated value come from a single source. When a token changes, all instances update automatically.
Audit workflow for finding repetition violations:
1# Search for hardcoded values that should be tokens
2grep -r 'border-radius: [0-9]' src/styles/ --include='*.css'
3grep -r 'font-size: [0-9]' src/styles/ --include='*.css'
4grep -r '#2563' src/styles/ --include='*.css'
5# Any hardcoded value found here should become a token referenceToken-driven repetition architecture:
1/* SINGLE SOURCE OF TRUTH for every repeated value */
2:root {
3 /* Colors: defined once, used everywhere */
4 --color-action: #2563eb;
5 --color-action-hover: #1d4ed8;
6 --color-success: #16a34a;
7 --color-danger: #dc2626;
8 --color-text-primary: #111827;
9 --color-text-muted: #6b7280;
10 --color-border: #e5e7eb;
11 --color-bg-surface: #f9fafb;
12
13 /* Typography: defined once */
14 --font-body: 'Inter', system-ui, sans-serif;
15 --text-base: 1rem; /* 16px */
16 --text-sm: 0.875rem; /* 14px */
17 --text-lg: 1.125rem; /* 18px */
18
19 /* Border radius: defined once */
20 --radius-sm: 0.25rem;
21 --radius-md: 0.5rem;
22 --radius-lg: 0.75rem;
23
24 /* Transitions: defined once */
25 --transition: 150ms cubic-bezier(0, 0, 0.2, 1);
26}
27
28/* Every component references tokens — never hardcodes */
29.btn-primary {
30 background-color: var(--color-action); /* not #2563eb */
31 border-radius: var(--radius-md); /* not 8px */
32 font-family: var(--font-body); /* not 'Inter' */
33 transition: background-color var(--transition); /* not 150ms ease */
34}
35
36.btn-primary:hover { background-color: var(--color-action-hover); }
37
38/* BENEFIT: Change brand blue from #2563EB to #7C3AED = one line */
39/* :root { --color-action: #7c3aed; } — all components update */Design audit: checking your repetition:
1Pass: Does every primary action use --color-action?
2Pass: Does every border-radius come from the radius scale?
3Pass: Does every spacing value use a spacing token?
4Pass: Does every focus ring use the same 2px outline?
5Fail: Any hardcoded pixel or hex value repeated in multiple filesBuilding a CSS foundation that enforces repetition through design tokens — eliminating 47 hardcoded values from a codebase and replacing them with a consistent token system.
1/* BEFORE: Inconsistent — 47 different hardcoded values */
2/* Every component invented its own values */
3
4.lesson-card { border-radius: 6px; }
5.course-card { border-radius: 8px; }
6.quiz-card { border-radius: 10px; }
7.profile-card { border-radius: 12px; }
8.modal { border-radius: 14px; }
9
10.btn-primary { background: #2563EB; transition: 0.2s; }
11.btn-secondary { border-radius: 6px; transition: 0.15s ease; }
12.link { transition: color 0.1s; }
13.card { transition: all 0.2s ease-in-out; }
14
15.form-group { gap: 15px; }
16.card-body { padding: 18px; }
17.section { padding: 55px 0; }
18
19/* AFTER: Token-enforced repetition */
20
21/* 1. Define the design language (tokens) */
22:root {
23 /* Border radius scale: 4 sizes only */
24 --radius-sm: 0.25rem; /* 4px — chips, badges */
25 --radius-md: 0.5rem; /* 8px — buttons, inputs, small cards */
26 --radius-lg: 0.75rem; /* 12px — cards, dropdowns */
27 --radius-xl: 1rem; /* 16px — modals, sidebars */
28 --radius-full: 9999px; /* pills, circular avatars */
29
30 /* Single transition for ALL interactive elements */
31 --transition-fast: 100ms cubic-bezier(0, 0, 0.2, 1);
32 --transition-base: 150ms cubic-bezier(0, 0, 0.2, 1);
33 --transition-slow: 300ms cubic-bezier(0, 0, 0.2, 1);
34
35 /* Spacing: 4px multiples only */
36 --space-1: 0.25rem; /* 4px */
37 --space-2: 0.5rem; /* 8px */
38 --space-3: 0.75rem; /* 12px */
39 --space-4: 1rem; /* 16px */
40 --space-6: 1.5rem; /* 24px */
41 --space-8: 2rem; /* 32px */
42 --space-12: 3rem; /* 48px */
43
44 /* Colors */
45 --color-action: #2563eb;
46 --color-action-hover: #1d4ed8;
47 --color-text: #111827;
48 --color-text-muted: #6b7280;
49 --color-border: #e5e7eb;
50 --color-bg: #f9fafb;
51}
52
53/* 2. All components reference tokens — zero hardcoded values */
54
55.lesson-card { border-radius: var(--radius-md); } /* 8px */
56.course-card { border-radius: var(--radius-lg); } /* 12px */
57.quiz-card { border-radius: var(--radius-lg); } /* 12px */
58.profile-card { border-radius: var(--radius-lg); } /* 12px */
59.modal { border-radius: var(--radius-xl); } /* 16px */
60/* 5 values → 3 (md, lg, xl) — deliberate, consistent */
61
62.btn-primary { transition: background-color var(--transition-base); }
63.btn-secondary { transition: background-color var(--transition-base),
64 border-color var(--transition-base); }
65.link { transition: color var(--transition-base); }
66.card { transition: box-shadow var(--transition-base); }
67/* Same duration and easing everywhere */
68
69.form-group { gap: var(--space-4); } /* 16px — on scale */
70.card-body { padding: var(--space-4); } /* 16px — on scale */
71.section { padding: var(--space-12) 0; } /* 48px — on scale */
72
73/* 3. Consistent interactive patterns */
74:is(button, a, [tabindex='0']):focus-visible {
75 outline: 2px solid var(--color-action);
76 outline-offset: 2px;
77 /* Exactly the same on every interactive element */
78}The token-driven system reduces 47 hardcoded values to a defined set of tokens. The border radius went from 5 different values (6, 8, 10, 12, 14px) to 3 intentional sizes (sm/md/lg/xl). Transitions now use identical duration and easing everywhere — the interface responds with the same 'speed' across all interactions. The spacing scale eliminates 15px, 18px, and 55px in favor of 16px, 16px, and 48px — values that are on the 4px scale and feel harmonious with each other.
The next lesson covers Alignment and Proximity — the remaining two CRAP principles that control spatial relationships and grid structure in interfaces.
Your progress is saved in this browser, on this device only.