@pugi/cli 1.0.0-alpha.25 → 1.0.0-alpha.250

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 (206) hide show
  1. package/README.md +1 -1
  2. package/THIRD_PARTY_NOTICES.md +39 -0
  3. package/bin/pugi +19 -5
  4. package/models.json +1 -0
  5. package/package.json +22 -6
  6. package/release.json +8 -0
  7. package/skills/a11y/SKILL.md +27 -0
  8. package/skills/ab-test-analysis/SKILL.md +15 -0
  9. package/skills/ai-sdk/SKILL.md +27 -0
  10. package/skills/api-integration/SKILL.md +27 -0
  11. package/skills/architecture-review/SKILL.md +19 -0
  12. package/skills/brand-voice/SKILL.md +27 -0
  13. package/skills/ci-cd-pipeline/SKILL.md +15 -0
  14. package/skills/code-review/SKILL.md +27 -0
  15. package/skills/competitor-analysis/SKILL.md +27 -0
  16. package/skills/content-design/SKILL.md +35 -0
  17. package/skills/css-tailwind/SKILL.md +39 -0
  18. package/skills/css-tailwind/quirks.md +86 -0
  19. package/skills/db-vector/SKILL.md +27 -0
  20. package/skills/design-radix/SKILL.md +35 -0
  21. package/skills/design-system/SKILL.md +35 -0
  22. package/skills/detecting-data-anomalies/SKILL.md +15 -0
  23. package/skills/docker-deploy/SKILL.md +15 -0
  24. package/skills/edge-case-finder/SKILL.md +27 -0
  25. package/skills/fonts/SKILL.md +34 -0
  26. package/skills/framework-grammy/SKILL.md +27 -0
  27. package/skills/framework-mastra/SKILL.md +27 -0
  28. package/skills/framework-nestjs/SKILL.md +27 -0
  29. package/skills/framework-prisma/SKILL.md +27 -0
  30. package/skills/funnel-optimization/SKILL.md +19 -0
  31. package/skills/ha-prototype-web/LICENSE-APACHE-2.0 +201 -0
  32. package/skills/ha-prototype-web/NOTICE.md +126 -0
  33. package/skills/ha-prototype-web/SKILL.md +25 -0
  34. package/skills/icons/SKILL.md +34 -0
  35. package/skills/landing-templates/SKILL.md +35 -0
  36. package/skills/market-research/SKILL.md +27 -0
  37. package/skills/metrics-dashboard/SKILL.md +19 -0
  38. package/skills/monitoring-setup/SKILL.md +15 -0
  39. package/skills/motion/SKILL.md +27 -0
  40. package/skills/nestjs-api/SKILL.md +38 -0
  41. package/skills/nestjs-api/quirks.md +36 -0
  42. package/skills/nextjs-page/SKILL.md +27 -0
  43. package/skills/od-blog-post/LICENSE-APACHE-2.0 +201 -0
  44. package/skills/od-blog-post/NOTICE.md +126 -0
  45. package/skills/od-blog-post/SKILL.md +81 -0
  46. package/skills/od-contact-widget/LICENSE-APACHE-2.0 +201 -0
  47. package/skills/od-contact-widget/NOTICE.md +126 -0
  48. package/skills/od-contact-widget/SKILL.md +132 -0
  49. package/skills/od-craft/LICENSE-APACHE-2.0 +201 -0
  50. package/skills/od-craft/NOTICE.md +126 -0
  51. package/skills/od-craft/SKILL.md +58 -0
  52. package/skills/od-craft/references/README.md +108 -0
  53. package/skills/od-craft/references/accessibility-baseline.md +203 -0
  54. package/skills/od-craft/references/animation-discipline.md +156 -0
  55. package/skills/od-craft/references/anti-ai-slop.md +86 -0
  56. package/skills/od-craft/references/color.md +90 -0
  57. package/skills/od-craft/references/form-validation.md +223 -0
  58. package/skills/od-craft/references/laws-of-ux.md +298 -0
  59. package/skills/od-craft/references/rtl-and-bidi.md +188 -0
  60. package/skills/od-craft/references/state-coverage.md +136 -0
  61. package/skills/od-craft/references/typography-hierarchy-editorial.md +184 -0
  62. package/skills/od-craft/references/typography-hierarchy.md +167 -0
  63. package/skills/od-craft/references/typography.md +124 -0
  64. package/skills/od-critique/LICENSE-APACHE-2.0 +201 -0
  65. package/skills/od-critique/NOTICE.md +126 -0
  66. package/skills/od-critique/SKILL.md +259 -0
  67. package/skills/od-dashboard/LICENSE-APACHE-2.0 +201 -0
  68. package/skills/od-dashboard/NOTICE.md +126 -0
  69. package/skills/od-dashboard/SKILL.md +77 -0
  70. package/skills/od-design-brief/LICENSE-APACHE-2.0 +201 -0
  71. package/skills/od-design-brief/NOTICE.md +126 -0
  72. package/skills/od-design-brief/SKILL.md +260 -0
  73. package/skills/od-docs-page/LICENSE-APACHE-2.0 +201 -0
  74. package/skills/od-docs-page/NOTICE.md +126 -0
  75. package/skills/od-docs-page/SKILL.md +81 -0
  76. package/skills/od-email-marketing/LICENSE-APACHE-2.0 +201 -0
  77. package/skills/od-email-marketing/NOTICE.md +126 -0
  78. package/skills/od-email-marketing/SKILL.md +85 -0
  79. package/skills/od-eng-runbook/LICENSE-APACHE-2.0 +201 -0
  80. package/skills/od-eng-runbook/NOTICE.md +126 -0
  81. package/skills/od-eng-runbook/SKILL.md +52 -0
  82. package/skills/od-faq-page/LICENSE-APACHE-2.0 +201 -0
  83. package/skills/od-faq-page/NOTICE.md +126 -0
  84. package/skills/od-faq-page/SKILL.md +113 -0
  85. package/skills/od-finance-report/LICENSE-APACHE-2.0 +201 -0
  86. package/skills/od-finance-report/NOTICE.md +126 -0
  87. package/skills/od-finance-report/SKILL.md +62 -0
  88. package/skills/od-frontend-design/LICENSE-APACHE-2.0 +201 -0
  89. package/skills/od-frontend-design/LICENSE.txt +177 -0
  90. package/skills/od-frontend-design/NOTICE.md +126 -0
  91. package/skills/od-frontend-design/SKILL.md +86 -0
  92. package/skills/od-hr-onboarding/LICENSE-APACHE-2.0 +201 -0
  93. package/skills/od-hr-onboarding/NOTICE.md +126 -0
  94. package/skills/od-hr-onboarding/SKILL.md +53 -0
  95. package/skills/od-invoice/LICENSE-APACHE-2.0 +201 -0
  96. package/skills/od-invoice/NOTICE.md +126 -0
  97. package/skills/od-invoice/SKILL.md +49 -0
  98. package/skills/od-kanban-board/LICENSE-APACHE-2.0 +201 -0
  99. package/skills/od-kanban-board/NOTICE.md +126 -0
  100. package/skills/od-kanban-board/SKILL.md +51 -0
  101. package/skills/od-login-flow/LICENSE-APACHE-2.0 +201 -0
  102. package/skills/od-login-flow/NOTICE.md +126 -0
  103. package/skills/od-login-flow/SKILL.md +49 -0
  104. package/skills/od-login-flow/references/checklist.md +19 -0
  105. package/skills/od-magazine-poster/LICENSE-APACHE-2.0 +201 -0
  106. package/skills/od-magazine-poster/NOTICE.md +126 -0
  107. package/skills/od-magazine-poster/SKILL.md +90 -0
  108. package/skills/od-meeting-notes/LICENSE-APACHE-2.0 +201 -0
  109. package/skills/od-meeting-notes/NOTICE.md +126 -0
  110. package/skills/od-meeting-notes/SKILL.md +48 -0
  111. package/skills/od-mobile-app/LICENSE-APACHE-2.0 +201 -0
  112. package/skills/od-mobile-app/NOTICE.md +126 -0
  113. package/skills/od-mobile-app/SKILL.md +103 -0
  114. package/skills/od-mobile-app/assets/template.html +442 -0
  115. package/skills/od-mobile-app/references/checklist.md +46 -0
  116. package/skills/od-mobile-app/references/layouts.md +312 -0
  117. package/skills/od-mobile-onboarding/LICENSE-APACHE-2.0 +201 -0
  118. package/skills/od-mobile-onboarding/NOTICE.md +126 -0
  119. package/skills/od-mobile-onboarding/SKILL.md +54 -0
  120. package/skills/od-pm-spec/LICENSE-APACHE-2.0 +201 -0
  121. package/skills/od-pm-spec/NOTICE.md +126 -0
  122. package/skills/od-pm-spec/SKILL.md +53 -0
  123. package/skills/od-pricing-page/LICENSE-APACHE-2.0 +201 -0
  124. package/skills/od-pricing-page/NOTICE.md +126 -0
  125. package/skills/od-pricing-page/SKILL.md +80 -0
  126. package/skills/od-redesign-skill/LICENSE +21 -0
  127. package/skills/od-redesign-skill/LICENSE-APACHE-2.0 +201 -0
  128. package/skills/od-redesign-skill/NOTICE.md +126 -0
  129. package/skills/od-redesign-skill/SKILL.md +205 -0
  130. package/skills/od-reference-design-contract/LICENSE-APACHE-2.0 +201 -0
  131. package/skills/od-reference-design-contract/NOTICE.md +126 -0
  132. package/skills/od-reference-design-contract/SKILL.md +131 -0
  133. package/skills/od-reference-design-contract/references/checklist.md +26 -0
  134. package/skills/od-saas-landing/LICENSE-APACHE-2.0 +201 -0
  135. package/skills/od-saas-landing/NOTICE.md +126 -0
  136. package/skills/od-saas-landing/SKILL.md +125 -0
  137. package/skills/od-social-carousel/LICENSE-APACHE-2.0 +201 -0
  138. package/skills/od-social-carousel/NOTICE.md +126 -0
  139. package/skills/od-social-carousel/SKILL.md +92 -0
  140. package/skills/od-team-okrs/LICENSE-APACHE-2.0 +201 -0
  141. package/skills/od-team-okrs/NOTICE.md +126 -0
  142. package/skills/od-team-okrs/SKILL.md +48 -0
  143. package/skills/od-waitlist-page/LICENSE-APACHE-2.0 +201 -0
  144. package/skills/od-waitlist-page/NOTICE.md +126 -0
  145. package/skills/od-waitlist-page/SKILL.md +229 -0
  146. package/skills/od-waitlist-page/assets/template.html +457 -0
  147. package/skills/od-waitlist-page/references/checklist.md +42 -0
  148. package/skills/od-web-design-guidelines/LICENSE +21 -0
  149. package/skills/od-web-design-guidelines/LICENSE-APACHE-2.0 +201 -0
  150. package/skills/od-web-design-guidelines/NOTICE.md +126 -0
  151. package/skills/od-web-design-guidelines/SKILL.md +66 -0
  152. package/skills/od-web-design-guidelines/references/guidelines.md +133 -0
  153. package/skills/od-web-prototype/LICENSE-APACHE-2.0 +201 -0
  154. package/skills/od-web-prototype/NOTICE.md +126 -0
  155. package/skills/od-web-prototype/SKILL.md +102 -0
  156. package/skills/od-web-prototype/assets/template.html +348 -0
  157. package/skills/od-web-prototype/references/checklist.md +47 -0
  158. package/skills/od-web-prototype/references/layouts.md +247 -0
  159. package/skills/od-web-prototype-taste-brutalist/LICENSE-APACHE-2.0 +201 -0
  160. package/skills/od-web-prototype-taste-brutalist/LICENSE-MIT-Leonxlnx +21 -0
  161. package/skills/od-web-prototype-taste-brutalist/NOTICE.md +126 -0
  162. package/skills/od-web-prototype-taste-brutalist/SKILL.md +64 -0
  163. package/skills/od-web-prototype-taste-editorial/LICENSE-APACHE-2.0 +201 -0
  164. package/skills/od-web-prototype-taste-editorial/LICENSE-MIT-Leonxlnx +21 -0
  165. package/skills/od-web-prototype-taste-editorial/NOTICE.md +126 -0
  166. package/skills/od-web-prototype-taste-editorial/SKILL.md +61 -0
  167. package/skills/od-web-prototype-taste-soft/LICENSE-APACHE-2.0 +201 -0
  168. package/skills/od-web-prototype-taste-soft/LICENSE-MIT-Leonxlnx +21 -0
  169. package/skills/od-web-prototype-taste-soft/NOTICE.md +126 -0
  170. package/skills/od-web-prototype-taste-soft/SKILL.md +64 -0
  171. package/skills/od-webgl-experience/LICENSE-APACHE-2.0 +201 -0
  172. package/skills/od-webgl-experience/NOTICE.md +126 -0
  173. package/skills/od-webgl-experience/SKILL.md +81 -0
  174. package/skills/od-wireframe-annotated/LICENSE-APACHE-2.0 +201 -0
  175. package/skills/od-wireframe-annotated/NOTICE.md +126 -0
  176. package/skills/od-wireframe-annotated/SKILL.md +90 -0
  177. package/skills/od-wireframe-greybox/LICENSE-APACHE-2.0 +201 -0
  178. package/skills/od-wireframe-greybox/NOTICE.md +126 -0
  179. package/skills/od-wireframe-greybox/SKILL.md +90 -0
  180. package/skills/od-wireframe-mobile-flow/LICENSE-APACHE-2.0 +201 -0
  181. package/skills/od-wireframe-mobile-flow/NOTICE.md +126 -0
  182. package/skills/od-wireframe-mobile-flow/SKILL.md +96 -0
  183. package/skills/od-wireframe-sketch/LICENSE-APACHE-2.0 +201 -0
  184. package/skills/od-wireframe-sketch/NOTICE.md +126 -0
  185. package/skills/od-wireframe-sketch/SKILL.md +98 -0
  186. package/skills/orchestrating-multi-agent-systems/SKILL.md +19 -0
  187. package/skills/preprocessing-data/SKILL.md +15 -0
  188. package/skills/project-memory-keeper/SKILL.md +19 -0
  189. package/skills/public-apis/SKILL.md +27 -0
  190. package/skills/pugi-patterns/SKILL.md +19 -0
  191. package/skills/react-component/SKILL.md +27 -0
  192. package/skills/sprint-planning/SKILL.md +19 -0
  193. package/skills/stack-selection/SKILL.md +15 -0
  194. package/skills/standup-summary/SKILL.md +15 -0
  195. package/skills/task-decomposition/SKILL.md +19 -0
  196. package/skills/team-routing/SKILL.md +15 -0
  197. package/skills/tech-survey/SKILL.md +15 -0
  198. package/skills/test-plan/SKILL.md +15 -0
  199. package/skills/typescript-refactor/SKILL.md +15 -0
  200. package/skills/ui-components/SKILL.md +35 -0
  201. package/skills/ux-patterns/SKILL.md +35 -0
  202. package/skills/web-platform/SKILL.md +27 -0
  203. package/skills/web-scrape/SKILL.md +27 -0
  204. package/skills/web-search/SKILL.md +15 -0
  205. package/skills/webapp-testing/SKILL.md +37 -0
  206. package/skills/webapp-testing/quirks.md +26 -0
@@ -0,0 +1,298 @@
1
+ <!-- Vendored from nexu-io/open-design@78d5637cb49d (craft/laws-of-ux.md), Apache-2.0, Copyright 2026 Open Design contributors. Modified by Pugi: this header line added; the text below is otherwise verbatim. Paths it names (apps/daemon/src/lint-artifact.ts, pnpm lint:craft, od.craft.requires) are upstream's, not Pugi's: the Pugi port of the linter is packages/opencode/src/session/design-lint.ts. See NOTICE.md in the skill root. -->
2
+
3
+ # Laws of UX craft rules
4
+
5
+ Universal cognitive, perceptual, and behavioral heuristics that decide
6
+ what a UI composes — how many pricing tiers fit on a screen, where a
7
+ primary action anchors in scanning order, when a progress indicator
8
+ earns its place, why a settings list needs grouping. The active
9
+ `DESIGN.md` decides brand visual language; the existing craft files
10
+ decide rendering rules (color, typography, motion, states, ARIA, RTL,
11
+ forms); this file decides composition rules grounded in named research.
12
+
13
+ > Distilled from primary sources: Hick (1952) + Hyman (1953), Miller
14
+ > (1956) for chunking / `7±2` channel capacity, Cowan (2001) for the
15
+ > modern ~4 working-memory bound, Fitts (1954), Wertheimer (1923) for
16
+ > proximity / similarity / Prägnanz, Palmer (1992) for Common Region,
17
+ > Palmer & Rock (1994) for Uniform Connectedness, Kahneman /
18
+ > Fredrickson / Schreiber / Redelmeier (1993) for Peak-End, Zeigarnik
19
+ > (1927), Csíkszentmihályi (1975), Hull (1932), von Restorff (1933),
20
+ > Broadbent (1958), Sweller (1988), Postel (RFC 760, 1980), Carroll &
21
+ > Rosson (1987), Tversky & Kahneman (1974) for Anchoring, Kurosu &
22
+ > Kashimura (1995), Iyengar & Lepper (2000), Toffler (1970), Pareto
23
+ > (c.1906) / Juran (*Quality Control Handbook*, 1951), Ebbinghaus
24
+ > (1885), Ockham (14th c.), Tesler at Apple (1980s), Nielsen (2000),
25
+ > Norman *POET* (1988), Parkinson (1955).
26
+
27
+ ## Prior art and scope
28
+
29
+ Existing public catalogs of UX heuristics (Yablonski's lawsofux.com,
30
+ NN/g's 10 usability heuristics, Material 3 motion + interaction
31
+ guidance, Apple HIG, Baymard Institute checkout research) inventory the
32
+ laws but rarely tie each one to a concrete code-gen directive. This
33
+ file does the translation: every entry ends with one actionable move
34
+ for an HTML / Tailwind / React-emitting agent. Sibling craft files
35
+ (`accessibility-baseline.md`, `state-coverage.md`, `typography.md`,
36
+ `anti-ai-slop.md`, `color.md`, `animation-discipline.md`,
37
+ `form-validation.md`) own the auto-checked rules; this file names the
38
+ underlying law and surfaces the folklore. Out of scope: Weber-Fechner
39
+ psychophysics and Signal Detection Theory — both apply to UI but the
40
+ prompt-emission directives are too narrow to earn a slot here. Add
41
+ later if a skill needs them.
42
+
43
+ The rules below are guidance, not auto-checked. Reviewers and the agent
44
+ apply them; the linter does not. Where a law has a sibling rule already
45
+ auto-checked elsewhere — touch-target floor in
46
+ `accessibility-baseline.md`, the 300 ms / 2 s / 10 s / 30 s / 60 s
47
+ loading thresholds in `state-coverage.md`, ALL CAPS letter-spacing in
48
+ `typography.md`, the indigo / gradient / emoji-icon list in
49
+ `anti-ai-slop.md` — the entry below cross-references rather than
50
+ duplicates.
51
+
52
+ ## Perception and visual grouping
53
+
54
+ Five Gestalt laws plus three attention-and-recognition laws govern how
55
+ the eye groups elements before the brain reads them.
56
+
57
+ - **Law of Proximity** (Wertheimer, 1923). Objects near each other read
58
+ as a group. Cheapest grouping signal — cheaper than borders or shared
59
+ color. Apply variable vertical rhythm: 8–12 px within a group,
60
+ 32–48 px between groups. Uniform spacing reads as nothing being
61
+ grouped.
62
+ - **Law of Similarity** (Wertheimer, 1923). Visually similar elements
63
+ read as a group. Equivalent affordances must share treatment — every
64
+ list row identical class set, every secondary button identical, every
65
+ destructive action identical. Visible deviation is reserved for the
66
+ one item meant to draw attention (the recommended pricing tier, the
67
+ selected nav item).
68
+ - **Law of Common Region** (Palmer, 1992). A shared bounded area binds
69
+ enclosed elements. Use enclosure when proximity is not enough — and
70
+ reserve it. Concrete numbers: padding ≥16 px inside the region,
71
+ distinct surface (border + tinted background, or card chrome at
72
+ ≥1 px hairline). A page where every section is bordered destroys the
73
+ signal.
74
+ - **Law of Prägnanz / Good Figure** (Wertheimer, 1923). The eye
75
+ resolves complex layouts into the simplest underlying form. Designs
76
+ that align with a clear underlying grid (12-column, F-pattern,
77
+ 4-quadrant) feel inevitable; ornate breaks that add nothing semantic
78
+ feel arbitrary.
79
+ - **Law of Uniform Connectedness** (Palmer & Rock, 1994). The
80
+ strongest grouping signal in the Gestalt hierarchy: connected lines,
81
+ shared toolbars, or bracketing containers tie items together more
82
+ strongly than proximity or similarity. Use for wizard steps,
83
+ comparison sets, and explicit navigation flows.
84
+ - **Selective Attention** (Broadbent, *Perception and Communication*,
85
+ 1958). Cognitive bandwidth is finite. Users filter aggressively and
86
+ ignore anything that looks irrelevant to their goal — banner blindness
87
+ comes from this. Reserve the strongest visual contrast for the single
88
+ goal-relevant action; let supporting content recede in weight.
89
+ - **Von Restorff Effect** (von Restorff, 1933). The item that differs
90
+ from a uniform field is the one most likely to be remembered. Make
91
+ the recommended pricing tier, the active nav item, the warning state
92
+ visually distinct. Pair contrast with a non-color signal (icon, text
93
+ label, position) — `accessibility-baseline.md` rules out color-alone
94
+ signaling.
95
+ - **Aesthetic-Usability Effect** (Kurosu & Kashimura, Hitachi Design
96
+ Center, 1995). Visual polish biases perceived usability. Refined
97
+ typography, generous whitespace, and a calm palette earn the benefit
98
+ of the doubt for minor friction. Never substitutes for measurable
99
+ usability or for `state-coverage.md`'s required-states rule.
100
+
101
+ ## Decision-making
102
+
103
+ Six laws govern how fast and how well users decide when an interface
104
+ offers a choice.
105
+
106
+ - **Hick's Law** (Hick, 1952; Hyman, 1953 replication). Decision time
107
+ grows roughly log(n+1) with the number of equivalent options. Cap any
108
+ single decision-screen to 3–5 visible primary options; collapse the
109
+ rest behind a "More" / progressive disclosure pattern; visually
110
+ distinguish the recommended choice. Aggressive truncation that hides
111
+ the path forward is the opposite failure mode — surface the full
112
+ option set, just don't render every option at the same visual weight.
113
+ - **Choice Overload** (Iyengar & Lepper, *Journal of Personality and
114
+ Social Psychology*, 2000; framing dates to Toffler, *Future Shock*,
115
+ 1970). Too many roughly-equivalent options stall or abandon the
116
+ decision. Pricing pages: 3–4 tiers, exactly one marked recommended.
117
+ Product grids: 6–9 hero cards above the fold. Settings panels: ≤5
118
+ named groups. Never emit a flat wall of equivalents.
119
+ - **Anchoring** (Tversky & Kahneman, *Science* 185:1124–1131, 1974).
120
+ The first number a user sees re-weights every subsequent number.
121
+ Place the recommended pricing tier where it anchors the comparison;
122
+ render yearly-billing savings as concrete dollar deltas, not just
123
+ percentage badges; pre-select the safer default in radio groups.
124
+ Visual weight matches intended decision weight.
125
+ - **Pareto Principle / 80-20** (Pareto, c.1906; Juran, *Quality Control
126
+ Handbook*, 1951 — popularized the management-application framing). A
127
+ small share of features drives most of the value. Identify the 2–3 actions
128
+ that drive the dominant journey for the target persona; emphasize
129
+ those visually; demote the long tail to overflow menus, footer
130
+ surfaces, or settings.
131
+ - **Tesler's Law / Conservation of Complexity** (Tesler, Apple, 1980s).
132
+ Every product has an irreducible amount of complexity. The design
133
+ choice is *where* it lives — engineering team, interface, user — not
134
+ whether to eliminate it. When complexity reaches the user, surface
135
+ contextual guidance (tooltips, smart defaults, inline empty-state
136
+ coaching, progressive disclosure) at the exact step where it
137
+ surfaces. Hiding it is not the same as removing it.
138
+ - **Occam's Razor** (Ockham, 14th c.). Among options that explain the
139
+ data equally well, prefer the one with the fewest assumptions. Specify
140
+ a minimal element inventory; forbid decorative chrome that doesn't
141
+ serve a stated user task. The law constrains assumptions, not feature
142
+ count — a "minimum viable" framing misreads it.
143
+
144
+ ## Memory and learning
145
+
146
+ Five laws cover how working memory handles information density and
147
+ what the user retains afterward.
148
+
149
+ - **Miller's Law and Chunking** (Miller, *The Magical Number Seven,
150
+ Plus or Minus Two*, *Psychological Review*, 1956 — channel capacity /
151
+ `7±2` and chunking; Cowan, *Behavioral and Brain Sciences* 24:1, 2001
152
+ for the modern ~4-item working-memory bound). Working memory holds
153
+ about four items reliably and up to seven for short-term recall. Each
154
+ slot can hold a *larger familiar unit*, constrained by the user's
155
+ domain knowledge — chunking does not let you pack arbitrary content
156
+ into a single slot. Often misread as a rule about menu length;
157
+ Miller's paper is about chunks. Group related fields with clear
158
+ section headings, dividers, or card containers. A settings page with
159
+ sections "Account / Notifications / Privacy / Billing / Danger zone"
160
+ beats one flat list of 30 toggles.
161
+ - **Working Memory** (Baddeley & Hitch, 1974; lineage to Atkinson &
162
+ Shiffrin, 1968). Items decay in seconds without rehearsal. Recognition
163
+ beats recall: persisting prior context across screens, marking visited
164
+ elements, and surfacing comparison views beats forcing the user to
165
+ memorize. On dashboards specifically: sticky filter chips, last-N
166
+ selections persisted, breadcrumbs that include applied filters.
167
+ - **Serial Position Effect** (Ebbinghaus, *Über das Gedächtnis*, 1885).
168
+ Recall favors the extremes — primacy at the start, recency at the
169
+ end — while middle items fade. Anchor the most important nav items at
170
+ the leftmost and rightmost positions of a horizontal menu; cluster
171
+ utilities in the middle.
172
+ - **Peak-End Rule** (Kahneman, Fredrickson, Schreiber, Redelmeier,
173
+ *Psychological Science*, 1993). Memory of an experience is dominated
174
+ by the emotional peak and the ending, not the average. Stage a
175
+ high-effort celebratory success state; let intermediate steps stay
176
+ calm. Mediocre middles matter less than a strong close. The peak
177
+ belongs at the *end* of a flow, not as arbitrary mid-flow motion —
178
+ `animation-discipline.md` rejects motion that performs (rather than
179
+ confirms) a state change.
180
+ - **Zeigarnik Effect** (Zeigarnik, *Über das Behalten erledigter und
181
+ unerledigter Handlungen*, 1927). Uncompleted tasks create cognitive
182
+ tension that pulls the user back. Visible progress ("3 of 5 steps",
183
+ greyed-out next sections) converts that tension into completion
184
+ pressure. Reserve for genuinely beneficial flows like onboarding;
185
+ applying the same lever to streaks, daily-quest counters, or notification-
186
+ reduction nags is a dark pattern.
187
+
188
+ ## Interaction and motor
189
+
190
+ Five laws cover how fast and how accurately users can act on the UI.
191
+
192
+ - **Fitts's Law** (Fitts, *Journal of Experimental Psychology*, 1954).
193
+ Time to acquire a target depends on its distance and size — bigger
194
+ and closer is faster. Spacing between adjacent hit zones matters as
195
+ much as size. Pair with `accessibility-baseline.md`'s 24 × 24 CSS px
196
+ AA touch-target floor; on mobile, place high-frequency controls in
197
+ the natural thumb arc.
198
+ - **Doherty Threshold** (Doherty & Thadani, *IBM Systems Journal*,
199
+ 1982). Sub-second feedback keeps users in flow; latency above ~1 s
200
+ breaks attention. The implementable directive lives in
201
+ `state-coverage.md`'s loading-threshold table (no indicator under
202
+ 300 ms; skeleton 300 ms – 2 s; labelled spinner 2 – 10 s; determinate
203
+ bar with cancel 10 – 60 s; stop and offer error/retry past 60 s).
204
+ This entry exists to name the underlying law and flag its folklore
205
+ (the 400 ms number doesn't appear in the 1982 paper — see
206
+ `animation-discipline.md` for the 400 ms folklore trace).
207
+ - **Flow** (Csíkszentmihályi, *Beyond Boredom and Anxiety*, 1975). Flow
208
+ sits in the balance between challenge and skill — too hard breeds
209
+ frustration, too easy breeds boredom. Continuous feedback and a clear
210
+ sense of control keep the user inside the state. System friction and
211
+ latency are the fastest ways to break it.
212
+ - **Goal-Gradient Effect** (Hull, *Psychological Review*, 1932; Kivetz,
213
+ Urminsky, Zheng, 2006 for the punch-card replication). Motivation to
214
+ finish rises as the goal gets closer. Multi-step flows render with a
215
+ prominent progress indicator that reflects *real* endowed progress —
216
+ show completed prerequisites when they truly exist (saved profile,
217
+ imported team, prior survey answer). When no real prerequisite
218
+ exists, render the current step honestly as `1 of N` with the
219
+ empty/current-step state clearly marked. Hull's hypothesis is
220
+ descriptive; treating it as license for fabricated progress, streak
221
+ dark patterns, or loyalty-program quota inflation is a misread.
222
+ - **Postel's Law / Robustness Principle** (Postel, RFC 760, 1980). "Be
223
+ liberal in what you accept, conservative in what you send." Take
224
+ input in whatever shape users naturally give it (phone numbers with
225
+ or without dashes, dates in mixed formats, percentages with or
226
+ without `%`); normalize internally to a canonical form; emit one
227
+ consistent format on output. The error-timing and ARIA-wiring half
228
+ lives in `form-validation.md`; the input-tolerance half is the
229
+ directive above. RFC 9413 (Thomson, IAB, 2023; built on the earlier
230
+ `draft-iab-protocol-maintenance`) retracts the maxim for protocol
231
+ design citing security surface; the UX-input application stands.
232
+
233
+ ## Behavior and expectation
234
+
235
+ Five laws cover what users predict and how that prediction interacts
236
+ with the rendered surface.
237
+
238
+ - **Jakob's Law** (Nielsen, Nielsen Norman Group, 2000). Users spend
239
+ most of their time on other sites and expect yours to work the same.
240
+ Reuse category convention — nav placement, cart icon, settings gear,
241
+ primary CTA in the upper right of a SaaS landing — so the user spends
242
+ zero cycles relearning interaction grammar. Novelty must earn its
243
+ keep against the convention's ROI; "innovate everywhere" is the
244
+ opposite failure mode.
245
+ - **Mental Model** (Craik, *The Nature of Explanation*, 1943; Norman,
246
+ *POET / The Psychology of Everyday Things*, 1988). Every user
247
+ arrives with a prior built from competitor products and the physical
248
+ world. When the prediction holds, the product feels intuitive; when
249
+ it breaks, friction shows up as confusion, not curiosity. When the
250
+ brief names a reference product, anchor explicitly — capture the
251
+ reference in the prompt and the agent inherits a transferable
252
+ interaction grammar.
253
+ - **Paradox of the Active User** (Carroll & Rosson, in *Interfacing
254
+ Thought: Cognitive Aspects of Human-Computer Interaction*, MIT Press,
255
+ 1987, pp. 80–111). Users skip the manual and start using the
256
+ software immediately, even when reading it would speed them up. Bake
257
+ guidance into the surface itself — empty-state coaching, inline
258
+ tooltips, contextual hints — at the action point.
259
+ - **Parkinson's Law** (Parkinson, *The Economist*, 1955). Work expands
260
+ to fill the time allotted to it. Loose interfaces let users dawdle;
261
+ cut friction and pre-fill what you can — autofill, smart defaults,
262
+ saved state — so a checkout finishes faster than the user expected.
263
+ Beating anticipated duration becomes the felt win.
264
+ - **Cognitive Load** (Sweller, *Cognitive Science*, 1988). Total
265
+ mental effort splits into intrinsic (the task's inherent difficulty)
266
+ and extraneous (poor layout, jargon, inconsistent patterns, visual
267
+ noise). Designers can't reduce intrinsic load; they own extraneous
268
+ fully. The visual-restraint directives (single accent in `color.md`,
269
+ three-weight typography rhythm in `typography.md`, P0 anti-default
270
+ list in `anti-ai-slop.md`) already constrain extraneous load; this
271
+ entry exists to name the cognitive cost the sibling rules reduce.
272
+
273
+ ## Common mistakes (lint these)
274
+
275
+ Folklore corrections that don't survive a primary-source check, in the
276
+ same vein as the busts already documented in `animation-discipline.md`
277
+ and `accessibility-baseline.md`. The first three are attribution
278
+ corrections (year / venue / institution) the body entries already
279
+ applied — restated here so a reviewer reading just this section sees
280
+ them. The remainder are folklore not addressed in the body.
281
+
282
+ - "Anchoring effect = Tversky & Kahneman 1972." Wrong year. The
283
+ *Science* paper introducing the anchoring framing is 1974; the 1972
284
+ paper is "Subjective probability: A judgment of representativeness"
285
+ (different work).
286
+ - "Tesler's Law was developed at Xerox PARC." Tesler left PARC for
287
+ Apple in 1980; the Conservation-of-Complexity formulation traces to
288
+ his Apple years.
289
+ - "Paradox of the Active User was a CACM article." It's a chapter in
290
+ Carroll's *Interfacing Thought* (MIT Press, 1987), not CACM.
291
+ - "Selective Attention = solved by red dots and badges." Repeated
292
+ attention-grabbers train banner blindness. Reserve the strongest
293
+ contrast for one goal-relevant action per surface.
294
+ - "Fitts's Law alone is enough for touch targets." Fitts gives the
295
+ speed-accuracy tradeoff. WCAG 2.2 SC 2.5.8 sets the AA floor at
296
+ 24 × 24 CSS px (`accessibility-baseline.md`); iOS HIG suggests
297
+ 44 × 44 pt; Material 3 suggests 48 × 48 dp. Fitts plus the
298
+ platform floor — never just Fitts.
@@ -0,0 +1,188 @@
1
+ <!-- Vendored from nexu-io/open-design@78d5637cb49d (craft/rtl-and-bidi.md), Apache-2.0, Copyright 2026 Open Design contributors. Modified by Pugi: this header line added; the text below is otherwise verbatim. Paths it names (apps/daemon/src/lint-artifact.ts, pnpm lint:craft, od.craft.requires) are upstream's, not Pugi's: the Pugi port of the linter is packages/opencode/src/session/design-lint.ts. See NOTICE.md in the skill root. -->
2
+
3
+ # RTL and bidirectional craft rules
4
+
5
+ Universal rules for right-to-left layout and bidirectional text. The
6
+ active `DESIGN.md` decides brand visual language; this file decides
7
+ how that language behaves when the script reads from the right or
8
+ mixes direction within a line.
9
+
10
+ > Grounded in primary sources: Unicode UAX #9 revision 51 (Sept 2025)
11
+ > + Unicode 17.0, CSS Logical Properties Level 1, HTML Living Standard
12
+ > (`dir`, `<bdi>`), Tailwind v4.0/v4.2 changelogs, W3C alreq,
13
+ > Material 3 RTL guidance, Apple HIG internationalization.
14
+
15
+ ## Base direction and language
16
+
17
+ Every full-page RTL artifact needs `<html dir="rtl" lang="ar">` (or
18
+ the matching `lang` for Hebrew, Persian, Urdu). The `lang` attribute
19
+ drives font-stack selection, hyphenation, locale-aware speech
20
+ synthesis, and search-engine indexing — `dir` alone isn't enough.
21
+ Three patterns cover the common cases:
22
+
23
+ - **Full-page RTL.** `<html dir="rtl" lang="ar">`. Everything inside inherits.
24
+ - **Mixed-language subtree.** Nest `<section dir="ltr" lang="en">…</section>` (or vice versa) when an embedded block uses a different script. Code samples, English citations, foreign brand names.
25
+ - **User-generated content of unknown direction.** `dir="auto"` on the paragraph. The browser resolves direction from the first strong directional character in the run.
26
+
27
+ Setting `lang` without `dir` is fine **at the document root in a
28
+ default-LTR page** — English doesn't need `dir="ltr"` there because
29
+ the bidi base direction is already LTR. Inside any opposite-direction
30
+ ancestor, `lang` does not reset the inherited base direction, so set
31
+ both `lang` and `dir` on the subtree (`<section dir="ltr" lang="en">`).
32
+ Setting `dir` without `lang` is rarely correct — at minimum drop the
33
+ appropriate ISO-639 tag in.
34
+
35
+ ## Logical properties first
36
+
37
+ Hardcoded `left` / `right` is a bug for any layout that might render
38
+ RTL. Use logical properties on the inline axis. Use them on the block
39
+ axis when the writing-mode varies; physical otherwise.
40
+
41
+ | Logical | LTR resolves to | RTL resolves to |
42
+ |---|---|---|
43
+ | `margin-inline-start` / `padding-inline-start` / `inset-inline-start` | left | right |
44
+ | `margin-inline-end` / `padding-inline-end` / `inset-inline-end` | right | left |
45
+ | `border-inline-start` | border-left | border-right |
46
+ | `border-start-start-radius` | border-top-left-radius | border-top-right-radius |
47
+ | `text-align: start` / `text-align: end` | left / right | right / left |
48
+ | `inline-size` / `block-size` | width / height | width / height |
49
+
50
+ Browser support: core inline-axis logical properties are Baseline
51
+ Widely Available (Chrome 87, Safari 14.1, Firefox 66; ≥95% global as
52
+ of 2026-05).
53
+
54
+ **Tailwind v4 changes the answer for new projects.** v4.0 (2025-01-22)
55
+ folded inline-axis logical utilities into core (`ms-*`, `me-*`, `ps-*`,
56
+ `pe-*`, `start-*`, `end-*`). v4.2 (2026-02-18) added the block-axis
57
+ set (`mbs-*`, `mbe-*`, `pbs-*`, `pbe-*`) and renamed the inset
58
+ utilities: `start-*` / `end-*` are deprecated (still work) in favor
59
+ of `inset-s-*` / `inset-e-*`. The `tailwindcss-rtl` plugin is obsolete.
60
+ Don't write `[dir="rtl"]:` overrides for spacing on Tailwind v4.
61
+
62
+ ## Bidirectional text
63
+
64
+ UAX #9 rev 51 (Sept 2025) is a version stamp for Unicode 17.0. No
65
+ algorithm change; `max_depth = 125` is permanently locked forward.
66
+
67
+ UAX #9 defines two distinct families of bidi formatting characters
68
+ that solve different problems:
69
+
70
+ - **Isolate controls** (modern, prefer these): U+2066 LRI, U+2067 RLI, U+2068 FSI — opened with these, all closed with U+2069 PDI. An isolated run does not affect, and is not affected by, the surrounding paragraph's bidi resolution. Use FSI when the embedded run's direction is unknown ahead of time.
71
+ - **Embedding / override controls** (legacy): U+202A LRE, U+202B RLE, U+202D LRO, U+202E RLO — all closed with U+202C PDF. These nest within the surrounding paragraph rather than isolating from it; LRO/RLO additionally force a direction onto neutral characters. Newer code should use isolates; touch embeddings only when interoperating with text from systems that emit them.
72
+
73
+ **Use `<bdi>` in HTML; in plain text, pick the isolate that matches
74
+ what you know about the run.** UAX #9 §2.7: *"where available, markup
75
+ should be used instead of the explicit formatting characters."*
76
+ `<bdi>` has been Baseline Widely Available since January 2020.
77
+ Reach for control characters only in plain-text contexts (logs,
78
+ plain-text emails, terminal output). When you do:
79
+
80
+ - **LRI U+2066 + PDI U+2069** for known-LTR runs (English name in an Arabic paragraph, code-style identifiers, phone numbers).
81
+ - **RLI U+2067 + PDI U+2069** for known-RTL runs (Arabic name in an English paragraph).
82
+ - **FSI U+2068 + PDI U+2069** for unknown direction (UGC where the author and language can vary).
83
+
84
+ Don't reach for FSI as the default — it auto-detects from the first
85
+ strong character, which is the wrong choice when you already know
86
+ what direction the run should be.
87
+
88
+ `dir="auto"` on a paragraph or `<bdi>` lets the browser detect
89
+ direction from the first strong directional character. Best for
90
+ user-generated content where direction isn't known at author time.
91
+
92
+ ## What mirrors and what doesn't
93
+
94
+ Mirroring isn't universal. The rules below are unanimous across
95
+ Material 3 RTL guidance and Apple HIG internationalization.
96
+
97
+ **Must mirror:**
98
+
99
+ - Directional arrows (back / forward / next / previous), navigation rail position, tab order, calendar-grid weekday order.
100
+ - Slider fill direction and **non-media** progress-bar fill (a download progress bar, a form-completion bar, an upload status). Media scrubbers stay LTR — see the Media row below.
101
+ - Checkbox-and-label position. Label sits to the right in LTR, to the left in RTL.
102
+ - Phone-number and IBAN affordances when the surrounding paragraph is RTL but the value itself is LTR — wrap the value in `<bdi dir="ltr">` (or `<span dir="ltr">`) so the digits don't reflow. Bare `<bdi>` is not enough: phone numbers and account numbers contain mostly weak / neutral characters, so first-strong direction detection is unreliable. Force LTR explicitly.
103
+
104
+ **Must not mirror:**
105
+
106
+ - Clock faces. Clockwise is universal.
107
+ - Circular refresh / sync / reload icons. Same reason.
108
+ - Media playback controls (play / pause / fast-forward / rewind) **and the media scrubber / progress timeline**. They represent tape direction, not reading direction.
109
+ - Charts and graphs. X-axis stays mathematical, not linguistic.
110
+ - Photographs, brand logos, physical-object icons (camera, keyboard, headphones). Identity over direction.
111
+
112
+ **Numerals are not a mirroring decision.** They follow locale, not
113
+ paragraph direction. Arabic-Indic digits carry bidi class **AN**, not
114
+ EN — affects how they sit inside mixed-direction lines but does not
115
+ flip them.
116
+
117
+ **Single live conflict between platforms:** the search icon. SF Symbols
118
+ ships an RTL `magnifyingglass` variant (Apple flips it). Material 3
119
+ says don't flip the magnifying glass (handle stays bottom-right).
120
+ Decide per-platform; don't synthesize a single rule.
121
+
122
+ ## Typography rules anchored here
123
+
124
+ Two RTL-coupled typography rules sit in this file because they cause
125
+ breakage at the layout level. The full Arabic / Hebrew typography
126
+ guide (font picks, harakat line-height, OpenType shaping, mixed-script
127
+ fallback chains) belongs in a future `craft/arabic-hebrew-typography.md`.
128
+
129
+ - **Never apply CSS `letter-spacing` to Arabic runs.** alreq treats
130
+ letter-spacing as a boundary concept, not a uniform tracking value.
131
+ Applying tracking breaks the cursive joining the script depends on.
132
+ - **Body type for Arabic runs ~14-18 px with line-height 1.5-1.75** to
133
+ give harakat (diacritics) clearance. Latin defaults are too tight.
134
+
135
+ ## Native mobile RTL parity
136
+
137
+ Web RTL handling does not auto-translate to mobile. Each platform has
138
+ its own direction primitive. Skills that emit web-only artifacts can
139
+ skim this section; it's the entry point for skills that ship to
140
+ mobile (mobile-onboarding, mobile-app, etc.).
141
+
142
+ | Platform | Direction primitive | Spacing |
143
+ |---|---|---|
144
+ | iOS UIKit | `semanticContentAttribute = .forceRightToLeft` | `NSDirectionalEdgeInsets` |
145
+ | iOS SwiftUI | `.environment(\.layoutDirection, .rightToLeft)` | `EdgeInsets` with `leading` / `trailing` |
146
+ | Android Compose | `CompositionLocalProvider(LocalLayoutDirection provides LayoutDirection.Rtl)` | `PaddingValues` accepts start / end |
147
+ | Flutter | `Directionality(textDirection: TextDirection.rtl)` | `EdgeInsetsDirectional.fromSTEB(...)` |
148
+ | React Native | `I18nManager.forceRTL(true)` (requires native reload; no `forceLTR` parity, no `react-native-web` support) | `marginStart` / `marginEnd` |
149
+
150
+ The rule across all platforms: prefer the directional primitive over
151
+ the absolute one. `EdgeInsets.left/right` in Flutter, `paddingLeft` /
152
+ `paddingRight` in Android, leading-vs-trailing in iOS — these are bugs
153
+ waiting for an Arabic deployment.
154
+
155
+ ## Forms in RTL
156
+
157
+ Form fields commonly mix scripts. Three rules cover most of it.
158
+
159
+ - **`<input dir="auto">`** for any field whose value's direction is uncertain (search boxes, comment fields, free-text inputs). The browser detects from the first strong directional character.
160
+ - **Force LTR on intrinsically-LTR fields** even inside an RTL paragraph: email, URL, phone, IBAN, credit-card. `<input type="email" dir="ltr">`.
161
+ - **Wrap rendered values in `<bdi>`** when displaying mixed-script content (a username inside a paragraph, a model number inside a description). Stops the surrounding direction from rearranging the embedded value. For values whose direction is fixed and weak-character-heavy (phone, IBAN, card number), use `<bdi dir="ltr">` rather than bare `<bdi>` so first-strong detection doesn't misclassify.
162
+
163
+ ## Common mistakes (lint these)
164
+
165
+ Mechanically lintable items can be flagged from CSS / source alone.
166
+ Script-aware items need to detect Arabic / Hebrew runs in the
167
+ rendered text and have legitimate exceptions (chart axes, physical
168
+ icons, platform-specific placement).
169
+
170
+ **Mechanically lintable:**
171
+
172
+ - Hardcoded `left` / `right` / `text-align: left` in new CSS — bug for any layout that may render RTL. Exceptions: chart x-axes, physical-object icons, platform-pinned UI like a status-bar clock. Lint with an allow-list rather than blanket banning.
173
+ - "Tailwind v4.2 logical-utility rename is `inline-s-*` / `inline-e-*`" — wrong family. Those are size utilities. The inset rename is `inset-s-*` / `inset-e-*`.
174
+ - "WebKit doesn't support U+2066-U+2069" — wrong, they're interoperable across modern browsers. The "still missing" claim traces to a stale 2015 W3C test snapshot.
175
+ - Setting `dir="rtl"` without `lang="ar"` (or matching). Lint together; `dir` alone misses the font-stack and locale path.
176
+ - Flutter `EdgeInsets.left/right` in code that needs to render RTL. Use `EdgeInsetsDirectional.start/end`.
177
+
178
+ **Needs script detection (will false-positive without it):**
179
+
180
+ - "Use `text-justify: kashida` for Arabic" — no browser implements it. CSS `text-align: justify` adds inter-word spacing and looks unnatural in Arabic; kashida elongation is the correct form, but it isn't shippable on the web today.
181
+ - Italics on Arabic or Hebrew text. Neither script has an italic tradition.
182
+ - CSS `letter-spacing` applied to Arabic. Breaks cursive joining (alreq treats it as a boundary concept, not a uniform tracking value).
183
+ - Lorem Ipsum used for RTL prototyping. Arabic word lengths, connection behaviors, and vertical extents differ; use real Arabic / Hebrew text.
184
+
185
+ **HTML semantics:**
186
+
187
+ - Reaching for CSS bidi controls (`unicode-bidi: isolate` / `plaintext` / `embed`) for inline runs when `<bdi>` or a `dir`-bearing element does the job. Prefer semantic isolation in HTML for inline content; `unicode-bidi: plaintext` operates on a different surface (it changes how base direction is determined for each plaintext paragraph in a block) and should only be used when that block-level paragraph behavior is explicitly required and tested. The two are not drop-in equivalents — don't lint one as a replacement for the other.
188
+ - Bare `<bdi>` around phone / IBAN / card numbers in an RTL paragraph. First-strong detection on weak/neutral characters is unreliable; force `dir="ltr"` explicitly.
@@ -0,0 +1,136 @@
1
+ <!-- Vendored from nexu-io/open-design@78d5637cb49d (craft/state-coverage.md), Apache-2.0, Copyright 2026 Open Design contributors. Modified by Pugi: this header line added; the text below is otherwise verbatim. Paths it names (apps/daemon/src/lint-artifact.ts, pnpm lint:craft, od.craft.requires) are upstream's, not Pugi's: the Pugi port of the linter is packages/opencode/src/session/design-lint.ts. See NOTICE.md in the skill root. -->
2
+
3
+ # State coverage craft rules
4
+
5
+ Universal rules for what every interactive surface must render. The active
6
+ `DESIGN.md` decides how each state looks; this file decides which states must
7
+ exist and what they must contain. The single most reliable AI-design failure
8
+ is shipping only the populated state.
9
+
10
+ > Distilled from WCAG 2.2, NN/g, Material Design 3, Apple HIG, and Baymard
11
+ > Institute checkout research.
12
+
13
+ ## The five required states
14
+
15
+ Every surface that fetches, transforms, or accepts data must render all five.
16
+
17
+ | State | Triggered when | Must contain |
18
+ |---|---|---|
19
+ | **Loading** | Data is in flight | Skeleton, spinner, or shell — plus a 15 s "taking longer than expected" fallback |
20
+ | **Empty** | No records yet, or query returned nothing | Headline, plain explanation, primary CTA |
21
+ | **Error** | Fetch failed, server failure, validation rejection | Plain-language cause, recovery action, preserved user input |
22
+ | **Populated** | Data present, primary case | The state the design was actually drawn for |
23
+ | **Edge** | Extreme volume, long strings, missing optional fields, RTL or long-word content, partial network | Layout that does not break |
24
+
25
+ Render-and-screenshot test: every list, table, card, form, and panel in the
26
+ artifact has all five. Missing states are the most common silent failure of
27
+ AI-generated UI.
28
+
29
+ **Test matrix.** Concrete edge scenarios the surface must survive:
30
+
31
+ | Skill type | Edge scenario |
32
+ |---|---|
33
+ | Dashboard / table | 10,000+ rows, all numeric columns, sort + filter applied |
34
+ | Mobile card / list | 200-char title, missing avatar, missing secondary CTA |
35
+ | Form | All optional fields empty, all required fields at max length |
36
+ | Search results | Single-character query, query with only special chars, 1,000+ result count |
37
+ | Detail view | Missing all optional metadata, RTL primary content with LTR embeds |
38
+
39
+ ## Form-specific states
40
+
41
+ Forms add three states on top of the five.
42
+
43
+ | State | Triggered when | Behavior |
44
+ |---|---|---|
45
+ | **Untouched** | Field has not yet had focus | Default styling; no validation messages |
46
+ | **Dirty (valid)** | User typed and field passes validation | Persistent helper text remains; no success-coloring |
47
+ | **Submitted-pending** | Submit clicked, awaiting server | Submit button enters loading state; fields lock against re-submission |
48
+
49
+ Validation timing: validate **on blur**, not on first keystroke. For password
50
+ and similar live fields, validate on each keystroke *only after the first
51
+ blur*. Remove the error message the instant input becomes valid.
52
+
53
+ ## Empty state composition
54
+
55
+ Empty is not the absence of state. It is its own state with a job.
56
+
57
+ - **First-use empty** — illustration + headline + value sentence + primary CTA. The empty is the onboarding moment.
58
+ - **No-results empty** — echo the query, suggest alternatives, never leave a true blank.
59
+ - **Cleared empty** — celebratory phrasing, optional next-action.
60
+ - **Error-as-empty** — never. An error is its own state with recovery information; do not collapse error into empty.
61
+
62
+ **Server-driven vs client-driven.** When a search or query API can return fallback content in the empty payload (suggestions, related categories, popular results), prefer that over a client-side echo. Algolia, Elastic, and most modern search backends support this — the server has more context for what "no results, but maybe try X" should mean.
63
+
64
+ ## Error state composition
65
+
66
+ Every error must answer three questions, in this order:
67
+
68
+ 1. **What happened.** "Your card was declined." Not "Something went wrong."
69
+ 2. **Why, if knowable.** "Insufficient funds." Or "Network unreachable — check your connection."
70
+ 3. **What the user can do.** A retry button, an alternative path, or a support link.
71
+
72
+ Preserve user input across the error. The form must not clear on submit
73
+ failure.
74
+
75
+ Severity tiers:
76
+
77
+ - **Field-level** — red border, inline message, focus moves to the field.
78
+ - **Form-level** — error summary banner at top + per-field markers.
79
+ - **Section-level** — inline panel with retry, surrounding sections still functional.
80
+ - **Page-level** — full error state with illustration and recovery CTA.
81
+ - **App-level** — persistent banner or modal for critical loss-of-functionality.
82
+
83
+ Match severity to surface scope. A field validation failure does not warrant
84
+ a page-level error.
85
+
86
+ **Retry discipline.** A retry surface is not a button alone. It has timing rules:
87
+
88
+ - First retry fires immediately on user click.
89
+ - Second and third retries use exponential backoff: 2 s, 4 s, 8 s max.
90
+ - After 3 failed retries, replace "Retry" with "Contact support" plus a copyable error ID. The user has done their job; the system now needs a human.
91
+ - Show "Last attempted: Xs ago" on the error surface after the first retry, so the user knows how stale the failure is.
92
+
93
+ ## Loading state thresholds
94
+
95
+ Pick the indicator by expected duration, not by what's available in the
96
+ component library.
97
+
98
+ | Duration | Indicator |
99
+ |---|---|
100
+ | 0–300 ms | None. Render synchronously; users perceive no delay. |
101
+ | 300 ms – 2 s | Subtle spinner or skeleton. |
102
+ | 2 – 10 s | Skeleton matched to expected layout, or labelled spinner ("Loading payments…"). |
103
+ | 10 – 30 s | Determinate progress bar with cancel option. |
104
+ | 30 – 60 s | Progress bar with explicit cancel affordance. The "taking longer than expected" notice already appeared at 15 s; do not repeat it. |
105
+ | 60 s+ | Stop animation. Show error with retry, cancel, or continue. |
106
+
107
+ Never leave a spinner running indefinitely. Start a timeout on every request.
108
+
109
+ ## ARIA and focus rules
110
+
111
+ State changes must be announced and focused correctly.
112
+
113
+ | Change | ARIA | Focus action |
114
+ |---|---|---|
115
+ | Inline error on submit | `role="alert"` on the message | Move focus to first error field |
116
+ | Toast / non-urgent confirmation | `role="status"` (polite live region) | Do not move focus |
117
+ | Critical error or destructive confirmation | `role="alertdialog"` (assertive) | Move focus to dialog |
118
+ | Loading begins | `role="status"` announcement ("Loading…") | Do not move focus to spinner |
119
+ | Loading ends, content appears | — | Move focus to loaded content if action was user-initiated |
120
+
121
+ Live region containers must exist in the DOM before content is injected.
122
+ Adding `aria-live` simultaneously with content does not trigger an
123
+ announcement.
124
+
125
+ ## Common mistakes (lint these)
126
+
127
+ - Surface renders only the populated state; loading, empty, error, and edge are absent.
128
+ - Empty state is a literal blank or "No data" text with no headline, explanation, or action.
129
+ - Error message reads "Something went wrong" with no cause or recovery.
130
+ - Spinner with no timeout; runs indefinitely on slow or failed requests.
131
+ - Submit clears form fields on validation failure, forcing re-entry.
132
+ - Inline validation fires on first keystroke instead of on blur.
133
+ - Full-page loading replaces the chrome when only one section is fetching.
134
+ - Toast appears at a different screen position than previous toasts in the same artifact.
135
+ - Color alone conveys error state — no icon, no text label.
136
+ - Auto-dismissing toast cannot be paused on hover or focus (WCAG SC 2.2.1).