@3xhaust/oh-my-design 0.16.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.ko.md +282 -0
- package/README.md +282 -0
- package/adapters/build.ts +184 -0
- package/adapters/claude.ts +111 -0
- package/adapters/codex.ts +68 -0
- package/adapters/tokens.ts +21 -0
- package/adapters/tool-map.json +7 -0
- package/bin/omd-install.ts +76 -0
- package/bin/omd.ts +1774 -0
- package/core/asset-sourcing/index.ts +187 -0
- package/core/coach/index.ts +98 -0
- package/core/composition/app-shell-workbench.md +168 -0
- package/core/composition/asymmetric-diagonal-grid.md +178 -0
- package/core/composition/bento-grid.md +223 -0
- package/core/composition/editorial-index-labels.md +173 -0
- package/core/composition/form-wizard-stepper.md +132 -0
- package/core/composition/master-detail-flow.md +129 -0
- package/core/composition/section-inversion.md +182 -0
- package/core/composition/sidebar-margin-annotation.md +184 -0
- package/core/composition/split-screen-hero.md +224 -0
- package/core/composition/sticky-sidebar-scroll.md +219 -0
- package/core/composition/typographic-hero.md +100 -0
- package/core/composition-contract/index.ts +233 -0
- package/core/composition-contract/visual-richness.ts +101 -0
- package/core/config/index.ts +27 -0
- package/core/copy/index.ts +296 -0
- package/core/craft/finish-pass.md +356 -0
- package/core/craft/index.ts +33 -0
- package/core/design/index.ts +581 -0
- package/core/design/interaction-states.ts +189 -0
- package/core/eval-harness/index.ts +225 -0
- package/core/evidence/final.ts +444 -0
- package/core/evidence/task.ts +459 -0
- package/core/figma/client.ts +312 -0
- package/core/figma/diff.ts +261 -0
- package/core/figma/responsive.ts +310 -0
- package/core/figma/system.ts +466 -0
- package/core/figma/types.ts +185 -0
- package/core/frame/check-ux.ts +180 -0
- package/core/frame/index.ts +39 -0
- package/core/frame/write.ts +168 -0
- package/core/graphics/css-illustration-primitives.md +189 -0
- package/core/graphics/duotone-image-presets.md +173 -0
- package/core/graphics/gradient-mesh.md +178 -0
- package/core/graphics/noise-grain-texture.md +150 -0
- package/core/graphics/placeholder-policy.md +185 -0
- package/core/graphics/svg-geometric-patterns.md +145 -0
- package/core/history/index.ts +39 -0
- package/core/install/detect.ts +32 -0
- package/core/install/install.ts +321 -0
- package/core/install/patch-claude.ts +109 -0
- package/core/install/patch-codex.ts +82 -0
- package/core/interaction/index.ts +101 -0
- package/core/interaction/recipes/signature-lighting.md +203 -0
- package/core/ir/dom.ts +352 -0
- package/core/ir/normalize.ts +177 -0
- package/core/motion/easing.md +151 -0
- package/core/motion/energy.ts +188 -0
- package/core/motion/recipes/image-hover-distortion.md +198 -0
- package/core/motion/recipes/magnetic-hover.md +229 -0
- package/core/motion/recipes/marquee.md +202 -0
- package/core/motion/recipes/number-counter.md +242 -0
- package/core/motion/recipes/page-loader.md +240 -0
- package/core/motion/recipes/parallax.md +233 -0
- package/core/motion/recipes/scroll-reveal.md +210 -0
- package/core/motion/recipes/section-color-inversion.md +217 -0
- package/core/motion/recipes/split-text-entrance.md +222 -0
- package/core/motion/recipes/stagger-orchestrator.md +227 -0
- package/core/motion/recipes/sticky-scene-transition.md +246 -0
- package/core/motion/recipes/view-transitions.md +239 -0
- package/core/probe/index.ts +186 -0
- package/core/probe/schema.json +46 -0
- package/core/protocol/composition-contract.md +192 -0
- package/core/protocol/copy-deck.md +96 -0
- package/core/protocol/human-design-loop.md +447 -0
- package/core/protocol/slop-review.md +71 -0
- package/core/ref/batch.ts +94 -0
- package/core/ref/blueprint.ts +199 -0
- package/core/ref/distance.ts +225 -0
- package/core/ref/invariants.ts +162 -0
- package/core/ref/signal.ts +37 -0
- package/core/ref/store.ts +122 -0
- package/core/render/index.ts +607 -0
- package/core/rules/attribution.ts +159 -0
- package/core/rules/builtin/contrast.yaml +8 -0
- package/core/rules/builtin/focus.yaml +8 -0
- package/core/rules/builtin/hit-area.yaml +8 -0
- package/core/rules/builtin/ko.yaml +6 -0
- package/core/rules/builtin/motion.yaml +80 -0
- package/core/rules/builtin/slop.yaml +365 -0
- package/core/rules/builtin/spacing.yaml +9 -0
- package/core/rules/builtin/system.yaml +42 -0
- package/core/rules/builtin/token.yaml +8 -0
- package/core/rules/builtin/ux.yaml +125 -0
- package/core/rules/engine.ts +88 -0
- package/core/rules/leakage.ts +72 -0
- package/core/rules/motion-spec.ts +204 -0
- package/core/site/index.ts +100 -0
- package/core/slop/index.ts +427 -0
- package/core/slop/text-slop.ts +220 -0
- package/core/source-seal/index.ts +174 -0
- package/core/stack/index.ts +53 -0
- package/core/target/index.ts +183 -0
- package/core/theory/color.md +285 -0
- package/core/theory/components.md +272 -0
- package/core/theory/craft.md +270 -0
- package/core/theory/expressive.md +213 -0
- package/core/theory/imagegen.md +105 -0
- package/core/theory/layout.md +159 -0
- package/core/theory/motion.md +283 -0
- package/core/theory/typography.md +134 -0
- package/core/theory/ux.md +580 -0
- package/core/theory/voice.md +595 -0
- package/core/types.ts +684 -0
- package/package.json +40 -0
- package/src/agents/composer.agent.yaml +174 -0
- package/src/agents/eye.agent.yaml +208 -0
- package/src/agents/framer.agent.yaml +52 -0
- package/src/agents/glance.agent.yaml +19 -0
- package/src/agents/hand.agent.yaml +200 -0
- package/src/agents/scout.agent.yaml +71 -0
- package/src/agents/sketch.agent.yaml +64 -0
- package/src/agents/typesetter.agent.yaml +52 -0
- package/src/agents/writer.agent.yaml +63 -0
- package/src/skills/omd-coach/SKILL.md +40 -0
- package/src/skills/omd-critique/SKILL.md +77 -0
- package/src/skills/omd-figma/SKILL.md +256 -0
- package/src/skills/omd-humanize/SKILL.md +160 -0
- package/src/skills/omd-scout/SKILL.md +77 -0
- package/src/skills/omd-ultradesign/SKILL.md +521 -0
|
@@ -0,0 +1,285 @@
|
|
|
1
|
+
# Colour — decision material
|
|
2
|
+
|
|
3
|
+
The question "what colour should this be" is not answered by preference. It is answered by
|
|
4
|
+
the domain the product sits in, the register the concept demands, and what the colour will
|
|
5
|
+
be required to do. Preference is the noise; these are the signal.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Domain conventions and why they exist
|
|
10
|
+
|
|
11
|
+
Colour conventions are not arbitrary. They are residues of what worked — or what users
|
|
12
|
+
learned to expect — in each industry. Violating them requires a reason proportional to the
|
|
13
|
+
cost of breaking the expectation.
|
|
14
|
+
|
|
15
|
+
**Fintech and banking.** Blue, deployed consistently since the 1980s, carries a specific
|
|
16
|
+
claim: trustworthiness and institutional stability. Elliot & Maier (2014,
|
|
17
|
+
*Color-in-Context*) map blue to cognitive associations with calm, reliability, and
|
|
18
|
+
competence — properties banking literally sells. Navy amplifies authority; desaturated
|
|
19
|
+
mid-blue reads as honest and measured; bright cobalt reads as technology-forward. If your
|
|
20
|
+
fintech product is positioning *against* legacy banking (neobanks, crypto wallets), the
|
|
21
|
+
blue is exactly what you reject — but you are then making a claim and you must support it.
|
|
22
|
+
Breaking the convention is valid. Breaking it without knowing you broke it is not.
|
|
23
|
+
|
|
24
|
+
**Healthcare.** Green and white. White signals cleanliness — a hospital ceiling rather than
|
|
25
|
+
a brand choice, but one that research has made load-bearing. Green is associated with
|
|
26
|
+
healing and growth in Western contexts (Itten, *The Art of Color*, 1961). The danger zone
|
|
27
|
+
is red: Elliot & Maier's color-in-context research documents a reliable anxiety response to
|
|
28
|
+
red in evaluative contexts — patients waiting for results are already in an elevated state.
|
|
29
|
+
High-saturation red raises it further. Red is reserved for confirmed errors only; everything
|
|
30
|
+
else uses softer treatment.
|
|
31
|
+
|
|
32
|
+
**Education.** Warm palettes — orange, amber, warm yellow — encode approachability and
|
|
33
|
+
curiosity. IBM Design Language classifies warm hues as "energetic and optimistic," which
|
|
34
|
+
is correct for learning contexts where the interaction cost is effort. Blue appears in
|
|
35
|
+
formal educational contexts (universities, certifications) where authority is the value
|
|
36
|
+
being sold.
|
|
37
|
+
|
|
38
|
+
**Food and beverage.** Red and yellow reliably increase appetite and urgency — McDonald's
|
|
39
|
+
is not branding folklore, it is a tested outcome. Brown and warm earth tones signal
|
|
40
|
+
naturalness and craft (artisan bread, coffee, fermented anything). Green signals health and
|
|
41
|
+
freshness, especially in plant-based or organic positioning. Cold blues and purples suppress
|
|
42
|
+
appetite; avoid them unless the product is a diet tool and suppression is the point.
|
|
43
|
+
|
|
44
|
+
**Developer tools.** Dark backgrounds with green or blue accent are so established they
|
|
45
|
+
read as genre, not style. If you are building a developer tool, dark mode is not an
|
|
46
|
+
aesthetic option — it is the expected register. Material Design classifies this as a "dark
|
|
47
|
+
theme" preference, not a style preference, for productivity contexts. Deviating succeeds
|
|
48
|
+
only when the product is deliberately positioned at non-technical users who happen to use
|
|
49
|
+
developer tooling (Vercel's white marketing site, Linear's light mode). Colour the UI dark
|
|
50
|
+
and the marketing light.
|
|
51
|
+
|
|
52
|
+
**Luxury.** Black, deep navy, warm off-white, metallics. High contrast without brightness.
|
|
53
|
+
The signal is restraint: a luxury product does not shout. IBM Design Language codes the
|
|
54
|
+
"premium" register as low saturation, high value contrast, accents used sparingly. Any
|
|
55
|
+
saturated colour in a luxury context reads as a mistake unless it is the brand's exact
|
|
56
|
+
signature colour.
|
|
57
|
+
|
|
58
|
+
---
|
|
59
|
+
|
|
60
|
+
## Harmony schemes: when to use each
|
|
61
|
+
|
|
62
|
+
These are not visual preferences. Each scheme produces a different relationship between
|
|
63
|
+
elements, and the wrong scheme undermines the concept regardless of the individual colours.
|
|
64
|
+
|
|
65
|
+
**Complementary** (opposite on the hue wheel — blue/orange, red/green, purple/yellow):
|
|
66
|
+
high tension, high contrast, high energy. Use when the concept demands urgency or power.
|
|
67
|
+
Use for a single CTA accent against a neutral ground — never for large fields of both
|
|
68
|
+
colours simultaneously, which produces visual vibration that is measurably harder to read.
|
|
69
|
+
|
|
70
|
+
**Analogous** (adjacent hues — blue/blue-green/green): low tension, harmonious, cohesive.
|
|
71
|
+
Use when the concept demands calm, trust, or continuity. Fintech blue into teal is
|
|
72
|
+
analogous; its visual cohesion is the point, not the accident.
|
|
73
|
+
|
|
74
|
+
**Triadic** (three equally spaced hues): balanced energy, more complex than analogous but
|
|
75
|
+
less aggressive than complementary. Use when the product has genuinely distinct feature
|
|
76
|
+
areas that benefit from colour differentiation. Hard to execute; requires one dominant, one
|
|
77
|
+
secondary, one accent — not three equals competing for attention.
|
|
78
|
+
|
|
79
|
+
---
|
|
80
|
+
|
|
81
|
+
## The 60-30-10 distribution
|
|
82
|
+
|
|
83
|
+
60% dominant (backgrounds, large surfaces), 30% secondary (components, containers), 10%
|
|
84
|
+
accent (CTAs, active states, critical notifications). This is not aesthetic orthodoxy — it
|
|
85
|
+
is a reading law. Eye-tracking research (NN/g, "Visual Hierarchy and Attention") shows that
|
|
86
|
+
equal colour distribution eliminates hierarchy: the eye cannot rank what weighs the same.
|
|
87
|
+
The 60-30-10 split creates a reading order by making some things obviously heavier.
|
|
88
|
+
|
|
89
|
+
Invert it and you get AI slop: the accent colour used for backgrounds, the dominant colour
|
|
90
|
+
used for highlights. Everything reads as equally important, so nothing is important.
|
|
91
|
+
|
|
92
|
+
---
|
|
93
|
+
|
|
94
|
+
## Saturation and register
|
|
95
|
+
|
|
96
|
+
Saturation is a signal about urgency and authority. This relationship is consistent across
|
|
97
|
+
Itten's original colour theory and validated in Material Design's colour system (2023):
|
|
98
|
+
|
|
99
|
+
**High saturation** reads as urgent, energetic, playful. Appropriate for: alerts, primary
|
|
100
|
+
CTAs, food brands, entertainment, consumer apps targeting younger users. Inappropriate for:
|
|
101
|
+
banking, legal software, health monitoring, anything where the user is already in an
|
|
102
|
+
elevated state. Anxiety does not need more stimulation.
|
|
103
|
+
|
|
104
|
+
**Mid saturation** reads as friendly and accessible without aggression. The safe register
|
|
105
|
+
for most consumer products — present without shouting.
|
|
106
|
+
|
|
107
|
+
**Low saturation (muted)** reads as authoritative, mature, premium. Appropriate for:
|
|
108
|
+
enterprise tools, luxury brands, healthcare, editorial products. IBM Design Language
|
|
109
|
+
explicitly codes low-saturation palettes as "professional" and reserves high saturation for
|
|
110
|
+
interactive states only.
|
|
111
|
+
|
|
112
|
+
A colour decision without a saturation decision is half a decision. The hue is the genre;
|
|
113
|
+
the saturation is the register within that genre. Choosing green for a healthcare product
|
|
114
|
+
is a start; choosing muted sage green instead of saturated lime is the actual decision.
|
|
115
|
+
|
|
116
|
+
---
|
|
117
|
+
|
|
118
|
+
## Background temperature
|
|
119
|
+
|
|
120
|
+
Backgrounds are not neutral. A warm background (slightly yellow, cream, warm grey) creates
|
|
121
|
+
a different psychological environment than a cool one (blue-grey, neutral-white, near-black).
|
|
122
|
+
|
|
123
|
+
**Warm backgrounds**: approachable, organic, human. Appropriate for products where
|
|
124
|
+
connection and warmth are the value proposition — communication tools, creative tools,
|
|
125
|
+
consumer apps. The warmth carries even at very low saturation; a barely-tinted cream reads
|
|
126
|
+
differently from a cool white.
|
|
127
|
+
|
|
128
|
+
**Cool backgrounds**: crisp, precise, efficient. Appropriate for productivity tools,
|
|
129
|
+
fintech, developer tools. Signals that the environment is here to help you work, not to be
|
|
130
|
+
pleasant. Cool surfaces say "focus."
|
|
131
|
+
|
|
132
|
+
**True neutral** (pure white or near-black): no temperature signal. Appropriate when the
|
|
133
|
+
content itself must carry all the temperature — photography sites, portfolio tools, editorial
|
|
134
|
+
platforms. The absence of warmth or coolness is deliberate.
|
|
135
|
+
|
|
136
|
+
The background temperature sets the emotional register for every colour that sits on it.
|
|
137
|
+
A warm accent on a cool background reads as a guest; the same accent on a warm background
|
|
138
|
+
reads as native. Choose the temperature before choosing the accent, not after.
|
|
139
|
+
|
|
140
|
+
---
|
|
141
|
+
|
|
142
|
+
## Dark mode: the rules for colour adjustment
|
|
143
|
+
|
|
144
|
+
Dark mode is not "light mode inverted." It is a different palette built on different
|
|
145
|
+
physics. The three most common errors:
|
|
146
|
+
|
|
147
|
+
**Pure black (#000000) is the wrong dark background.** On OLED panels, pure black pixels
|
|
148
|
+
are off — the transition between lit and unlit pixels as you scroll produces a strobing
|
|
149
|
+
effect that users feel as discomfort without being able to name. More importantly, pure
|
|
150
|
+
black gives zero depth: every surface is on the same plane, so the elevation system
|
|
151
|
+
(cards, modals, tooltips) collapses. Google's Material Design dark theme specification uses
|
|
152
|
+
#121212 as the baseline surface; layers of elevation are expressed as white-tint overlays
|
|
153
|
+
at increasing opacity (4%, 8%, 12%, 16%, 24%). This preserves the depth system without
|
|
154
|
+
fighting the contrast requirements.
|
|
155
|
+
|
|
156
|
+
**Saturated accent colours vibrate on dark backgrounds.** A brand colour calibrated for
|
|
157
|
+
a white ground carries too much energy when placed over dark grey — the eye perceives it as
|
|
158
|
+
louder than intended, a phenomenon called simultaneous contrast. The fix is desaturation:
|
|
159
|
+
reduce the accent's saturation by 20–30% for the dark palette. The colour reads as the same
|
|
160
|
+
brand colour; it no longer shouts. Uxcel's dark mode guidelines and Material Design 3's
|
|
161
|
+
tonal palette system both prescribe this adjustment explicitly.
|
|
162
|
+
|
|
163
|
+
**Text hierarchy uses opacity, not grey values.** On dark surfaces, secondary text is not
|
|
164
|
+
a specific grey hex — it is white at reduced opacity. Material Design's dark theme
|
|
165
|
+
recommendation: primary text at 87% opacity, secondary text at 60%, disabled/hint text at
|
|
166
|
+
38%. The reason is practical: the surface colour changes across the elevation system, and
|
|
167
|
+
fixed hex values for text will fail the contrast requirements on the wrong surface. Opacity
|
|
168
|
+
scales with whatever it sits on; a fixed grey does not.
|
|
169
|
+
|
|
170
|
+
Condition → choice → reason: when the background lightness is below L\* 25 (very dark),
|
|
171
|
+
desaturate all chromatic accents and express text hierarchy through white-opacity tiers,
|
|
172
|
+
not through separate grey values.
|
|
173
|
+
|
|
174
|
+
---
|
|
175
|
+
|
|
176
|
+
## Accessibility contrast: APCA vs WCAG 2
|
|
177
|
+
|
|
178
|
+
The WCAG 2.x contrast ratio (minimum 4.5:1 for body text, 3:1 for large text) has been
|
|
179
|
+
the accessibility floor since 2008. It has a fundamental flaw: it computes contrast as a
|
|
180
|
+
static ratio between two colours, and treats a light-weight 12px label identically to a
|
|
181
|
+
bold 24px heading at the same colour values. The same contrast ratio on different text
|
|
182
|
+
produces radically different readability outcomes.
|
|
183
|
+
|
|
184
|
+
The Advanced Perceptual Contrast Algorithm (APCA), developed by Andrew Somers, corrects
|
|
185
|
+
this. APCA calculates a lightness contrast value (Lc) that accounts for font size and
|
|
186
|
+
weight simultaneously — a thin 14px label and a bold 32px heading require different
|
|
187
|
+
minimum contrast scores. The model is based on human visual perception of spatial frequency
|
|
188
|
+
and luminance, not arithmetic ratios. The W3C has confirmed APCA as the contrast model for
|
|
189
|
+
the forthcoming WCAG 3.0 standard.
|
|
190
|
+
|
|
191
|
+
The practical consequence today: WCAG 2.x compliance is required for legal accessibility
|
|
192
|
+
standards in most jurisdictions — it is the floor. APCA thinking is the ceiling: use it to
|
|
193
|
+
catch situations where a WCAG 2.x-passing colour combination still fails at small sizes or
|
|
194
|
+
light weights. A muted secondary label in a fintech dashboard might pass 4.5:1 against its
|
|
195
|
+
background while being genuinely difficult to read at 11px regular weight. APCA flags it;
|
|
196
|
+
WCAG 2.x does not.
|
|
197
|
+
|
|
198
|
+
Condition → choice → reason: when the design includes small text (below 16px) or thin
|
|
199
|
+
weights (300–400) in informational roles, verify not just the WCAG 2.x ratio but the Lc
|
|
200
|
+
score under APCA. The APCA Contrast Checker tool at git.apcacontrast.com provides the
|
|
201
|
+
calculation. Do not stop at compliance; stop at legibility.
|
|
202
|
+
|
|
203
|
+
---
|
|
204
|
+
|
|
205
|
+
## Cultural colour conventions: East Asian and Korean markets
|
|
206
|
+
|
|
207
|
+
Western colour psychology does not travel without adjustment. The two most commercially
|
|
208
|
+
significant divergences for Korean and East Asian markets:
|
|
209
|
+
|
|
210
|
+
**Red signals prosperity, not danger.** In Western UI convention, red is reserved for
|
|
211
|
+
errors, alerts, and destructive actions — a reliable association built on traffic signals,
|
|
212
|
+
fire, and blood. In Korean and broader East Asian contexts, red historically signals good
|
|
213
|
+
fortune, celebration, and vitality. Red envelopes (홍보봉투) carry money at celebrations;
|
|
214
|
+
red text appears in festive and auspicious contexts. A Korean e-commerce product using red
|
|
215
|
+
for a sale badge is following cultural convention, not violating the error-colour rule. The
|
|
216
|
+
consequence for product design: the error-state semantic for red must be established
|
|
217
|
+
explicitly through context (icon, label, placement) when designing for Korean audiences,
|
|
218
|
+
not assumed from the colour alone.
|
|
219
|
+
|
|
220
|
+
**White carries mourning associations.** In Western contexts, white is sterile, clean,
|
|
221
|
+
minimal. In traditional Korean and East Asian mourning customs, white is the colour of
|
|
222
|
+
death and funerals. Contemporary Korean design has largely absorbed the global white-equals-
|
|
223
|
+
clean convention for digital products, but white-dominant aesthetics in healthcare or
|
|
224
|
+
financial products touching elderly Korean users carry a risk of misread that does not
|
|
225
|
+
exist in Western markets. A warm off-white or a cream-tinted background removes the
|
|
226
|
+
ambiguity without sacrificing the clean register.
|
|
227
|
+
|
|
228
|
+
**Gold and yellow signal prestige.** Where luxury Western products typically reach for
|
|
229
|
+
black, navy, and restraint, East Asian luxury context extends to gold as a prestige signal
|
|
230
|
+
— associated with royalty, success, and premium quality across Korean, Chinese, and
|
|
231
|
+
Japanese markets. This legitimises gold accents in premium Korean product design in ways
|
|
232
|
+
that would read as garish in a Western luxury context.
|
|
233
|
+
|
|
234
|
+
Condition → choice → reason: when the primary market is Korean or East Asian, audit every
|
|
235
|
+
colour's semantic layer against the target culture's conventions before treating Western
|
|
236
|
+
colour psychology as ground truth.
|
|
237
|
+
|
|
238
|
+
---
|
|
239
|
+
|
|
240
|
+
## Data visualisation palettes vs UI palettes
|
|
241
|
+
|
|
242
|
+
These are different systems serving different purposes, and conflating them produces both
|
|
243
|
+
ugly charts and a broken UI colour system.
|
|
244
|
+
|
|
245
|
+
**UI palettes** are built for hierarchy. The accent is one colour, used at one saturation,
|
|
246
|
+
in one role. Introducing six distinct categorical colours into a UI palette creates visual
|
|
247
|
+
chaos: the eye cannot rank what cannot be compared. UI palettes need three to five tones
|
|
248
|
+
from one or two hue families, and one semantically loaded accent.
|
|
249
|
+
|
|
250
|
+
**Data visualisation palettes** are built for distinction. The primary requirement is that
|
|
251
|
+
every category is unambiguously different from every other under multiple viewing conditions
|
|
252
|
+
— colour blindness, screen glare, printout. The standard approach (Color Brewer, a research
|
|
253
|
+
project by Cynthia Brewer at Penn State) provides tested categorical palettes of 3–12
|
|
254
|
+
colours calibrated for perceptual distance. These palettes are deliberately non-hierarchical:
|
|
255
|
+
no one colour should read as more important than another in a categorical context.
|
|
256
|
+
|
|
257
|
+
The failure mode is using the UI brand colour as the first category colour in a chart and
|
|
258
|
+
the UI secondary as the second. The user reads the first bar as "primary action" and the
|
|
259
|
+
second bar as "secondary action" — a UI reading that corrupts the data reading. Separate
|
|
260
|
+
the systems. The chart's categorical blue is not the button's blue; they are different
|
|
261
|
+
tokens with different semantics.
|
|
262
|
+
|
|
263
|
+
Condition → choice → reason: when the product includes data visualisation, create and
|
|
264
|
+
maintain a separate visualisation token set. Borrow nothing from the UI palette except the
|
|
265
|
+
neutral surfaces and the background.
|
|
266
|
+
|
|
267
|
+
---
|
|
268
|
+
|
|
269
|
+
## Sources
|
|
270
|
+
|
|
271
|
+
- Itten, *The Art of Color* (1961) — hue relationships and the psychological force of colour
|
|
272
|
+
- Elliot & Maier, "Color-in-Context Theory" (2014) — empirical mapping of colour to
|
|
273
|
+
psychological state in evaluative contexts
|
|
274
|
+
- IBM Design Language (2023) — enterprise colour system and saturation register
|
|
275
|
+
classification
|
|
276
|
+
- Material Design 3 (2023) — dynamic colour system, role definitions, tonal palettes, dark
|
|
277
|
+
theme surface specification (#121212 baseline, white overlay elevation)
|
|
278
|
+
- NN/g, "Visual Hierarchy and Attention" — eye-tracking evidence for the reading-order
|
|
279
|
+
effect of colour weight distribution
|
|
280
|
+
- Uxcel, "12 Principles of Dark Mode Design" (2023) — desaturation guidelines for accent
|
|
281
|
+
colours on dark surfaces
|
|
282
|
+
- APCA / Andrew Somers, git.apcacontrast.com — Advanced Perceptual Contrast Algorithm;
|
|
283
|
+
the contrast model for forthcoming WCAG 3.0
|
|
284
|
+
- Brewer, Color Brewer (colorbrewer2.org) — perceptually calibrated categorical palettes
|
|
285
|
+
for data visualisation
|
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
# Components — decision material
|
|
2
|
+
|
|
3
|
+
A component is not a widget. It is the materialisation of a decision about what a
|
|
4
|
+
particular kind of interaction should feel like. The button is not a rectangle with a
|
|
5
|
+
label — it is a commitment about hierarchy, risk level, and action weight. Get the
|
|
6
|
+
component wrong and you get the interaction wrong regardless of how well everything else
|
|
7
|
+
was designed.
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## Buttons: hierarchy and the destructive case
|
|
12
|
+
|
|
13
|
+
Buttons communicate hierarchy before the label is read. The visual treatment — fill, stroke,
|
|
14
|
+
or text — signals importance. The order is not convention; it is a reading rule.
|
|
15
|
+
|
|
16
|
+
**Primary (filled)**: the most important action on the current view. There should be at
|
|
17
|
+
most one primary button per context. Two filled buttons of equal visual weight cancel each
|
|
18
|
+
other — the user cannot tell which to press, which is the same as not telling them at all.
|
|
19
|
+
Material Design and Carbon Design System both specify single primary-per-view as an
|
|
20
|
+
explicit constraint, not a preference.
|
|
21
|
+
|
|
22
|
+
**Secondary (outlined or tonal)**: an alternative action — not less important in absolute
|
|
23
|
+
terms, but less important than the primary in this context. A "Save Draft" alongside a
|
|
24
|
+
"Publish" is a secondary. An "Export" alongside a "Save" is a secondary. The outline
|
|
25
|
+
signals presence without competing with the fill for attention.
|
|
26
|
+
|
|
27
|
+
**Tertiary (text or ghost)**: low-stakes or escape actions. "Cancel," "Back," "Skip for
|
|
28
|
+
now" — actions the user needs available but should not be nudged toward. The absence of
|
|
29
|
+
visual container makes these recede from attention deliberately.
|
|
30
|
+
|
|
31
|
+
**Three tiers is the ceiling.** Four button types on one screen is four sentences spoken
|
|
32
|
+
simultaneously. Helios Design System (HashiCorp) and most mature design systems cap
|
|
33
|
+
hierarchy at three tiers. If the design requires a fourth tier, the problem is not a
|
|
34
|
+
missing button variant — it is too many actions on one view.
|
|
35
|
+
|
|
36
|
+
**Destructive actions sit outside the hierarchy and require explicit treatment.** A
|
|
37
|
+
"Delete," "Cancel Subscription," "Remove Account" action should never be styled as a
|
|
38
|
+
primary button unless deletion is the only thing the current view exists to do. The
|
|
39
|
+
destructive variant — typically a red or amber filled button — communicates risk at a
|
|
40
|
+
glance. Position destructive buttons away from the primary CTA: the spatial separation is
|
|
41
|
+
the last friction before an irreversible action. The Carbon Design System specifies a
|
|
42
|
+
"danger" semantic variant with its own colour role precisely because the danger cannot be
|
|
43
|
+
communicated by label alone — a user scanning quickly will press the large filled button.
|
|
44
|
+
If that button is "Delete," the fill is a trap.
|
|
45
|
+
|
|
46
|
+
Condition → choice → reason: when a view contains a destructive action, create a dedicated
|
|
47
|
+
destructive variant (never reuse primary styling), position it away from the primary CTA,
|
|
48
|
+
and consider a confirmation step for irreversible actions.
|
|
49
|
+
|
|
50
|
+
---
|
|
51
|
+
|
|
52
|
+
## Form inputs: validation timing
|
|
53
|
+
|
|
54
|
+
Inline validation — showing error or success state as the user types — changes
|
|
55
|
+
completion rates, but timing matters more than presence.
|
|
56
|
+
|
|
57
|
+
Validating on each keystroke is the worst option. The field fails before the user has
|
|
58
|
+
finished typing the correct answer. An email address field that flags an error after "user"
|
|
59
|
+
(before "@domain.com" is typed) teaches the user that the form is hostile. The error state
|
|
60
|
+
is wrong, and the user did nothing wrong.
|
|
61
|
+
|
|
62
|
+
Validating on blur (when the user leaves the field) is the standard and usually correct
|
|
63
|
+
timing. The user has completed their answer; now the system responds. Errors are surfaced
|
|
64
|
+
at the moment they can be acted upon, without interrupting the act of entering them.
|
|
65
|
+
|
|
66
|
+
Validating only on submit is appropriate for short forms (2–3 fields) where the
|
|
67
|
+
relationship between fields matters (password and password-confirmation must match). It is
|
|
68
|
+
wrong for long forms — an error at the top of a 20-field form that only surfaces on submit
|
|
69
|
+
sends the user back to the beginning.
|
|
70
|
+
|
|
71
|
+
The Baymard Institute's form usability research (baymard.com, "Avoid Extensive Multi-column
|
|
72
|
+
Layouts") documents the failure mode: inline validation errors that fire too early increase
|
|
73
|
+
form abandonment rates because users interpret them as the form rejecting valid input.
|
|
74
|
+
|
|
75
|
+
Condition → choice → reason: validate on blur for single-field validation; validate on
|
|
76
|
+
submit when validation requires cross-field data; never validate on keystroke unless the
|
|
77
|
+
field has a live counter (character limit, password strength meter) where the feedback is
|
|
78
|
+
progress information rather than error state.
|
|
79
|
+
|
|
80
|
+
---
|
|
81
|
+
|
|
82
|
+
## Navigation: Miller's Law, misused
|
|
83
|
+
|
|
84
|
+
Miller's Law — "The Magical Number Seven, Plus or Minus Two" (Miller, 1956, Psychological
|
|
85
|
+
Review) — established that working memory can hold approximately 7 items at once. It has
|
|
86
|
+
been misused as a design rule for navigation: "keep navigation under 7 items because of
|
|
87
|
+
Miller." This is a misapplication.
|
|
88
|
+
|
|
89
|
+
Miller's original finding was about chunking in working memory under experimental
|
|
90
|
+
conditions, not about navigation comprehension in visual scanning. Navigation items are
|
|
91
|
+
not memorised serially — they are scanned visually. The eye finds familiar labels through
|
|
92
|
+
pattern recognition, not sequential recall. A navigation bar with 8 items is not harder to
|
|
93
|
+
use than one with 7 because of working memory limits; it is harder to use if the visual
|
|
94
|
+
weight is too high, the labels are ambiguous, or the hierarchy is unclear.
|
|
95
|
+
|
|
96
|
+
The legitimate constraint is visual: navigation items compete for attention with the
|
|
97
|
+
content. Too many items reduce the available space per item, force abbreviation or wrapping,
|
|
98
|
+
and reduce the visual weight of each label below the scannable threshold. The practical
|
|
99
|
+
ceiling for top-level navigation is 5–7 items — not because of Miller, but because above
|
|
100
|
+
that count, the labels are too compressed to scan reliably at normal reading distance.
|
|
101
|
+
|
|
102
|
+
**Mobile: bottom tab bar vs hamburger menu.** Research (NN/g, "Hamburger Menus and Hidden
|
|
103
|
+
Navigation Hurt UX Metrics", 2016) measured the effect of hiding navigation behind a
|
|
104
|
+
hamburger icon: discoverability, engagement, and user satisfaction all dropped when
|
|
105
|
+
navigation was hidden. Bottom tab bars — persistent, always-visible — produce higher
|
|
106
|
+
engagement with navigation items than hamburger menus, because visible options are more
|
|
107
|
+
likely to be used than hidden ones. The tab bar is the better choice for the primary
|
|
108
|
+
navigation of any mobile product where the user's destination is not always the same.
|
|
109
|
+
The hamburger is legitimate for secondary or overflow navigation — options that the user
|
|
110
|
+
knows to look for but does not need on every session.
|
|
111
|
+
|
|
112
|
+
Condition → choice → reason: use a persistent bottom tab bar for the 3–5 primary
|
|
113
|
+
destinations of a mobile app; reserve the hamburger for secondary settings, profile, and
|
|
114
|
+
overflow items.
|
|
115
|
+
|
|
116
|
+
---
|
|
117
|
+
|
|
118
|
+
## Tables: density, alignment, and column locking
|
|
119
|
+
|
|
120
|
+
A table is a spatial argument. The row is a record; the column is a property. Every
|
|
121
|
+
decision in a table design either supports or undermines the user's ability to compare
|
|
122
|
+
across rows and navigate down columns.
|
|
123
|
+
|
|
124
|
+
**Column alignment follows data type.** Text columns are left-aligned — the eye enters
|
|
125
|
+
text from the left and left-alignment provides a consistent anchor column for scanning.
|
|
126
|
+
Numeric columns are right-aligned — this places the least significant digit at a fixed
|
|
127
|
+
horizontal position, which enables numerical comparison down the column at a glance.
|
|
128
|
+
Dates are right-aligned when used for sorting comparison; they may be left-aligned in
|
|
129
|
+
conversational contexts (an activity log) where the date is a label, not a value.
|
|
130
|
+
Misaligned numeric columns make comparison impossible regardless of the data they contain.
|
|
131
|
+
Use `font-variant-numeric: tabular-nums` to ensure digits are the same width; proportional
|
|
132
|
+
digits misalign even in right-aligned columns.
|
|
133
|
+
|
|
134
|
+
**Density is a tradeoff between information per screen and cognitive load.** Compact rows
|
|
135
|
+
(24–32px) allow more data per viewport, which is appropriate for expert users who have
|
|
136
|
+
trained for the task (data analysts, financial operators). Comfortable rows (40–48px) allow
|
|
137
|
+
the eye to identify the current row without active tracking, which is appropriate for
|
|
138
|
+
general-purpose tables where the user's cursor is not always near the data they are reading.
|
|
139
|
+
Default to comfortable; move to compact only when the reduction in scrolling genuinely
|
|
140
|
+
improves the task.
|
|
141
|
+
|
|
142
|
+
**Fixed columns unlock horizontal scrolling.** When a table has many columns and requires
|
|
143
|
+
horizontal scrolling, the first column (the row identifier — name, ID, record title) should
|
|
144
|
+
be fixed. Without it, the user scrolls right and loses the context that names what they
|
|
145
|
+
are looking at. This is not a visual preference; it is the difference between a scannable
|
|
146
|
+
table and one that requires memorising the row identity before scrolling.
|
|
147
|
+
|
|
148
|
+
**Zebra striping vs hover highlighting.** Alternating row background colours (zebra
|
|
149
|
+
striping) aid row tracking in wide tables by giving the eye a colour reference to follow
|
|
150
|
+
across columns. Research at Software Usability Research Laboratory (Purdue University, 2008)
|
|
151
|
+
found zebra striping reduced reading errors in wide tables by a measurable margin. In
|
|
152
|
+
narrow tables (3–4 columns), striping adds visual noise without the benefit — the columns
|
|
153
|
+
are close enough to track without colour assistance.
|
|
154
|
+
|
|
155
|
+
---
|
|
156
|
+
|
|
157
|
+
## Modals: the overuse problem and its alternatives
|
|
158
|
+
|
|
159
|
+
A modal dialog interrupts the current task and requires the user to respond before
|
|
160
|
+
continuing. This interrupt is the cost. The interrupt is only worth paying when the
|
|
161
|
+
information requiring response is genuinely critical and cannot be presented in context.
|
|
162
|
+
|
|
163
|
+
The overuse pattern: modals are used as the path of least resistance for any complex
|
|
164
|
+
interaction that the designer does not want to solve in context — a settings form, a
|
|
165
|
+
confirmation step, an extended detail view, a secondary workflow. The modal becomes a
|
|
166
|
+
workaround for insufficient layout thinking.
|
|
167
|
+
|
|
168
|
+
**The alternatives, by use case:**
|
|
169
|
+
|
|
170
|
+
*Additional context about an existing item* — use **inline expansion** (a disclosure
|
|
171
|
+
region below the row, or an accordion panel). The content appears in context; the user
|
|
172
|
+
never leaves their current position; no overlay is required.
|
|
173
|
+
|
|
174
|
+
*A secondary workflow that the user initiates deliberately* — use a **side panel / drawer**
|
|
175
|
+
that slides in from the edge without replacing the current content. The primary view
|
|
176
|
+
remains visible; the secondary workflow is spatially separated but contextually connected.
|
|
177
|
+
|
|
178
|
+
*A transient action requiring quick input* — use a **popover** anchored to the trigger
|
|
179
|
+
element. Small, non-blocking, dismissable. Appropriate for colour pickers, tag editors,
|
|
180
|
+
date pickers, short confirmations.
|
|
181
|
+
|
|
182
|
+
*Non-critical status communication* — use a **toast notification** that appears without
|
|
183
|
+
interrupting the task and disappears after a short duration.
|
|
184
|
+
|
|
185
|
+
Modals are legitimate for: destructive confirmation (deleting records, cancelling
|
|
186
|
+
subscriptions), authentication gates (permission prompts, reauthentication), and complex
|
|
187
|
+
forms that are explicitly task-isolated (a "create new project" form where no in-context
|
|
188
|
+
alternative exists). Even in these cases, keep the modal content minimal — a modal with
|
|
189
|
+
scrolling content has ceased to be a modal; it is a page that lacks a URL.
|
|
190
|
+
|
|
191
|
+
Condition → choice → reason: before designing a modal, name the alternative and the reason
|
|
192
|
+
it does not apply. "The modal was easier to implement" is not a reason; it is a cost
|
|
193
|
+
transferred from the designer to the user.
|
|
194
|
+
|
|
195
|
+
---
|
|
196
|
+
|
|
197
|
+
## Toast notifications and alert hierarchy
|
|
198
|
+
|
|
199
|
+
Feedback messages are not all the same weight. A toast that says "Changes saved" and a
|
|
200
|
+
toast that says "Your account will be deleted in 24 hours" are using the same component
|
|
201
|
+
for radically different situations. The hierarchy of feedback:
|
|
202
|
+
|
|
203
|
+
**Inline validation errors**: adjacent to the field, persistent until corrected. The error
|
|
204
|
+
stays visible because it needs to be referenced while the user corrects the input. A toast
|
|
205
|
+
that vanishes after 3 seconds is wrong for this — the user may not have read it before it
|
|
206
|
+
disappears.
|
|
207
|
+
|
|
208
|
+
**Page-level alerts / banners**: a persistent strip above the content, requiring explicit
|
|
209
|
+
dismissal or action. Used for: degraded service warnings, quota limits, required actions
|
|
210
|
+
before proceeding. Appropriate when the condition affects the entire current task and needs
|
|
211
|
+
to remain visible.
|
|
212
|
+
|
|
213
|
+
**Toast / snackbar notifications**: transient, auto-dismiss after 3–5 seconds. Used for:
|
|
214
|
+
confirmation of non-critical completed actions ("Saved," "Copied to clipboard," "Email
|
|
215
|
+
sent"). Never use for errors that require action — a toast dismisses itself before the user
|
|
216
|
+
can read and respond, leaving the user without context for what went wrong.
|
|
217
|
+
|
|
218
|
+
**Modal alerts**: reserved for situations where the user must acknowledge before proceeding.
|
|
219
|
+
True critical errors, destructive confirmations, blocking conditions. The interrupt is the
|
|
220
|
+
point — the user cannot proceed without addressing this.
|
|
221
|
+
|
|
222
|
+
The common failure is using toasts for errors. "Payment failed" as a toast is a critical
|
|
223
|
+
error delivered in a transient container — it may vanish before the user notices it, and
|
|
224
|
+
even if they see it, it disappears before they can read any detail about why it failed.
|
|
225
|
+
Errors that require action belong in persistent, in-context containers.
|
|
226
|
+
|
|
227
|
+
---
|
|
228
|
+
|
|
229
|
+
## Search UI patterns
|
|
230
|
+
|
|
231
|
+
Search is not a feature, it is an entire interaction model. The decision of how to surface
|
|
232
|
+
search determines whether users who cannot navigate will find anything at all.
|
|
233
|
+
|
|
234
|
+
**The search input placement.** The search input belongs where the content lives — in the
|
|
235
|
+
page header for global search, above a list for list-specific search. A search icon that
|
|
236
|
+
triggers an overlay or a page navigation to a search page adds a step between the user and
|
|
237
|
+
the results. The step costs engagement, particularly for exploratory searches where the
|
|
238
|
+
user is not certain what they are looking for.
|
|
239
|
+
|
|
240
|
+
**Immediate vs submit-triggered results.** Typeahead (results updating on each keystroke)
|
|
241
|
+
is the correct choice for any search over bounded data where the server can respond in under
|
|
242
|
+
200ms. It removes the "submit and wait" loop and allows the user to steer the query
|
|
243
|
+
toward what is actually available, which is more efficient than crafting a full query in
|
|
244
|
+
advance. Submit-triggered search is appropriate for large unindexed datasets where
|
|
245
|
+
typeahead would be too slow or too broad to be useful.
|
|
246
|
+
|
|
247
|
+
**Empty search state vs zero-results state.** These are different screens with different
|
|
248
|
+
jobs. The empty search state (the search input just received focus, no query typed) should
|
|
249
|
+
show recent searches, popular items, or a helpful starting prompt — never a blank field
|
|
250
|
+
with nothing to do. The zero-results state should explain why there are no results (common
|
|
251
|
+
causes: typo, too-specific query, out-of-scope category) and offer a path forward (suggested
|
|
252
|
+
queries, a broader search, a direct contact option). A zero-results page that says only
|
|
253
|
+
"No results found" has told the user that their query failed without helping them succeed.
|
|
254
|
+
|
|
255
|
+
---
|
|
256
|
+
|
|
257
|
+
## Sources
|
|
258
|
+
|
|
259
|
+
- Miller, "The Magical Number Seven, Plus or Minus Two" (1956, Psychological Review) —
|
|
260
|
+
working memory chunking; note the common misapplication to navigation counts
|
|
261
|
+
- NN/g, "Hamburger Menus and Hidden Navigation Hurt UX Metrics" (2016) — bottom tab bar
|
|
262
|
+
vs hamburger menu engagement and discoverability comparison
|
|
263
|
+
- Material Design 3, Button component — single primary-per-view, hierarchy specification
|
|
264
|
+
- Carbon Design System (IBM), Button — danger/destructive semantic variant specification
|
|
265
|
+
- Helios Design System (HashiCorp), Button Organisation — three-tier ceiling rationale
|
|
266
|
+
- Baymard Institute, "Avoid Extensive Multi-column Layouts" (baymard.com) — inline
|
|
267
|
+
validation timing and abandonment rate research
|
|
268
|
+
- Software Usability Research Laboratory (Purdue University, 2008) — zebra striping and
|
|
269
|
+
row-tracking error rates in wide tables
|
|
270
|
+
- NN/g, "Website Forms Usability: Top 10 Recommendations" — validation timing, label
|
|
271
|
+
placement, single-column preference
|
|
272
|
+
- Google Fonts Knowledge, "Understanding numerals" — tabular-nums for column alignment
|