@twoux/ui 0.0.0-stage → 0.1.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 (325) hide show
  1. package/LICENSE.md +61 -0
  2. package/LICENSES/Apache-2.0.txt +202 -0
  3. package/NOTICE.md +64 -0
  4. package/README.md +211 -2
  5. package/SHADCN-DIFF.md +228 -0
  6. package/agent/AGENTS.md +358 -0
  7. package/agent/docs-md/accessibility.md +49 -0
  8. package/agent/docs-md/action-catalogue.md +81 -0
  9. package/agent/docs-md/actions.md +74 -0
  10. package/agent/docs-md/agent-activity-log.md +70 -0
  11. package/agent/docs-md/agent-permissions.md +82 -0
  12. package/agent/docs-md/agent-run.md +90 -0
  13. package/agent/docs-md/agents-on-behalf.md +74 -0
  14. package/agent/docs-md/ai-column.md +85 -0
  15. package/agent/docs-md/ai-origin-label.md +70 -0
  16. package/agent/docs-md/announcing-features.md +73 -0
  17. package/agent/docs-md/answer-actions.md +81 -0
  18. package/agent/docs-md/app-shell.md +114 -0
  19. package/agent/docs-md/artifact-card.md +89 -0
  20. package/agent/docs-md/ask-on-screen.md +72 -0
  21. package/agent/docs-md/assistant-page.md +100 -0
  22. package/agent/docs-md/assistant-panel.md +142 -0
  23. package/agent/docs-md/avatar.md +82 -0
  24. package/agent/docs-md/banner.md +106 -0
  25. package/agent/docs-md/board.md +163 -0
  26. package/agent/docs-md/breadcrumb.md +89 -0
  27. package/agent/docs-md/breakdown-table.md +97 -0
  28. package/agent/docs-md/bulk-action-bar.md +112 -0
  29. package/agent/docs-md/button.md +293 -0
  30. package/agent/docs-md/card.md +115 -0
  31. package/agent/docs-md/change-list.md +99 -0
  32. package/agent/docs-md/change-request.md +166 -0
  33. package/agent/docs-md/chart-area.md +87 -0
  34. package/agent/docs-md/chart-bar.md +100 -0
  35. package/agent/docs-md/chart-box-plot.md +88 -0
  36. package/agent/docs-md/chart-bullet.md +89 -0
  37. package/agent/docs-md/chart-candlestick.md +112 -0
  38. package/agent/docs-md/chart-card-line.md +120 -0
  39. package/agent/docs-md/chart-donut.md +90 -0
  40. package/agent/docs-md/chart-drill-down.md +72 -0
  41. package/agent/docs-md/chart-funnel.md +71 -0
  42. package/agent/docs-md/chart-heatmap.md +76 -0
  43. package/agent/docs-md/chart-histogram.md +81 -0
  44. package/agent/docs-md/chart-map.md +165 -0
  45. package/agent/docs-md/chart-mixed.md +88 -0
  46. package/agent/docs-md/chart-price.md +92 -0
  47. package/agent/docs-md/chart-radial-bars.md +109 -0
  48. package/agent/docs-md/chart-radial-lines.md +111 -0
  49. package/agent/docs-md/chart-sankey.md +104 -0
  50. package/agent/docs-md/chart-scatter.md +87 -0
  51. package/agent/docs-md/chart-share-bar.md +89 -0
  52. package/agent/docs-md/chart-treemap.md +98 -0
  53. package/agent/docs-md/chart-waterfall.md +93 -0
  54. package/agent/docs-md/checkbox.md +131 -0
  55. package/agent/docs-md/choosing-selection-control.md +71 -0
  56. package/agent/docs-md/citations-sources.md +79 -0
  57. package/agent/docs-md/citing-sources.md +63 -0
  58. package/agent/docs-md/clarify-before-acting.md +69 -0
  59. package/agent/docs-md/clarifying-question.md +92 -0
  60. package/agent/docs-md/code-block.md +174 -0
  61. package/agent/docs-md/colours.md +87 -0
  62. package/agent/docs-md/columns-menu.md +140 -0
  63. package/agent/docs-md/combobox.md +161 -0
  64. package/agent/docs-md/command-palette.md +126 -0
  65. package/agent/docs-md/confidence-note.md +87 -0
  66. package/agent/docs-md/configurable-dashboard.md +76 -0
  67. package/agent/docs-md/confirmation-dialog.md +93 -0
  68. package/agent/docs-md/connected-tools.md +80 -0
  69. package/agent/docs-md/connector-picker.md +127 -0
  70. package/agent/docs-md/consistency.md +53 -0
  71. package/agent/docs-md/content-density.md +61 -0
  72. package/agent/docs-md/conversation-history.md +78 -0
  73. package/agent/docs-md/create-record.md +80 -0
  74. package/agent/docs-md/csv-import.md +88 -0
  75. package/agent/docs-md/dashboard-items.md +78 -0
  76. package/agent/docs-md/data-export.md +61 -0
  77. package/agent/docs-md/data-grid.md +137 -0
  78. package/agent/docs-md/data-vis-colours.md +76 -0
  79. package/agent/docs-md/data-visualisation.md +76 -0
  80. package/agent/docs-md/date-picker.md +178 -0
  81. package/agent/docs-md/delete-record.md +75 -0
  82. package/agent/docs-md/dense-table.md +495 -0
  83. package/agent/docs-md/design-tokens.md +53 -0
  84. package/agent/docs-md/disabled-read-only.md +75 -0
  85. package/agent/docs-md/drag-and-drop.md +74 -0
  86. package/agent/docs-md/dropdown-menu.md +137 -0
  87. package/agent/docs-md/edit-record.md +73 -0
  88. package/agent/docs-md/empty-loading-error.md +78 -0
  89. package/agent/docs-md/empty-state.md +128 -0
  90. package/agent/docs-md/error-messages.md +75 -0
  91. package/agent/docs-md/error-state.md +123 -0
  92. package/agent/docs-md/export-menu.md +86 -0
  93. package/agent/docs-md/feedback-prompt.md +88 -0
  94. package/agent/docs-md/field-mapping.md +78 -0
  95. package/agent/docs-md/file-drop.md +110 -0
  96. package/agent/docs-md/filter-bar.md +138 -0
  97. package/agent/docs-md/filter-panel.md +103 -0
  98. package/agent/docs-md/filtering-search.md +79 -0
  99. package/agent/docs-md/follow-ups-starters.md +79 -0
  100. package/agent/docs-md/form-validation.md +95 -0
  101. package/agent/docs-md/generated-columns.md +65 -0
  102. package/agent/docs-md/generated-drafts-files.md +71 -0
  103. package/agent/docs-md/grouped-list.md +89 -0
  104. package/agent/docs-md/guide-cue.md +170 -0
  105. package/agent/docs-md/help-panel.md +103 -0
  106. package/agent/docs-md/help-system.md +74 -0
  107. package/agent/docs-md/iconography.md +49 -0
  108. package/agent/docs-md/inbox.md +108 -0
  109. package/agent/docs-md/inline-editing-validation.md +80 -0
  110. package/agent/docs-md/inline-input-in-answer.md +70 -0
  111. package/agent/docs-md/items-palette.md +116 -0
  112. package/agent/docs-md/keyboard-model.md +47 -0
  113. package/agent/docs-md/keyboard-navigation.md +68 -0
  114. package/agent/docs-md/keyboard-shortcut-hint.md +87 -0
  115. package/agent/docs-md/keyboard-shortcut-sheet.md +90 -0
  116. package/agent/docs-md/labelling-ai-output.md +66 -0
  117. package/agent/docs-md/layout.md +47 -0
  118. package/agent/docs-md/list-table.md +254 -0
  119. package/agent/docs-md/loading-skeleton.md +91 -0
  120. package/agent/docs-md/managing-agents.md +71 -0
  121. package/agent/docs-md/message-composer.md +105 -0
  122. package/agent/docs-md/migrating.md +49 -0
  123. package/agent/docs-md/motion.md +93 -0
  124. package/agent/docs-md/navigation.md +76 -0
  125. package/agent/docs-md/notification-channels.md +65 -0
  126. package/agent/docs-md/notifications-decision.md +100 -0
  127. package/agent/docs-md/onboarding-step.md +114 -0
  128. package/agent/docs-md/order-book.md +108 -0
  129. package/agent/docs-md/page-header.md +96 -0
  130. package/agent/docs-md/pagination.md +88 -0
  131. package/agent/docs-md/pane-layout.md +105 -0
  132. package/agent/docs-md/period-comparison.md +66 -0
  133. package/agent/docs-md/plan-card.md +120 -0
  134. package/agent/docs-md/popover-rules.md +88 -0
  135. package/agent/docs-md/prompt-editor.md +90 -0
  136. package/agent/docs-md/propose-apply.md +65 -0
  137. package/agent/docs-md/provider-logo.md +97 -0
  138. package/agent/docs-md/radio.md +83 -0
  139. package/agent/docs-md/ranked-list.md +102 -0
  140. package/agent/docs-md/reasoning-progress.md +72 -0
  141. package/agent/docs-md/record-detail.md +71 -0
  142. package/agent/docs-md/regenerating-answers.md +70 -0
  143. package/agent/docs-md/run-status-bar.md +94 -0
  144. package/agent/docs-md/saved-tasks-schedules.md +63 -0
  145. package/agent/docs-md/saved-views.md +149 -0
  146. package/agent/docs-md/saving-views.md +68 -0
  147. package/agent/docs-md/scheduler.md +258 -0
  148. package/agent/docs-md/secondary-panels.md +72 -0
  149. package/agent/docs-md/segmented-control.md +88 -0
  150. package/agent/docs-md/selection-bulk-actions.md +70 -0
  151. package/agent/docs-md/separator.md +75 -0
  152. package/agent/docs-md/setup-checklist.md +92 -0
  153. package/agent/docs-md/shape-elevation.md +55 -0
  154. package/agent/docs-md/sheet-grid.md +211 -0
  155. package/agent/docs-md/side-panel.md +134 -0
  156. package/agent/docs-md/sign-in.md +114 -0
  157. package/agent/docs-md/spacing.md +55 -0
  158. package/agent/docs-md/split-button.md +100 -0
  159. package/agent/docs-md/stat-compare.md +82 -0
  160. package/agent/docs-md/static-dashboard.md +75 -0
  161. package/agent/docs-md/status-badge.md +140 -0
  162. package/agent/docs-md/step-card.md +75 -0
  163. package/agent/docs-md/stepper.md +92 -0
  164. package/agent/docs-md/suggested-changes.md +91 -0
  165. package/agent/docs-md/switch.md +78 -0
  166. package/agent/docs-md/table-settings.md +81 -0
  167. package/agent/docs-md/tabs.md +85 -0
  168. package/agent/docs-md/tag-input.md +108 -0
  169. package/agent/docs-md/tag.md +79 -0
  170. package/agent/docs-md/task-builder.md +87 -0
  171. package/agent/docs-md/team-instructions.md +72 -0
  172. package/agent/docs-md/templates-gallery.md +85 -0
  173. package/agent/docs-md/text-input.md +160 -0
  174. package/agent/docs-md/thinking-steps.md +89 -0
  175. package/agent/docs-md/timeline.md +189 -0
  176. package/agent/docs-md/timestamp.md +84 -0
  177. package/agent/docs-md/timestamps.md +70 -0
  178. package/agent/docs-md/toast.md +117 -0
  179. package/agent/docs-md/tooltip.md +90 -0
  180. package/agent/docs-md/typography.md +62 -0
  181. package/agent/docs-md/undo-destructive.md +66 -0
  182. package/agent/docs-md/unsaved-changes.md +71 -0
  183. package/agent/docs-md/usage-meter.md +80 -0
  184. package/agent/docs-md/user-feedback.md +71 -0
  185. package/agent/docs-md/voice-and-tone.md +60 -0
  186. package/agent/docs-md/why-choice.md +93 -0
  187. package/agent/docs-md/workspace-onboarding.md +74 -0
  188. package/agent/docs-md/writing.md +48 -0
  189. package/agent/llms-full.txt +18369 -0
  190. package/agent/llms.txt +198 -0
  191. package/agent/skills/twoux-audit/SKILL.md +49 -0
  192. package/agent/skills/twoux-build/SKILL.md +39 -0
  193. package/agent/skills/twoux-design/SKILL.md +54 -0
  194. package/agent/skills/twoux-write/SKILL.md +32 -0
  195. package/bin/check.mjs +123 -0
  196. package/bin/docs.mjs +24 -0
  197. package/bin/guard.mjs +27 -0
  198. package/bin/init.mjs +113 -0
  199. package/bin/knowledge.mjs +87 -0
  200. package/bin/mcp.mjs +116 -0
  201. package/bin/theme.mjs +168 -0
  202. package/bin/twoux.mjs +25 -0
  203. package/package.json +80 -3
  204. package/registry/jobs.json +1028 -0
  205. package/registry/palette.json +770 -0
  206. package/registry/rules.json +913 -0
  207. package/src/components/agent-review.tsx +364 -0
  208. package/src/components/ai-basics.tsx +212 -0
  209. package/src/components/ai-work.tsx +304 -0
  210. package/src/components/ai.tsx +227 -0
  211. package/src/components/alert-dialog.tsx +203 -0
  212. package/src/components/alert.tsx +86 -0
  213. package/src/components/avatar.tsx +114 -0
  214. package/src/components/badge.tsx +59 -0
  215. package/src/components/board.tsx +397 -0
  216. package/src/components/breadcrumb.tsx +114 -0
  217. package/src/components/button-group.tsx +90 -0
  218. package/src/components/button.tsx +109 -0
  219. package/src/components/calendar.tsx +247 -0
  220. package/src/components/card.tsx +102 -0
  221. package/src/components/chart-map.tsx +336 -0
  222. package/src/components/charts-more.tsx +1331 -0
  223. package/src/components/charts-radial.tsx +328 -0
  224. package/src/components/charts.tsx +390 -0
  225. package/src/components/checkbox.tsx +40 -0
  226. package/src/components/code.tsx +139 -0
  227. package/src/components/collapsible.tsx +37 -0
  228. package/src/components/combobox.tsx +339 -0
  229. package/src/components/command-extras.tsx +11 -0
  230. package/src/components/command.tsx +196 -0
  231. package/src/components/data-grid.tsx +213 -0
  232. package/src/components/data-table.tsx +334 -0
  233. package/src/components/data-views.tsx +740 -0
  234. package/src/components/date-picker.tsx +686 -0
  235. package/src/components/dense-table.tsx +207 -0
  236. package/src/components/dialog.tsx +162 -0
  237. package/src/components/dropdown-menu.tsx +264 -0
  238. package/src/components/empty.tsx +114 -0
  239. package/src/components/feedback-prompt.tsx +80 -0
  240. package/src/components/field.tsx +254 -0
  241. package/src/components/fields.tsx +112 -0
  242. package/src/components/flow.tsx +840 -0
  243. package/src/components/guide-cue.tsx +273 -0
  244. package/src/components/inbox.tsx +113 -0
  245. package/src/components/input-group.tsx +176 -0
  246. package/src/components/input.tsx +26 -0
  247. package/src/components/kbd.tsx +34 -0
  248. package/src/components/label.tsx +27 -0
  249. package/src/components/labels.tsx +176 -0
  250. package/src/components/motion.tsx +136 -0
  251. package/src/components/onboarding.tsx +454 -0
  252. package/src/components/order-book.tsx +185 -0
  253. package/src/components/pane-layout.tsx +417 -0
  254. package/src/components/popover.tsx +96 -0
  255. package/src/components/provider-logos.data.ts +49 -0
  256. package/src/components/radio-group.tsx +52 -0
  257. package/src/components/scheduler.tsx +997 -0
  258. package/src/components/segmented-control.tsx +28 -0
  259. package/src/components/select.tsx +195 -0
  260. package/src/components/separator.tsx +31 -0
  261. package/src/components/sheet.tsx +148 -0
  262. package/src/components/shell.tsx +426 -0
  263. package/src/components/shortcut-sheet.tsx +83 -0
  264. package/src/components/skeleton.tsx +18 -0
  265. package/src/components/sonner.tsx +107 -0
  266. package/src/components/spinner.tsx +13 -0
  267. package/src/components/split-button.tsx +58 -0
  268. package/src/components/stepper.tsx +78 -0
  269. package/src/components/switch.tsx +43 -0
  270. package/src/components/table.tsx +126 -0
  271. package/src/components/tabs.tsx +118 -0
  272. package/src/components/tag-input.tsx +253 -0
  273. package/src/components/textarea.tsx +21 -0
  274. package/src/components/theme.tsx +80 -0
  275. package/src/components/timeline.tsx +408 -0
  276. package/src/components/toggle-group.tsx +87 -0
  277. package/src/components/toggle.tsx +54 -0
  278. package/src/components/tooltip.tsx +65 -0
  279. package/src/components/workspace-actions.tsx +294 -0
  280. package/src/components/workspace-import.tsx +465 -0
  281. package/src/components/workspace.tsx +1884 -0
  282. package/src/index.ts +80 -0
  283. package/src/lib/active-indicator.tsx +53 -0
  284. package/src/lib/chart-kit.tsx +72 -0
  285. package/src/lib/cn.ts +6 -0
  286. package/src/lib/shortcuts.tsx +61 -0
  287. package/src/lib/workspace.tsx +10 -0
  288. package/src/shadcn/alert-dialog.tsx +196 -0
  289. package/src/shadcn/alert.tsx +65 -0
  290. package/src/shadcn/avatar.tsx +108 -0
  291. package/src/shadcn/badge.tsx +47 -0
  292. package/src/shadcn/breadcrumb.tsx +108 -0
  293. package/src/shadcn/button-group.tsx +83 -0
  294. package/src/shadcn/button.tsx +63 -0
  295. package/src/shadcn/calendar.tsx +220 -0
  296. package/src/shadcn/card.tsx +91 -0
  297. package/src/shadcn/checkbox.tsx +31 -0
  298. package/src/shadcn/collapsible.tsx +33 -0
  299. package/src/shadcn/command.tsx +184 -0
  300. package/src/shadcn/dialog.tsx +158 -0
  301. package/src/shadcn/dropdown-menu.tsx +256 -0
  302. package/src/shadcn/empty.tsx +103 -0
  303. package/src/shadcn/field.tsx +248 -0
  304. package/src/shadcn/input-group.tsx +170 -0
  305. package/src/shadcn/input.tsx +20 -0
  306. package/src/shadcn/kbd.tsx +28 -0
  307. package/src/shadcn/label.tsx +23 -0
  308. package/src/shadcn/popover.tsx +88 -0
  309. package/src/shadcn/radio-group.tsx +44 -0
  310. package/src/shadcn/select.tsx +189 -0
  311. package/src/shadcn/separator.tsx +27 -0
  312. package/src/shadcn/sheet.tsx +142 -0
  313. package/src/shadcn/skeleton.tsx +13 -0
  314. package/src/shadcn/sonner.tsx +40 -0
  315. package/src/shadcn/switch.tsx +34 -0
  316. package/src/shadcn/table.tsx +115 -0
  317. package/src/shadcn/tabs.tsx +90 -0
  318. package/src/shadcn/textarea.tsx +17 -0
  319. package/src/shadcn/toggle-group.tsx +83 -0
  320. package/src/shadcn/toggle.tsx +46 -0
  321. package/src/shadcn/tooltip.tsx +56 -0
  322. package/src/styles/palette.css +127 -0
  323. package/src/styles/theme.css +35 -0
  324. package/src/styles/twoux.css +236 -0
  325. package/theme.json +14 -0
@@ -0,0 +1,913 @@
1
+ {
2
+ "$comment": "Source of truth for the rules every builder follows, human or AI. Read by the docs site (/for-agents), AGENTS.md and llms.txt. Edit here only.",
3
+ "before": [
4
+ {
5
+ "rule": "Find the job in the job table. Use the component it names, and read that component's page, including “When not to use”.",
6
+ "why": "Same job, same component, everywhere."
7
+ },
8
+ {
9
+ "rule": "If a pattern covers the flow (filtering, bulk actions, propose then apply, record detail…), follow its steps in order.",
10
+ "why": "Patterns keep whole flows consistent, not just parts."
11
+ },
12
+ {
13
+ "rule": "If the component is planned or missing, do not build a local version, a lookalike or a partial one. Use the closest existing TwoUX component and tell the person what is missing.",
14
+ "why": "A local copy is how drift starts, and it never gets the fixes the real component does."
15
+ },
16
+ {
17
+ "rule": "When you suggest a new component, layout or flow, base it on the patterns here, WAI-ARIA APG and WCAG 2.2. Use the closest existing component meanwhile.",
18
+ "why": "Suggestions grounded in tested patterns and standards beat taste."
19
+ },
20
+ {
21
+ "rule": "Before you finish, run `npx twoux check` and fix every error it reports.",
22
+ "why": "The check catches raw colours, one-off sizes, direct primitive imports, local copies and edits to TwoUX itself."
23
+ }
24
+ ],
25
+ "groups": [
26
+ {
27
+ "title": "Code",
28
+ "rules": [
29
+ {
30
+ "rule": "Import components from the TwoUX package. Never copy, restyle or rebuild a primitive locally.",
31
+ "why": "One implementation means one behaviour and one accessibility fix for everyone.",
32
+ "use": [
33
+ [
34
+ "Package",
35
+ [
36
+ "@twoux/ui"
37
+ ]
38
+ ]
39
+ ],
40
+ "scope": "twoux"
41
+ },
42
+ {
43
+ "rule": "Use shadcn's component names and props exactly. TwoUX only adds a few props, such as loading and shortcut on Button, and the warning variant.",
44
+ "why": "TwoUX is generated from shadcn's source, so shadcn knowledge applies directly and agents do not invent variants.",
45
+ "use": [
46
+ [
47
+ "Button variants",
48
+ [
49
+ "default",
50
+ "outline",
51
+ "secondary",
52
+ "ghost",
53
+ "warning",
54
+ "destructive",
55
+ "link"
56
+ ]
57
+ ],
58
+ [
59
+ "Button sizes",
60
+ [
61
+ "xs",
62
+ "sm",
63
+ "default",
64
+ "lg",
65
+ "icon",
66
+ "icon-sm",
67
+ "icon-xs"
68
+ ]
69
+ ]
70
+ ],
71
+ "scope": "twoux"
72
+ },
73
+ {
74
+ "rule": "Support light and dark by using role tokens only (bg-surface, text-ink-2, border-line, bg-ok-bg…). Never write dark: variants, a second set of classes, or a colour that only works on white. Light is the default; offer ThemeSwitcher (Light, Dark, Match system) in the header or account menu, and add THEME_SCRIPT to <head> so the page doesn't flash.",
75
+ "why": "Each role holds a light and a dark value (CSS light-dark()), chosen and contrast-checked per theme, so code written once works in both. Charts use their own dark selection, not an inverted one.",
76
+ "use": [
77
+ [
78
+ "Switch",
79
+ [
80
+ "ThemeSwitcher",
81
+ "useTheme",
82
+ "setTheme",
83
+ "THEME_SCRIPT"
84
+ ]
85
+ ],
86
+ [
87
+ "Root attribute",
88
+ [
89
+ "<html data-theme=\"dark\">",
90
+ "<html data-theme=\"system\">"
91
+ ]
92
+ ]
93
+ ],
94
+ "scope": "twoux"
95
+ },
96
+ {
97
+ "rule": "Keep shadcn's own motion for overlays (dialogs, sheets, menus, popovers, tooltips, toasts); never retime it. For changes inside a screen use TwoUX's motion helpers, never a hand-written animation: Swap for an icon or label that changes state (Copy → Copied, Save → Saved), Count for a number that changes, RollingNumber for a figure on a stat card, ShimmerText for a working status line, StreamingText for AI text as it arrives, AnimateHeight for a card that changes size.",
98
+ "why": "shadcn's timing is tuned and familiar, and one set of helpers keeps motion consistent, short and off under reduced motion. Motion confirms a change; it never decorates.",
99
+ "use": [
100
+ [
101
+ "Helpers",
102
+ [
103
+ "Swap",
104
+ "Count",
105
+ "RollingNumber",
106
+ "ShimmerText",
107
+ "StreamingText",
108
+ "AnimateHeight"
109
+ ]
110
+ ],
111
+ [
112
+ "Easing",
113
+ [
114
+ "--ease-standard",
115
+ "--ease-entrance",
116
+ "--ease-move",
117
+ "--ease-exit",
118
+ "--ease-pop (pops only)"
119
+ ]
120
+ ]
121
+ ],
122
+ "scope": "twoux"
123
+ },
124
+ {
125
+ "rule": "Never import Radix or a shadcn file directly in app code. Import from @twoux/ui, where the tailored versions live.",
126
+ "why": "TwoUX wraps them with its tokens and restrictions; going around it bypasses both.",
127
+ "scope": "twoux"
128
+ },
129
+ {
130
+ "rule": "Style only with TwoUX role tokens and the utilities they generate.",
131
+ "why": "Roles can be re-pointed in one place; raw values cannot.",
132
+ "use": [
133
+ [
134
+ "Examples",
135
+ [
136
+ "bg-surface",
137
+ "text-ink-2",
138
+ "border-control",
139
+ "h-control",
140
+ "rounded-md"
141
+ ]
142
+ ]
143
+ ],
144
+ "scope": "twoux"
145
+ },
146
+ {
147
+ "rule": "No hex colours, pixel values, inline styles or arbitrary Tailwind values.",
148
+ "why": "Every raw value is a decision nobody reviewed. Tailwind's default palette is switched off, so bg-slate-500 fails to build.",
149
+ "use": [
150
+ [
151
+ "Blocked",
152
+ [
153
+ "#5753E0",
154
+ "w-[37px]",
155
+ "style={…}",
156
+ "bg-slate-500"
157
+ ]
158
+ ]
159
+ ],
160
+ "scope": "twoux"
161
+ },
162
+ {
163
+ "rule": "Use range tokens only in charts and reviewed illustrations, never in components.",
164
+ "why": "Components say what a colour is for, not which colour it is.",
165
+ "use": [
166
+ [
167
+ "Range token",
168
+ [
169
+ "--color-indigo-600"
170
+ ]
171
+ ]
172
+ ],
173
+ "scope": "twoux"
174
+ },
175
+ {
176
+ "rule": "Show third-party services with ProviderLogo: official marks only for services people sign in with or connect (Google, Microsoft, Slack…), unaltered and beside the service's name. Never draw, recolour or crop a mark, and never use the marks of reference or competing products. New providers are added in scripts/provider-logos.mjs.",
177
+ "why": "Brand guidelines allow a mark only to identify that service, unchanged; one generated source keeps licences and versions traceable.",
178
+ "use": [
179
+ [
180
+ "Component",
181
+ [
182
+ "ProviderLogo",
183
+ "PROVIDERS"
184
+ ]
185
+ ]
186
+ ]
187
+ }
188
+ ]
189
+ },
190
+ {
191
+ "title": "Changing TwoUX",
192
+ "rules": [
193
+ {
194
+ "rule": "Never edit TwoUX itself during app work: nothing under `packages/ui/src`, `packages/ui/registry` or `node_modules/@twoux`.",
195
+ "why": "TwoUX components are generated from shadcn source by a script; a hand edit is lost on the next run and silently forks behaviour for everyone.",
196
+ "use": [
197
+ [
198
+ "Protected",
199
+ [
200
+ "packages/ui/src/components",
201
+ "packages/ui/src/shadcn",
202
+ "packages/ui/src/styles"
203
+ ]
204
+ ],
205
+ [
206
+ "Instead",
207
+ [
208
+ "props · on the component",
209
+ "composition · in app code",
210
+ "request · ask the person to request it"
211
+ ]
212
+ ]
213
+ ],
214
+ "scope": "twoux"
215
+ },
216
+ {
217
+ "rule": "To change how the whole product looks (brand colour, corners, button shape, font, motion tempo), edit `twoux.theme.json` (in the TwoUX repo, `packages/ui/theme.json`) and run `npx twoux theme`. Never restyle or retime a component on one page, not even with valid tokens such as `px-3` or `rounded-full` on a Button or menu item.",
218
+ "why": "One theme file keeps every screen consistent, and the generator refuses colours that would fail WCAG contrast.",
219
+ "use": [
220
+ [
221
+ "File",
222
+ [
223
+ "twoux.theme.json"
224
+ ]
225
+ ],
226
+ [
227
+ "Fields",
228
+ [
229
+ "primary · palette name or #RRGGBB",
230
+ "radius · xs, sm, md in px",
231
+ "radius.button · px or \"pill\"",
232
+ "font",
233
+ "motion.tempo · 0.75 to 1.25",
234
+ "motion.character · standard or crisp"
235
+ ]
236
+ ],
237
+ [
238
+ "Command",
239
+ [
240
+ "npx twoux theme"
241
+ ]
242
+ ]
243
+ ],
244
+ "scope": "twoux"
245
+ },
246
+ {
247
+ "rule": "If a component needs a behaviour it lacks, first look for a prop or composition (for example onSelect with preventDefault to keep a menu open). If there is none, use the component as it is and tell the person what is missing; don't patch the component.",
248
+ "why": "Props and composition keep the shared behaviour intact; patches fork it.",
249
+ "scope": "twoux"
250
+ }
251
+ ]
252
+ },
253
+ {
254
+ "title": "Layout and density",
255
+ "rules": [
256
+ {
257
+ "rule": "Compact density only. No other sizes, except size=\"dense\" on tables and sheets for spreadsheet-like data.",
258
+ "why": "Power users scan dense data; a fixed set of sizes keeps every screen aligned.",
259
+ "use": [
260
+ [
261
+ "Controls",
262
+ [
263
+ "input · 32px",
264
+ "row · 36px"
265
+ ]
266
+ ],
267
+ [
268
+ "Buttons",
269
+ [
270
+ "xs · 24px",
271
+ "sm · 28px",
272
+ "md · 32px",
273
+ "lg · 36px"
274
+ ]
275
+ ],
276
+ [
277
+ "Dense tables and sheets",
278
+ [
279
+ "row · 28px, spreadsheet-like data only"
280
+ ]
281
+ ]
282
+ ],
283
+ "scope": "twoux"
284
+ },
285
+ {
286
+ "rule": "Use workspace density only on tool pages that pass all three tests: people watch and act on several regions at once, experts work in it for long repeated sessions, and the content is mostly numbers or tables scanned across many rows (trading, operations consoles, data workspaces, editors). Workspace density removes chrome and gaps, never size: AppShell frame=\"flush\" with the sidebar as a rail, regions in PaneLayout divided by 1px lines, panes padded 8px, charts flush, size=\"dense\" for spreadsheet-like tables. Forms, settings, onboarding, record detail, list pages and glance dashboards keep the inset canvas.",
287
+ "why": "Removing gaps and frames is what puts every region in view on a trading or operations screen; shrinking text or targets would make the same screen slower and fail WCAG 2.5.8. Text stays on TwoUX's type scale (13px body, 12px meta, never smaller), targets at 24px or more, and alerts, validation and empty states keep their size.",
288
+ "use": [
289
+ [
290
+ "Shell",
291
+ [
292
+ "AppShell frame=\"flush\"",
293
+ "sidebarRail"
294
+ ]
295
+ ],
296
+ [
297
+ "Regions",
298
+ [
299
+ "PaneLayout",
300
+ "usePaneSize"
301
+ ]
302
+ ],
303
+ [
304
+ "Inside a pane",
305
+ [
306
+ "ChartFrame frame=\"flush\" (default in a flush shell)",
307
+ "Table size=\"dense\" for spreadsheet-like data"
308
+ ]
309
+ ]
310
+ ],
311
+ "scope": "twoux"
312
+ },
313
+ {
314
+ "rule": "One primary button per view.",
315
+ "why": "The next step is obvious when only one action is primary."
316
+ },
317
+ {
318
+ "rule": "Show record detail in the side panel (push by default), not a modal or a new page.",
319
+ "why": "People keep their place in the list."
320
+ },
321
+ {
322
+ "rule": "Animate only to show what changed, such as panels, tabs, menus and content swaps. Never animate decoration or row hover. Pick the duration by job and the easing by role: arrivals decelerate, movement eases both ends, exits are shorter and accelerate.",
323
+ "why": "Short, consistent motion helps people follow a change; anything longer slows power users down. Everything stops when the system asks for reduced motion (WCAG 2.3.3).",
324
+ "use": [
325
+ [
326
+ "Durations",
327
+ [
328
+ "duration-fast · 120ms",
329
+ "duration-base · 180ms",
330
+ "duration-move · 200ms",
331
+ "duration-slow · 260ms",
332
+ "duration-long · 480ms (rolling figures only)",
333
+ "duration-exit · 120ms"
334
+ ]
335
+ ],
336
+ [
337
+ "Easing",
338
+ [
339
+ "ease-standard",
340
+ "ease-entrance",
341
+ "ease-move",
342
+ "ease-exit"
343
+ ]
344
+ ],
345
+ [
346
+ "Utilities",
347
+ [
348
+ "animate-fade-in",
349
+ "animate-rise-in",
350
+ "animate-pop-in",
351
+ "animate-fade-out"
352
+ ]
353
+ ]
354
+ ]
355
+ },
356
+ {
357
+ "rule": "Every screen reflows down to 320px wide without sideways scrolling. Wide tables, code and charts scroll inside their own box; navigation collapses behind a menu button.",
358
+ "why": "People zoom, split screens and use phones. WCAG 1.4.10 Reflow.",
359
+ "use": [
360
+ [
361
+ "Breakpoints",
362
+ [
363
+ "md · 768px",
364
+ "lg · 1024px",
365
+ "xl · 1280px"
366
+ ]
367
+ ]
368
+ ]
369
+ },
370
+ {
371
+ "rule": "Icons stay smaller than the text they sit beside and are muted: 14px in menus, lists and small controls, 16px in buttons and default fields.",
372
+ "why": "Icons support the label; at full size and full contrast they compete with it.",
373
+ "use": [
374
+ [
375
+ "Menus and lists",
376
+ [
377
+ "size-3.5",
378
+ "text-ink-3"
379
+ ]
380
+ ],
381
+ [
382
+ "Buttons and fields",
383
+ [
384
+ "size-4"
385
+ ]
386
+ ]
387
+ ],
388
+ "scope": "twoux"
389
+ },
390
+ {
391
+ "rule": "Open at most one right-hand panel at a time; opening record detail, the assistant or help replaces the open one, and closing restores it. Workspace pages built with PaneLayout keep their regions as panes instead, and still open at most one right-hand panel.",
392
+ "why": "Two side panels leave the table too narrow to use; a workspace gives each region its own pane that people size and arrange."
393
+ }
394
+ ]
395
+ },
396
+ {
397
+ "title": "Separation and hierarchy",
398
+ "rules": [
399
+ {
400
+ "rule": "Use three elevations, each a light border plus a shadow: flat for tables and previews, card for things on the page, float for things above it.",
401
+ "why": "The border gives a crisp edge, the soft shadow gives lift. Together they separate without heavy lines.",
402
+ "use": [
403
+ [
404
+ "Flat",
405
+ [
406
+ "border-line"
407
+ ]
408
+ ],
409
+ [
410
+ "Card",
411
+ [
412
+ "border-line",
413
+ "shadow-card"
414
+ ]
415
+ ],
416
+ [
417
+ "Float",
418
+ [
419
+ "shadow-pop"
420
+ ]
421
+ ]
422
+ ],
423
+ "scope": "twoux"
424
+ },
425
+ {
426
+ "rule": "Use a grey fill for quiet, supporting groups such as summaries, anatomy and sources. Not for primary content, and never with a shadow.",
427
+ "why": "Fills recede, so the page reads primary content first.",
428
+ "use": [
429
+ [
430
+ "Fill",
431
+ [
432
+ "bg-surface-2"
433
+ ]
434
+ ]
435
+ ],
436
+ "scope": "twoux"
437
+ },
438
+ {
439
+ "rule": "Use a tint only when it carries meaning: green for do, red for don't, primary for selection or the recommended path.",
440
+ "why": "Colour that always means something is colour people learn."
441
+ },
442
+ {
443
+ "rule": "Selected items get a fill, not a heavier border.",
444
+ "why": "A fill reads as state at a glance and keeps edges aligned.",
445
+ "use": [
446
+ [
447
+ "Fill",
448
+ [
449
+ "bg-selected"
450
+ ]
451
+ ]
452
+ ]
453
+ },
454
+ {
455
+ "rule": "Text fields and selects use a light edge with a hairline shadow, and always have a visible label. Checkboxes, radios and switches keep the stronger edge, because nothing else marks them.",
456
+ "why": "WCAG 1.4.11 needs 3:1 for the visual that identifies a control. A field is identified by its label and shadow; a checkbox only by its edge.",
457
+ "use": [
458
+ [
459
+ "Fields",
460
+ [
461
+ "border-input",
462
+ "shadow-xs",
463
+ "hover:border-input-hover"
464
+ ]
465
+ ],
466
+ [
467
+ "Focus",
468
+ [
469
+ "border-primary",
470
+ "ring-3 ring-primary/20"
471
+ ]
472
+ ],
473
+ [
474
+ "Small controls",
475
+ [
476
+ "border-control · 3.59:1"
477
+ ]
478
+ ]
479
+ ]
480
+ },
481
+ {
482
+ "rule": "Corners are tight and follow role: 4px inside a control, 6px for controls and cards, 8px for what floats. Inner radius is the outer radius minus the padding.",
483
+ "why": "Tight corners read as precise in a dense tool; matched nested corners make an element read as one object.",
484
+ "use": [
485
+ [
486
+ "Inside",
487
+ [
488
+ "rounded-xs · badge, checkbox, menu item, segment"
489
+ ]
490
+ ],
491
+ [
492
+ "Controls",
493
+ [
494
+ "rounded-sm · button, input, toggle, banner, card"
495
+ ]
496
+ ],
497
+ [
498
+ "Floating",
499
+ [
500
+ "rounded-md · menu, popover, palette, toast, dialog"
501
+ ]
502
+ ]
503
+ ],
504
+ "scope": "twoux"
505
+ }
506
+ ]
507
+ },
508
+ {
509
+ "title": "Content",
510
+ "rules": [
511
+ {
512
+ "rule": "UK English, sentence case, verb-first labels: Publish listing, Export 2 rows.",
513
+ "why": "Labels that say what happens are faster to act on."
514
+ },
515
+ {
516
+ "rule": "Before writing any label, message, empty state or assistant text, read docs-md/writing.md and docs-md/voice-and-tone.md (in Claude Code, /twoux-write); they override any general UX writing or design skill where the two differ. One voice everywhere, product and assistant alike: precise, calm, candid, respectful of expertise. The tone follows the moment (inform by default, empower at decisions, encourage when stuck, motivate only when starting, satisfy on routine work). Never humour, exclamation marks, celebration, \"we\", or effort words such as just, simply or easy.",
517
+ "why": "Trust drives recommendation far more than friendliness, and power users see the same message hundreds of times; what amuses once annoys by the tenth."
518
+ },
519
+ {
520
+ "rule": "Say what happened and what to do next. No apologies, no vague errors.",
521
+ "why": "People recover faster from errors that explain the fix."
522
+ },
523
+ {
524
+ "rule": "Never write “AI-powered”, “magic” or “smart”. Say what the assistant did.",
525
+ "why": "Plain words build trust; hype erodes it."
526
+ },
527
+ {
528
+ "rule": "Status is always a word in a Badge; colour is never the only cue.",
529
+ "why": "WCAG 1.4.1 Use of colour."
530
+ },
531
+ {
532
+ "rule": "Write every error as what happened plus what to do next, with raw codes only in a collapsed Details section.",
533
+ "why": "Raw API text gives people nothing to act on."
534
+ },
535
+ {
536
+ "rule": "Render every date and time with the Timestamp component: relative in lists, absolute UK format with zone in details and deadlines, ISO 8601 in exports, always labelled.",
537
+ "why": "Hand-formatted dates drift in format and leave out the zone and the event they refer to."
538
+ },
539
+ {
540
+ "rule": "If an action can't be undone, say so before it runs, in the button's confirmation or next to it, never only afterwards.",
541
+ "why": "People decide differently when they know there is no way back."
542
+ }
543
+ ]
544
+ },
545
+ {
546
+ "title": "Keyboard and accessibility",
547
+ "rules": [
548
+ {
549
+ "rule": "Every action is reachable by keyboard, and shows its shortcut on the button. A button that opens a menu shows its shortcut inside the menu instead, beside the menu's heading.",
550
+ "why": "Keyboard-first users learn shortcuts by seeing them. On a menu button the hint competes with the label and the menu's own options, so it moves to where people look once the menu is open.",
551
+ "use": [
552
+ [
553
+ "Prop",
554
+ [
555
+ "shortcut=\"C\""
556
+ ]
557
+ ]
558
+ ]
559
+ },
560
+ {
561
+ "rule": "Single-key shortcuts must respect the setting that turns them off.",
562
+ "why": "WCAG 2.1.4 Character key shortcuts."
563
+ },
564
+ {
565
+ "rule": "Meet WCAG 2.2 AA: 4.5:1 text, 3:1 control edges and icons, 24px minimum targets, visible focus.",
566
+ "why": "The baseline TwoUX is tested against."
567
+ },
568
+ {
569
+ "rule": "Only one popover open at a time, and never cut off by its container.",
570
+ "why": "Stacked or clipped popovers hide information and trap focus."
571
+ },
572
+ {
573
+ "rule": "When an action is unavailable but could become available, use aria-disabled and give a one-sentence reason in a tooltip; hide actions the person's role can never use.",
574
+ "why": "A natively disabled button can't take focus, so keyboard users never see why."
575
+ },
576
+ {
577
+ "rule": "Ship every drag-and-drop with a keyboard path (pick up, arrows, drop, Esc) and a menu or button alternative that does the same move.",
578
+ "why": "WCAG 2.2 requires a way to complete dragging tasks without dragging."
579
+ }
580
+ ]
581
+ },
582
+ {
583
+ "title": "AI and agents",
584
+ "rules": [
585
+ {
586
+ "rule": "The assistant changes data only through a proposal the person applies, with Undo.",
587
+ "why": "People stay in control of what changes."
588
+ },
589
+ {
590
+ "rule": "Decisions about candidates are never set to Allow, and are reviewed one at a time with a recorded reason.",
591
+ "why": "UK GDPR Article 22 and ICO guidance: rubber-stamped AI decisions about people count as solely automated."
592
+ },
593
+ {
594
+ "rule": "Mark AI-written content with the origin label and cite the records it used.",
595
+ "why": "People can check where an answer came from."
596
+ },
597
+ {
598
+ "rule": "Every run can be stopped, and shows what it is doing.",
599
+ "why": "Visible progress and a way out keep people in charge."
600
+ },
601
+ {
602
+ "rule": "Never give AI features their own colour, gradient, glow or sparkle icon; mark generated output only with `AiOrigin` (\"Assistant · from …\").",
603
+ "why": "A separate AI style competes with status colours and labels the feature instead of telling people where the output came from."
604
+ },
605
+ {
606
+ "rule": "Show Regenerate only on the latest answer, keep the previous version reachable with a version switch, and never let it undo or repeat changes already applied.",
607
+ "why": "Regenerating an earlier answer silently drops later turns, and applied changes are reversed only through Undo."
608
+ },
609
+ {
610
+ "rule": "Route any generated draft that would change a record through `SuggestedChanges`; a generated file card may offer Open, Copy, Download or Suggest, never Save.",
611
+ "why": "TwoUX's only path for the assistant to change data is a proposal the person applies, with Undo."
612
+ },
613
+ {
614
+ "rule": "Collect values needed to act in an inline input card with at most five required, prefilled fields that produces a proposal; use `ClarifyingQuestion` only to settle intent.",
615
+ "why": "Mixing the two makes people type values into free text or answer forms about what they meant."
616
+ },
617
+ {
618
+ "rule": "After every approval request, leave a resolved line in the thread (Approved, Rejected, Approved automatically, Expired) and have the assistant acknowledge a rejection.",
619
+ "why": "The decision is part of the record, and a closed card with no trace leaves people unsure what ran."
620
+ },
621
+ {
622
+ "rule": "Never offer Always allow, or the Allow level, for actions that decide on candidates or that reach people outside the workspace.",
623
+ "why": "Candidate decisions stay with a person under UK GDPR Article 22, and outside contact can't be undone."
624
+ },
625
+ {
626
+ "rule": "Show no loading state for waits under one second, stream only prose, and render tables, record lists and files as a skeleton until they arrive whole.",
627
+ "why": "Flicker and half-built tables mislead more than a short wait does."
628
+ },
629
+ {
630
+ "rule": "Never count failed or stopped steps as done, and end a summary that didn't finish with the reason: \"2 of 5 steps · stopped\".",
631
+ "why": "A bare percentage reads as work still in progress and contradicts the list under it."
632
+ },
633
+ {
634
+ "rule": "Keep the composer pinned and never auto-scroll the thread or an activity log while the person is reading earlier content.",
635
+ "why": "Jumping to new output loses the person's place and breaks copying."
636
+ },
637
+ {
638
+ "rule": "Never switch off or delete an agent automatically; mark it Needs attention and send its owner an Inbox item.",
639
+ "why": "Agents that vanish silently break schedules and trust."
640
+ },
641
+ {
642
+ "rule": "Before a multi-step or costly run, show the plan with PlanCard: steps people can remove, steps that reach people outside the workspace or can't be undone marked, nothing running until approved and never on a timeout. If the plan changes mid-run, add a PlanChange line saying what changed and why.",
643
+ "why": "Agreeing the plan first is cheaper than undoing the work, and a silent reroute breaks trust."
644
+ },
645
+ {
646
+ "rule": "Approvals for steps that send to people outside the workspace or move money show Approve and Cancel at equal weight (both outline). This is the only exception to one primary action per view.",
647
+ "why": "A highlighted Approve nudges people to rubber-stamp the actions that are hardest to take back."
648
+ },
649
+ {
650
+ "rule": "Any change an agent proposes to records, procedures, prompts, tools or another agent arrives as a ChangeRequest: the reason in plain words, its origin, the diff (DiffView) and simulation results (SimulationResults). Nothing goes live until a person approves; rejecting asks for a reason; every decision leaves who and when.",
651
+ "why": "Agents can change faster than people can read; review, test evidence and an attributable log keep a person accountable."
652
+ },
653
+ {
654
+ "rule": "When the agent is unsure, say so in words with ConfidenceNote, never as a percentage: what caused it and a safer option or a way to check. If it's unclear whether a change applies to one item, all items or future items, ask before acting.",
655
+ "why": "People misread probabilities; a named cause and an alternative tell them what to do."
656
+ },
657
+ {
658
+ "rule": "Offer WhyChoice next to a choice the agent made on someone's behalf: the deciding factors, the options that lost, any requirement it couldn't meet, then Change this or Keep it. Explain on demand, not by default.",
659
+ "why": "An explanation is only useful if it can be acted on and still matches the choice later."
660
+ },
661
+ {
662
+ "rule": "Cite every key claim next to the claim. Say when an answer rests on one source and how fresh time-sensitive facts are, keep what came from a source, from the person's own data and what was inferred apart, and label promoted or partner options every time.",
663
+ "why": "Polished output hides weak evidence; people need to see how much to trust each part."
664
+ },
665
+ {
666
+ "rule": "Write the assistant's caveat as one specific limit and one thing to check, shown in the flow (AssistantPanel caveat), never a generic \"can make mistakes\".",
667
+ "why": "A generic disclaimer is ignored; a specific limit tells people where to look."
668
+ },
669
+ {
670
+ "rule": "Decline out-of-scope requests in one sentence that names the boundary, then offer what the agent can do or where to get help.",
671
+ "why": "A short, specific decline keeps people moving without making them feel they asked something wrong."
672
+ },
673
+ {
674
+ "rule": "Use one set of words for agent work everywhere: agent (the thing that acts), run (one piece of work), step (part of a run), plan (steps agreed before a run), proposal (changes waiting to be applied), change request (a proposed change to how something works), handoff (passing to a person), instructions (standing guidance people write), memory (facts the agent kept). Never task, job or workflow for a run.",
675
+ "why": "Mixed words for the same thing make people think there are two features."
676
+ },
677
+ {
678
+ "rule": "Show the cost of a computed column before it runs and the running total while it runs; Stop is always available, and paid columns test on a few rows before running on all of them.",
679
+ "why": "People decide differently when they see the price, and a run they can't stop spends credits they never agreed to."
680
+ }
681
+ ]
682
+ },
683
+ {
684
+ "title": "Data and charts",
685
+ "rules": [
686
+ {
687
+ "rule": "Choose the chart from the question: change over time is a line, amounts are bars, parts of a total over time are a stacked area, two to five parts at one moment are a donut, ordered stages are a funnel, two measures across records are a scatter.",
688
+ "why": "A fixed mapping stops agents picking charts for looks and keeps every dashboard readable the same way."
689
+ },
690
+ {
691
+ "rule": "Never add a second y-axis; put a measure with a different unit in its own chart beside the first.",
692
+ "why": "Dual scales make the relationship between series depend on the chosen scales and mislead readers."
693
+ },
694
+ {
695
+ "rule": "Build all four states for every chart (loading, empty, no match with Clear filters, error with Try again) inside the chart card, using TwoUX's state copy.",
696
+ "why": "A blank or zeroed plot is read as real data."
697
+ },
698
+ {
699
+ "rule": "Plot missing data as a gap, never as zero, and start bars and areas at zero.",
700
+ "why": "Both errors invent drops or exaggerate change that the data does not show."
701
+ },
702
+ {
703
+ "rule": "Give each chart an accessible name equal to its visible title, a description \"[Chart type] showing [data], [period].\", and a Table view.",
704
+ "why": "Screen reader users and anyone who reads numbers better than shapes get the same information."
705
+ },
706
+ {
707
+ "rule": "Show a legend only for two or more series and a series filter only for three or more; legend items toggle series without changing any colour.",
708
+ "why": "Extra controls on simple charts add noise, and repainting series on toggle breaks recognition."
709
+ },
710
+ {
711
+ "rule": "Make chart popovers pin on select or Enter, close on Esc, hold at most seven breakdown items and two buttons, and link a row's name or value, never both.",
712
+ "why": "Hover-only popovers fail keyboard and touch, and crowded popovers hide the point."
713
+ },
714
+ {
715
+ "rule": "Use either chart drill-down or page drill-down on a page, never both, and show every drill-down filter as a removable pill in the filter bar.",
716
+ "why": "Two levels of filters conflict, and hidden filters make people trust numbers that are narrowed without them knowing."
717
+ },
718
+ {
719
+ "rule": "Draw targets and limits as a dashed threshold line with a text label, not as a series and not in a status colour alone.",
720
+ "why": "A threshold is a reference, and status colours are reserved for status meaning."
721
+ },
722
+ {
723
+ "rule": "Never plot individual candidates against each other on a chart, and never put candidate actions in a chart popover.",
724
+ "why": "Charts of people read as rankings, and candidate decisions must stay with a person reviewing each record."
725
+ },
726
+ {
727
+ "rule": "Use a bullet chart for actual against target, never a gauge or dial. Use RadialBars only for share of a fixed limit (seats, credits, storage, a budget), with one to four rings at small size and used of limit written beside them.",
728
+ "why": "Gauges spend a large area on one number and angles are read less exactly than lengths; a ring is acceptable only when the value is already in words and the limit is real."
729
+ },
730
+ {
731
+ "rule": "In a waterfall, draw increases and decreases in two categorical colours with + and − labels, and totals in neutral grey from zero.",
732
+ "why": "Green and red would claim a judgement the chart cannot make, and the signs keep direction readable without colour."
733
+ },
734
+ {
735
+ "rule": "Fold parts past six in a share bar, or past twelve in a treemap, into a grey Other; never add colours to fit more.",
736
+ "why": "Past that, parts cannot be told apart, and a colour on Other would make it read as one more series."
737
+ },
738
+ {
739
+ "rule": "When a price or candlestick axis does not start at zero, say so under the axis; every other chart starts at zero.",
740
+ "why": "A cropped axis exaggerates change unless the reader is told."
741
+ },
742
+ {
743
+ "rule": "Give every sheet column a type shown by its icon, and show computed columns' state in words: in each cell (Waiting, Writing, Failed with the reason, Not found) and in a status row under the header.",
744
+ "why": "People recognise a column by its type at a glance, and a word tells them what to do where a colour or spinner can't."
745
+ },
746
+ {
747
+ "rule": "Use size=\"dense\" (28px rows) on Table or SheetGrid only for spreadsheet-like data people scan many rows of: inventories, ledgers, bills of materials, imports and field mappings. Everything else keeps 36px rows.",
748
+ "why": "Dense rows put more of a sheet in view where people compare many rows; elsewhere they only cost readability.",
749
+ "scope": "twoux"
750
+ },
751
+ {
752
+ "rule": "An in-cell button (TableCellAction, or SheetGrid's Open) shows on row hover and keyboard focus, always on touch screens, and repeats an action that is also in the row's ⋮ menu. It is never the only way to do the action.",
753
+ "why": "Hover-revealed controls are missed by some people and can't be hovered on touch; the menu is the dependable path."
754
+ },
755
+ {
756
+ "rule": "Spreadsheet keys follow Excel and Google Sheets: Shift+arrows extend the selection, ⌘ or Ctrl+arrows jump to the edge of the data, ⌘C and ⌘V copy and paste tab-separated values, ⌘D and ⌘R fill, Delete clears, ⌘Z and ⌘⇧Z (or Ctrl+Y) undo and redo. Use SheetGrid for this; never write another keyboard handler for these actions.",
757
+ "why": "People bring years of spreadsheet habits, and copy and paste must work across the tools they already use."
758
+ },
759
+ {
760
+ "rule": "A paste checks every value against its column. Rejected cells keep their value, are marked on the cell with the reason, and the status line says how many were left and why; a value is never dropped silently or written in part.",
761
+ "why": "Pastes from other tools often carry text into number or date columns; people can only fix what they are told about."
762
+ },
763
+ {
764
+ "rule": "Use SplitButton when one action is the usual choice and a few close variants exist (Import data, then Import all and Import selection). Never join a Button and a menu trigger by hand.",
765
+ "why": "One component keeps the parts, divider, names and menu the same everywhere.",
766
+ "scope": "twoux"
767
+ },
768
+ {
769
+ "rule": "Use a map only when location itself is the question; to compare amounts use a bar chart or ranked list. Shade regions only by a rate or per-head figure, put raw counts on sized markers, and draw regions with no data hatched and labelled No data, never as zero.",
770
+ "why": "Large regions draw the eye whatever their value, and shading raw counts mostly maps where people live; a missing value painted as zero invents a result."
771
+ },
772
+ {
773
+ "rule": "Use RadialLines only for cyclical periods (hour of day, day of week, month of year) where the last period runs into the first: one to three series, values from zero, missing data as a gap. For any other time use a line chart.",
774
+ "why": "Polar coordinates help only when the end of the scale meets the start; elsewhere angles and a crowded centre read less exactly than a line."
775
+ },
776
+ {
777
+ "rule": "Show records people book or rearrange in time (interviews, shifts, rooms) in Scheduler: Day, Week and Month in its segmented control, open events in the side panel, create in a dialog, and move with drag, keyboard (Space, arrows, Enter) or the event menu, each with Undo. Never build a calendar grid by hand.",
778
+ "why": "One scheduler keeps the keyboard and menu paths every drag needs (WCAG 2.5.7), UK 24-hour times and the agenda that reflows to 320px."
779
+ }
780
+ ]
781
+ },
782
+ {
783
+ "title": "Patterns",
784
+ "rules": [
785
+ {
786
+ "rule": "Before adding anything to a dashboard, pick one item type from Dashboard items (key metrics, chart, list, status and alerts, quick links, getting started, feature news) and give it one job.",
787
+ "why": "A fixed catalogue stops items growing into mini reports with their own filters and tables."
788
+ },
789
+ {
790
+ "rule": "Make a dashboard either fully static or fully configurable; never mix fixed and movable items on one page.",
791
+ "why": "Mixed layouts confuse people about what they can change."
792
+ },
793
+ {
794
+ "rule": "Keep a dashboard to about seven items and link every chart or list item to the page with the full data.",
795
+ "why": "The page must answer its questions in seconds; detail belongs on report and list pages."
796
+ },
797
+ {
798
+ "rule": "Give a board item at most one header action and put the rest in its more menu, ordered Preferences first and Remove from dashboard last.",
799
+ "why": "Consistent menus let people find Remove and settings in the same place on every item."
800
+ },
801
+ {
802
+ "rule": "Provide a keyboard and single-click alternative for every drag on a board or items palette: menu moves, step resizes and an Add to dashboard button.",
803
+ "why": "WCAG 2.2 2.5.7 requires it and TwoUX is keyboard-first."
804
+ },
805
+ {
806
+ "rule": "Remove dashboard items and reset layouts at once with an Undo toast, never with a confirmation dialog.",
807
+ "why": "Both are reversible, so TwoUX's Undo rule applies."
808
+ },
809
+ {
810
+ "rule": "Save configurable dashboard layouts on the server per person, never only in localStorage, and never change another person's layout or the workspace default.",
811
+ "why": "People expect their layout on every device, and a personal change must not move a colleague's dashboard."
812
+ },
813
+ {
814
+ "rule": "Give each dashboard item its own loading, empty and error state inside its frame instead of one page-level spinner or error.",
815
+ "why": "One failing source must not hide the numbers that did load."
816
+ },
817
+ {
818
+ "rule": "Never disable a form's submit button; keep it enabled and run validation when it is pressed.",
819
+ "why": "A disabled submit hides which field is wrong and leaves people stuck."
820
+ },
821
+ {
822
+ "rule": "Show every error at the smallest scope that contains it (field, form banner, region error state, page) and never in a toast.",
823
+ "why": "Errors far from their cause are missed, and TwoUX toasts disappear after 6 seconds."
824
+ },
825
+ {
826
+ "rule": "Ask before discarding unsaved changes only while the form is dirty, register beforeunload only while it is dirty, and autosave drafts of long text instead of asking.",
827
+ "why": "Prompts that appear when nothing changed train people to click through the one that matters."
828
+ },
829
+ {
830
+ "rule": "Keep every row action also reachable from a selection (bulk bar) and ⌘K, and show at most two quick actions plus a More actions menu on each row.",
831
+ "why": "Actions that exist only on one row's hover are missed and can't be applied in bulk."
832
+ },
833
+ {
834
+ "rule": "Choose selection controls by rule: up to seven options shown as radios or checkboxes, eight or more in a select, switch only for changes that apply at once.",
835
+ "why": "Agents otherwise default to dropdowns everywhere, which hides short option sets."
836
+ },
837
+ {
838
+ "rule": "Choose the create surface by size: a dialog for up to three short fields, one page for most records, the Stepper only for more than about 15 main fields or steps that depend on each other.",
839
+ "why": "Wizards slow short tasks down and dialogs cannot hold long ones."
840
+ },
841
+ {
842
+ "rule": "Never disable a Create or Save changes button for an incomplete form; validate on submit and focus the first error.",
843
+ "why": "A disabled button gives no reason and hides what is missing."
844
+ },
845
+ {
846
+ "rule": "Put only required fields without a good default, and fields most people need, in the main section; put the rest under a collapsed Additional settings with their defaults filled in.",
847
+ "why": "Short forms are finished faster and visible defaults let people create in one step."
848
+ },
849
+ {
850
+ "rule": "Build the edit page from the create form component, with the same fields, order and headings.",
851
+ "why": "Two forms for one record drift apart and confuse people."
852
+ },
853
+ {
854
+ "rule": "Make delete move records to Deleted with Undo; use a confirmation dialog only for permanent deletes, and a typed name only when the permanent delete also removes other records.",
855
+ "why": "Friction must match severity or people click through every warning."
856
+ },
857
+ {
858
+ "rule": "Never add a sidebar item for a single record, a create page or an edit page; keep the record's type active instead.",
859
+ "why": "The sidebar shows where people can go, not where they have been."
860
+ },
861
+ {
862
+ "rule": "Put global destinations and utilities in the sidebar and keep the top bar for the breadcrumb and page-level controls.",
863
+ "why": "Splitting the same kind of thing across two places makes both harder to find."
864
+ },
865
+ {
866
+ "rule": "Announce features only with a New or Beta text label (FeatureLabel: plain in the sidebar via SidebarItem tag, a popover on request after the page title via PageHeader titleAddon), removed after 30 days for New; never with a toast, banner or auto-opening popover. A feature that spans several controls may offer Show me in its popover, which starts a GuideCue of at most three steps.",
867
+ "why": "Announcements must not compete with status messages people need to act on; a guide the person starts shows where the feature lives without interrupting anyone else."
868
+ },
869
+ {
870
+ "rule": "Never use a hero header, gradient banner or marketing-style title block inside the product.",
871
+ "why": "It pushes the work below the fold and draws attention away from the task."
872
+ },
873
+ {
874
+ "rule": "TwoUX has one density for the app: never build a density setting, comfortable mode or row height preference. Tables and sheets also offer size=\"dense\" (28px rows) only for spreadsheet-like data (inventories, ledgers, imports, mapping), never as a user preference or app-wide switch.",
875
+ "why": "One density keeps every screen consistent; dense rows exist only where people scan many rows of tabular data, as in a spreadsheet.",
876
+ "scope": "twoux"
877
+ },
878
+ {
879
+ "rule": "Sign in with SignInOptions: provider buttons first, then a work email that switches to single sign-on when the domain requires it, otherwise sends an email link. Errors go under the field and say what to do next.",
880
+ "why": "People pick the method they already use; SSO detection saves a failed attempt; a specific error gets them in."
881
+ },
882
+ {
883
+ "rule": "First-run setup is three to five OnboardingStep screens on a plain page: each says why it asks, optional steps say Optional and offer Skip, Continue is never disabled, progress is written as \"Step 2 of 5\", and Back keeps what was entered. No tours, auto-playing videos or gradient backgrounds; the only in-product guide is GuideCue, one to three steps, started by the person.",
884
+ "why": "Short, explained steps get finished; skippable questions respect people's time; text progress is read out by screen readers. Guides people ask for get followed; tours that start on their own get skipped."
885
+ },
886
+ {
887
+ "rule": "Connect services with ConnectorPicker and ConnectDialog: say what each connection lets the product do and who it is shared with before connecting, open OAuth in a new window, test API keys before saving and never show them again, and mark expired connections Needs attention with Reconnect.",
888
+ "why": "People grant access they understand; a tested key fails at setup, not in the middle of a run."
889
+ },
890
+ {
891
+ "rule": "After onboarding, keep a SetupChecklist of three to five useful tasks in the sidebar until done or dismissed, never as a modal.",
892
+ "why": "A checklist people can return to finishes setup without interrupting work."
893
+ },
894
+ {
895
+ "rule": "Show credits, seats or storage with UsageMeter: used of limit, when it resets and what used it. Warn from 80% and say what stops at the limit; show the upgrade link only then, never a permanent bright button. Show the cost of an action before it runs.",
896
+ "why": "People manage what they can see; surprise limits and constant upsell erode trust."
897
+ },
898
+ {
899
+ "rule": "Announce what changed on a What's new page or panel opened from the Help menu, with a FeatureLabel on the changed item. Never open it, a changelog modal or a video on load.",
900
+ "why": "Updates people ask for get read; ones that interrupt get dismissed unread."
901
+ },
902
+ {
903
+ "rule": "Imports show problems before importing and say what happens to bad rows (skipped, or imported with the cell left empty), and the Import button states how many rows it will add.",
904
+ "why": "Finding bad rows after an import means cleaning up data that is already in use."
905
+ },
906
+ {
907
+ "rule": "Show people where a task's controls are only with GuideCue, opened from something they pressed (SetupChecklist Start, Help menu, HelpPanel link, FeatureLabel Show me), never on load, a timer or hover: one to three steps on real targets, missing targets skipped, no overlay or beacon, Esc closes and focus returns.",
908
+ "why": "Pulled guidance is followed and remembered; pushed tours interrupt (WCAG 2.2.4) and are dismissed unread."
909
+ }
910
+ ]
911
+ }
912
+ ]
913
+ }