@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.
Files changed (131) hide show
  1. package/LICENSE +21 -0
  2. package/README.ko.md +282 -0
  3. package/README.md +282 -0
  4. package/adapters/build.ts +184 -0
  5. package/adapters/claude.ts +111 -0
  6. package/adapters/codex.ts +68 -0
  7. package/adapters/tokens.ts +21 -0
  8. package/adapters/tool-map.json +7 -0
  9. package/bin/omd-install.ts +76 -0
  10. package/bin/omd.ts +1774 -0
  11. package/core/asset-sourcing/index.ts +187 -0
  12. package/core/coach/index.ts +98 -0
  13. package/core/composition/app-shell-workbench.md +168 -0
  14. package/core/composition/asymmetric-diagonal-grid.md +178 -0
  15. package/core/composition/bento-grid.md +223 -0
  16. package/core/composition/editorial-index-labels.md +173 -0
  17. package/core/composition/form-wizard-stepper.md +132 -0
  18. package/core/composition/master-detail-flow.md +129 -0
  19. package/core/composition/section-inversion.md +182 -0
  20. package/core/composition/sidebar-margin-annotation.md +184 -0
  21. package/core/composition/split-screen-hero.md +224 -0
  22. package/core/composition/sticky-sidebar-scroll.md +219 -0
  23. package/core/composition/typographic-hero.md +100 -0
  24. package/core/composition-contract/index.ts +233 -0
  25. package/core/composition-contract/visual-richness.ts +101 -0
  26. package/core/config/index.ts +27 -0
  27. package/core/copy/index.ts +296 -0
  28. package/core/craft/finish-pass.md +356 -0
  29. package/core/craft/index.ts +33 -0
  30. package/core/design/index.ts +581 -0
  31. package/core/design/interaction-states.ts +189 -0
  32. package/core/eval-harness/index.ts +225 -0
  33. package/core/evidence/final.ts +444 -0
  34. package/core/evidence/task.ts +459 -0
  35. package/core/figma/client.ts +312 -0
  36. package/core/figma/diff.ts +261 -0
  37. package/core/figma/responsive.ts +310 -0
  38. package/core/figma/system.ts +466 -0
  39. package/core/figma/types.ts +185 -0
  40. package/core/frame/check-ux.ts +180 -0
  41. package/core/frame/index.ts +39 -0
  42. package/core/frame/write.ts +168 -0
  43. package/core/graphics/css-illustration-primitives.md +189 -0
  44. package/core/graphics/duotone-image-presets.md +173 -0
  45. package/core/graphics/gradient-mesh.md +178 -0
  46. package/core/graphics/noise-grain-texture.md +150 -0
  47. package/core/graphics/placeholder-policy.md +185 -0
  48. package/core/graphics/svg-geometric-patterns.md +145 -0
  49. package/core/history/index.ts +39 -0
  50. package/core/install/detect.ts +32 -0
  51. package/core/install/install.ts +321 -0
  52. package/core/install/patch-claude.ts +109 -0
  53. package/core/install/patch-codex.ts +82 -0
  54. package/core/interaction/index.ts +101 -0
  55. package/core/interaction/recipes/signature-lighting.md +203 -0
  56. package/core/ir/dom.ts +352 -0
  57. package/core/ir/normalize.ts +177 -0
  58. package/core/motion/easing.md +151 -0
  59. package/core/motion/energy.ts +188 -0
  60. package/core/motion/recipes/image-hover-distortion.md +198 -0
  61. package/core/motion/recipes/magnetic-hover.md +229 -0
  62. package/core/motion/recipes/marquee.md +202 -0
  63. package/core/motion/recipes/number-counter.md +242 -0
  64. package/core/motion/recipes/page-loader.md +240 -0
  65. package/core/motion/recipes/parallax.md +233 -0
  66. package/core/motion/recipes/scroll-reveal.md +210 -0
  67. package/core/motion/recipes/section-color-inversion.md +217 -0
  68. package/core/motion/recipes/split-text-entrance.md +222 -0
  69. package/core/motion/recipes/stagger-orchestrator.md +227 -0
  70. package/core/motion/recipes/sticky-scene-transition.md +246 -0
  71. package/core/motion/recipes/view-transitions.md +239 -0
  72. package/core/probe/index.ts +186 -0
  73. package/core/probe/schema.json +46 -0
  74. package/core/protocol/composition-contract.md +192 -0
  75. package/core/protocol/copy-deck.md +96 -0
  76. package/core/protocol/human-design-loop.md +447 -0
  77. package/core/protocol/slop-review.md +71 -0
  78. package/core/ref/batch.ts +94 -0
  79. package/core/ref/blueprint.ts +199 -0
  80. package/core/ref/distance.ts +225 -0
  81. package/core/ref/invariants.ts +162 -0
  82. package/core/ref/signal.ts +37 -0
  83. package/core/ref/store.ts +122 -0
  84. package/core/render/index.ts +607 -0
  85. package/core/rules/attribution.ts +159 -0
  86. package/core/rules/builtin/contrast.yaml +8 -0
  87. package/core/rules/builtin/focus.yaml +8 -0
  88. package/core/rules/builtin/hit-area.yaml +8 -0
  89. package/core/rules/builtin/ko.yaml +6 -0
  90. package/core/rules/builtin/motion.yaml +80 -0
  91. package/core/rules/builtin/slop.yaml +365 -0
  92. package/core/rules/builtin/spacing.yaml +9 -0
  93. package/core/rules/builtin/system.yaml +42 -0
  94. package/core/rules/builtin/token.yaml +8 -0
  95. package/core/rules/builtin/ux.yaml +125 -0
  96. package/core/rules/engine.ts +88 -0
  97. package/core/rules/leakage.ts +72 -0
  98. package/core/rules/motion-spec.ts +204 -0
  99. package/core/site/index.ts +100 -0
  100. package/core/slop/index.ts +427 -0
  101. package/core/slop/text-slop.ts +220 -0
  102. package/core/source-seal/index.ts +174 -0
  103. package/core/stack/index.ts +53 -0
  104. package/core/target/index.ts +183 -0
  105. package/core/theory/color.md +285 -0
  106. package/core/theory/components.md +272 -0
  107. package/core/theory/craft.md +270 -0
  108. package/core/theory/expressive.md +213 -0
  109. package/core/theory/imagegen.md +105 -0
  110. package/core/theory/layout.md +159 -0
  111. package/core/theory/motion.md +283 -0
  112. package/core/theory/typography.md +134 -0
  113. package/core/theory/ux.md +580 -0
  114. package/core/theory/voice.md +595 -0
  115. package/core/types.ts +684 -0
  116. package/package.json +40 -0
  117. package/src/agents/composer.agent.yaml +174 -0
  118. package/src/agents/eye.agent.yaml +208 -0
  119. package/src/agents/framer.agent.yaml +52 -0
  120. package/src/agents/glance.agent.yaml +19 -0
  121. package/src/agents/hand.agent.yaml +200 -0
  122. package/src/agents/scout.agent.yaml +71 -0
  123. package/src/agents/sketch.agent.yaml +64 -0
  124. package/src/agents/typesetter.agent.yaml +52 -0
  125. package/src/agents/writer.agent.yaml +63 -0
  126. package/src/skills/omd-coach/SKILL.md +40 -0
  127. package/src/skills/omd-critique/SKILL.md +77 -0
  128. package/src/skills/omd-figma/SKILL.md +256 -0
  129. package/src/skills/omd-humanize/SKILL.md +160 -0
  130. package/src/skills/omd-scout/SKILL.md +77 -0
  131. 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