@twoux/ui 0.0.0-stage → 0.1.0

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/SHADCN-DIFF.md +228 -0
  5. package/agent/AGENTS.md +358 -0
  6. package/agent/docs-md/accessibility.md +49 -0
  7. package/agent/docs-md/action-catalogue.md +81 -0
  8. package/agent/docs-md/actions.md +74 -0
  9. package/agent/docs-md/agent-activity-log.md +70 -0
  10. package/agent/docs-md/agent-permissions.md +82 -0
  11. package/agent/docs-md/agent-run.md +90 -0
  12. package/agent/docs-md/agents-on-behalf.md +74 -0
  13. package/agent/docs-md/ai-column.md +85 -0
  14. package/agent/docs-md/ai-origin-label.md +70 -0
  15. package/agent/docs-md/announcing-features.md +73 -0
  16. package/agent/docs-md/answer-actions.md +81 -0
  17. package/agent/docs-md/app-shell.md +114 -0
  18. package/agent/docs-md/artifact-card.md +89 -0
  19. package/agent/docs-md/ask-on-screen.md +72 -0
  20. package/agent/docs-md/assistant-page.md +100 -0
  21. package/agent/docs-md/assistant-panel.md +142 -0
  22. package/agent/docs-md/avatar.md +82 -0
  23. package/agent/docs-md/banner.md +106 -0
  24. package/agent/docs-md/board.md +163 -0
  25. package/agent/docs-md/breadcrumb.md +89 -0
  26. package/agent/docs-md/breakdown-table.md +97 -0
  27. package/agent/docs-md/bulk-action-bar.md +112 -0
  28. package/agent/docs-md/button.md +293 -0
  29. package/agent/docs-md/card.md +115 -0
  30. package/agent/docs-md/change-list.md +99 -0
  31. package/agent/docs-md/change-request.md +166 -0
  32. package/agent/docs-md/chart-area.md +87 -0
  33. package/agent/docs-md/chart-bar.md +100 -0
  34. package/agent/docs-md/chart-box-plot.md +88 -0
  35. package/agent/docs-md/chart-bullet.md +89 -0
  36. package/agent/docs-md/chart-candlestick.md +112 -0
  37. package/agent/docs-md/chart-card-line.md +120 -0
  38. package/agent/docs-md/chart-donut.md +90 -0
  39. package/agent/docs-md/chart-drill-down.md +72 -0
  40. package/agent/docs-md/chart-funnel.md +71 -0
  41. package/agent/docs-md/chart-heatmap.md +76 -0
  42. package/agent/docs-md/chart-histogram.md +81 -0
  43. package/agent/docs-md/chart-map.md +165 -0
  44. package/agent/docs-md/chart-mixed.md +88 -0
  45. package/agent/docs-md/chart-price.md +92 -0
  46. package/agent/docs-md/chart-radial-bars.md +109 -0
  47. package/agent/docs-md/chart-radial-lines.md +111 -0
  48. package/agent/docs-md/chart-sankey.md +104 -0
  49. package/agent/docs-md/chart-scatter.md +87 -0
  50. package/agent/docs-md/chart-share-bar.md +89 -0
  51. package/agent/docs-md/chart-treemap.md +98 -0
  52. package/agent/docs-md/chart-waterfall.md +93 -0
  53. package/agent/docs-md/checkbox.md +131 -0
  54. package/agent/docs-md/choosing-selection-control.md +71 -0
  55. package/agent/docs-md/citations-sources.md +79 -0
  56. package/agent/docs-md/citing-sources.md +63 -0
  57. package/agent/docs-md/clarify-before-acting.md +69 -0
  58. package/agent/docs-md/clarifying-question.md +92 -0
  59. package/agent/docs-md/code-block.md +174 -0
  60. package/agent/docs-md/colours.md +87 -0
  61. package/agent/docs-md/columns-menu.md +140 -0
  62. package/agent/docs-md/combobox.md +161 -0
  63. package/agent/docs-md/command-palette.md +126 -0
  64. package/agent/docs-md/confidence-note.md +87 -0
  65. package/agent/docs-md/configurable-dashboard.md +76 -0
  66. package/agent/docs-md/confirmation-dialog.md +93 -0
  67. package/agent/docs-md/connected-tools.md +80 -0
  68. package/agent/docs-md/connector-picker.md +127 -0
  69. package/agent/docs-md/consistency.md +53 -0
  70. package/agent/docs-md/content-density.md +61 -0
  71. package/agent/docs-md/conversation-history.md +78 -0
  72. package/agent/docs-md/create-record.md +80 -0
  73. package/agent/docs-md/csv-import.md +88 -0
  74. package/agent/docs-md/dashboard-items.md +78 -0
  75. package/agent/docs-md/data-export.md +61 -0
  76. package/agent/docs-md/data-grid.md +137 -0
  77. package/agent/docs-md/data-vis-colours.md +76 -0
  78. package/agent/docs-md/data-visualisation.md +76 -0
  79. package/agent/docs-md/date-picker.md +178 -0
  80. package/agent/docs-md/delete-record.md +75 -0
  81. package/agent/docs-md/dense-table.md +495 -0
  82. package/agent/docs-md/design-tokens.md +53 -0
  83. package/agent/docs-md/disabled-read-only.md +75 -0
  84. package/agent/docs-md/drag-and-drop.md +74 -0
  85. package/agent/docs-md/dropdown-menu.md +137 -0
  86. package/agent/docs-md/edit-record.md +73 -0
  87. package/agent/docs-md/empty-loading-error.md +78 -0
  88. package/agent/docs-md/empty-state.md +128 -0
  89. package/agent/docs-md/error-messages.md +75 -0
  90. package/agent/docs-md/error-state.md +123 -0
  91. package/agent/docs-md/export-menu.md +86 -0
  92. package/agent/docs-md/feedback-prompt.md +88 -0
  93. package/agent/docs-md/field-mapping.md +78 -0
  94. package/agent/docs-md/file-drop.md +110 -0
  95. package/agent/docs-md/filter-bar.md +138 -0
  96. package/agent/docs-md/filter-panel.md +103 -0
  97. package/agent/docs-md/filtering-search.md +79 -0
  98. package/agent/docs-md/follow-ups-starters.md +79 -0
  99. package/agent/docs-md/form-validation.md +95 -0
  100. package/agent/docs-md/generated-columns.md +65 -0
  101. package/agent/docs-md/generated-drafts-files.md +71 -0
  102. package/agent/docs-md/grouped-list.md +89 -0
  103. package/agent/docs-md/guide-cue.md +170 -0
  104. package/agent/docs-md/help-panel.md +103 -0
  105. package/agent/docs-md/help-system.md +74 -0
  106. package/agent/docs-md/iconography.md +49 -0
  107. package/agent/docs-md/inbox.md +108 -0
  108. package/agent/docs-md/inline-editing-validation.md +80 -0
  109. package/agent/docs-md/inline-input-in-answer.md +70 -0
  110. package/agent/docs-md/items-palette.md +116 -0
  111. package/agent/docs-md/keyboard-model.md +47 -0
  112. package/agent/docs-md/keyboard-navigation.md +68 -0
  113. package/agent/docs-md/keyboard-shortcut-hint.md +87 -0
  114. package/agent/docs-md/keyboard-shortcut-sheet.md +90 -0
  115. package/agent/docs-md/labelling-ai-output.md +66 -0
  116. package/agent/docs-md/layout.md +47 -0
  117. package/agent/docs-md/list-table.md +254 -0
  118. package/agent/docs-md/loading-skeleton.md +91 -0
  119. package/agent/docs-md/managing-agents.md +71 -0
  120. package/agent/docs-md/message-composer.md +105 -0
  121. package/agent/docs-md/migrating.md +49 -0
  122. package/agent/docs-md/motion.md +93 -0
  123. package/agent/docs-md/navigation.md +76 -0
  124. package/agent/docs-md/notification-channels.md +65 -0
  125. package/agent/docs-md/notifications-decision.md +100 -0
  126. package/agent/docs-md/onboarding-step.md +114 -0
  127. package/agent/docs-md/order-book.md +108 -0
  128. package/agent/docs-md/page-header.md +96 -0
  129. package/agent/docs-md/pagination.md +88 -0
  130. package/agent/docs-md/pane-layout.md +105 -0
  131. package/agent/docs-md/period-comparison.md +66 -0
  132. package/agent/docs-md/plan-card.md +120 -0
  133. package/agent/docs-md/popover-rules.md +88 -0
  134. package/agent/docs-md/prompt-editor.md +90 -0
  135. package/agent/docs-md/propose-apply.md +65 -0
  136. package/agent/docs-md/provider-logo.md +97 -0
  137. package/agent/docs-md/radio.md +83 -0
  138. package/agent/docs-md/ranked-list.md +102 -0
  139. package/agent/docs-md/reasoning-progress.md +72 -0
  140. package/agent/docs-md/record-detail.md +71 -0
  141. package/agent/docs-md/regenerating-answers.md +70 -0
  142. package/agent/docs-md/run-status-bar.md +94 -0
  143. package/agent/docs-md/saved-tasks-schedules.md +63 -0
  144. package/agent/docs-md/saved-views.md +149 -0
  145. package/agent/docs-md/saving-views.md +68 -0
  146. package/agent/docs-md/scheduler.md +258 -0
  147. package/agent/docs-md/secondary-panels.md +72 -0
  148. package/agent/docs-md/segmented-control.md +88 -0
  149. package/agent/docs-md/selection-bulk-actions.md +70 -0
  150. package/agent/docs-md/separator.md +75 -0
  151. package/agent/docs-md/setup-checklist.md +92 -0
  152. package/agent/docs-md/shape-elevation.md +55 -0
  153. package/agent/docs-md/sheet-grid.md +211 -0
  154. package/agent/docs-md/side-panel.md +134 -0
  155. package/agent/docs-md/sign-in.md +114 -0
  156. package/agent/docs-md/spacing.md +55 -0
  157. package/agent/docs-md/split-button.md +100 -0
  158. package/agent/docs-md/stat-compare.md +82 -0
  159. package/agent/docs-md/static-dashboard.md +75 -0
  160. package/agent/docs-md/status-badge.md +140 -0
  161. package/agent/docs-md/step-card.md +75 -0
  162. package/agent/docs-md/stepper.md +92 -0
  163. package/agent/docs-md/suggested-changes.md +91 -0
  164. package/agent/docs-md/switch.md +78 -0
  165. package/agent/docs-md/table-settings.md +81 -0
  166. package/agent/docs-md/tabs.md +85 -0
  167. package/agent/docs-md/tag-input.md +108 -0
  168. package/agent/docs-md/tag.md +79 -0
  169. package/agent/docs-md/task-builder.md +87 -0
  170. package/agent/docs-md/team-instructions.md +72 -0
  171. package/agent/docs-md/templates-gallery.md +85 -0
  172. package/agent/docs-md/text-input.md +160 -0
  173. package/agent/docs-md/thinking-steps.md +89 -0
  174. package/agent/docs-md/timeline.md +189 -0
  175. package/agent/docs-md/timestamp.md +84 -0
  176. package/agent/docs-md/timestamps.md +70 -0
  177. package/agent/docs-md/toast.md +117 -0
  178. package/agent/docs-md/tooltip.md +90 -0
  179. package/agent/docs-md/typography.md +62 -0
  180. package/agent/docs-md/undo-destructive.md +66 -0
  181. package/agent/docs-md/unsaved-changes.md +71 -0
  182. package/agent/docs-md/usage-meter.md +80 -0
  183. package/agent/docs-md/user-feedback.md +71 -0
  184. package/agent/docs-md/voice-and-tone.md +60 -0
  185. package/agent/docs-md/why-choice.md +93 -0
  186. package/agent/docs-md/workspace-onboarding.md +74 -0
  187. package/agent/docs-md/writing.md +48 -0
  188. package/agent/llms-full.txt +18369 -0
  189. package/agent/llms.txt +198 -0
  190. package/agent/skills/twoux-audit/SKILL.md +49 -0
  191. package/agent/skills/twoux-build/SKILL.md +39 -0
  192. package/agent/skills/twoux-design/SKILL.md +54 -0
  193. package/agent/skills/twoux-write/SKILL.md +32 -0
  194. package/bin/check.mjs +123 -0
  195. package/bin/docs.mjs +24 -0
  196. package/bin/guard.mjs +27 -0
  197. package/bin/init.mjs +113 -0
  198. package/bin/knowledge.mjs +87 -0
  199. package/bin/mcp.mjs +116 -0
  200. package/bin/theme.mjs +168 -0
  201. package/bin/twoux.mjs +25 -0
  202. package/package.json +80 -3
  203. package/registry/jobs.json +1028 -0
  204. package/registry/palette.json +770 -0
  205. package/registry/rules.json +913 -0
  206. package/src/components/agent-review.tsx +364 -0
  207. package/src/components/ai-basics.tsx +212 -0
  208. package/src/components/ai-work.tsx +304 -0
  209. package/src/components/ai.tsx +227 -0
  210. package/src/components/alert-dialog.tsx +203 -0
  211. package/src/components/alert.tsx +86 -0
  212. package/src/components/avatar.tsx +114 -0
  213. package/src/components/badge.tsx +59 -0
  214. package/src/components/board.tsx +397 -0
  215. package/src/components/breadcrumb.tsx +114 -0
  216. package/src/components/button-group.tsx +90 -0
  217. package/src/components/button.tsx +109 -0
  218. package/src/components/calendar.tsx +247 -0
  219. package/src/components/card.tsx +102 -0
  220. package/src/components/chart-map.tsx +336 -0
  221. package/src/components/charts-more.tsx +1331 -0
  222. package/src/components/charts-radial.tsx +328 -0
  223. package/src/components/charts.tsx +390 -0
  224. package/src/components/checkbox.tsx +40 -0
  225. package/src/components/code.tsx +139 -0
  226. package/src/components/collapsible.tsx +37 -0
  227. package/src/components/combobox.tsx +339 -0
  228. package/src/components/command-extras.tsx +11 -0
  229. package/src/components/command.tsx +196 -0
  230. package/src/components/data-grid.tsx +213 -0
  231. package/src/components/data-table.tsx +334 -0
  232. package/src/components/data-views.tsx +740 -0
  233. package/src/components/date-picker.tsx +686 -0
  234. package/src/components/dense-table.tsx +207 -0
  235. package/src/components/dialog.tsx +162 -0
  236. package/src/components/dropdown-menu.tsx +264 -0
  237. package/src/components/empty.tsx +114 -0
  238. package/src/components/feedback-prompt.tsx +80 -0
  239. package/src/components/field.tsx +254 -0
  240. package/src/components/fields.tsx +112 -0
  241. package/src/components/flow.tsx +840 -0
  242. package/src/components/guide-cue.tsx +273 -0
  243. package/src/components/inbox.tsx +113 -0
  244. package/src/components/input-group.tsx +176 -0
  245. package/src/components/input.tsx +26 -0
  246. package/src/components/kbd.tsx +34 -0
  247. package/src/components/label.tsx +27 -0
  248. package/src/components/labels.tsx +176 -0
  249. package/src/components/motion.tsx +136 -0
  250. package/src/components/onboarding.tsx +454 -0
  251. package/src/components/order-book.tsx +185 -0
  252. package/src/components/pane-layout.tsx +417 -0
  253. package/src/components/popover.tsx +96 -0
  254. package/src/components/provider-logos.data.ts +49 -0
  255. package/src/components/radio-group.tsx +52 -0
  256. package/src/components/scheduler.tsx +997 -0
  257. package/src/components/segmented-control.tsx +28 -0
  258. package/src/components/select.tsx +195 -0
  259. package/src/components/separator.tsx +31 -0
  260. package/src/components/sheet.tsx +148 -0
  261. package/src/components/shell.tsx +426 -0
  262. package/src/components/shortcut-sheet.tsx +83 -0
  263. package/src/components/skeleton.tsx +18 -0
  264. package/src/components/sonner.tsx +107 -0
  265. package/src/components/spinner.tsx +13 -0
  266. package/src/components/split-button.tsx +58 -0
  267. package/src/components/stepper.tsx +78 -0
  268. package/src/components/switch.tsx +43 -0
  269. package/src/components/table.tsx +126 -0
  270. package/src/components/tabs.tsx +118 -0
  271. package/src/components/tag-input.tsx +253 -0
  272. package/src/components/textarea.tsx +21 -0
  273. package/src/components/theme.tsx +80 -0
  274. package/src/components/timeline.tsx +408 -0
  275. package/src/components/toggle-group.tsx +87 -0
  276. package/src/components/toggle.tsx +54 -0
  277. package/src/components/tooltip.tsx +65 -0
  278. package/src/components/workspace-actions.tsx +294 -0
  279. package/src/components/workspace-import.tsx +465 -0
  280. package/src/components/workspace.tsx +1884 -0
  281. package/src/index.ts +80 -0
  282. package/src/lib/active-indicator.tsx +53 -0
  283. package/src/lib/chart-kit.tsx +72 -0
  284. package/src/lib/cn.ts +6 -0
  285. package/src/lib/shortcuts.tsx +61 -0
  286. package/src/lib/workspace.tsx +10 -0
  287. package/src/shadcn/alert-dialog.tsx +196 -0
  288. package/src/shadcn/alert.tsx +65 -0
  289. package/src/shadcn/avatar.tsx +108 -0
  290. package/src/shadcn/badge.tsx +47 -0
  291. package/src/shadcn/breadcrumb.tsx +108 -0
  292. package/src/shadcn/button-group.tsx +83 -0
  293. package/src/shadcn/button.tsx +63 -0
  294. package/src/shadcn/calendar.tsx +220 -0
  295. package/src/shadcn/card.tsx +91 -0
  296. package/src/shadcn/checkbox.tsx +31 -0
  297. package/src/shadcn/collapsible.tsx +33 -0
  298. package/src/shadcn/command.tsx +184 -0
  299. package/src/shadcn/dialog.tsx +158 -0
  300. package/src/shadcn/dropdown-menu.tsx +256 -0
  301. package/src/shadcn/empty.tsx +103 -0
  302. package/src/shadcn/field.tsx +248 -0
  303. package/src/shadcn/input-group.tsx +170 -0
  304. package/src/shadcn/input.tsx +20 -0
  305. package/src/shadcn/kbd.tsx +28 -0
  306. package/src/shadcn/label.tsx +23 -0
  307. package/src/shadcn/popover.tsx +88 -0
  308. package/src/shadcn/radio-group.tsx +44 -0
  309. package/src/shadcn/select.tsx +189 -0
  310. package/src/shadcn/separator.tsx +27 -0
  311. package/src/shadcn/sheet.tsx +142 -0
  312. package/src/shadcn/skeleton.tsx +13 -0
  313. package/src/shadcn/sonner.tsx +40 -0
  314. package/src/shadcn/switch.tsx +34 -0
  315. package/src/shadcn/table.tsx +115 -0
  316. package/src/shadcn/tabs.tsx +90 -0
  317. package/src/shadcn/textarea.tsx +17 -0
  318. package/src/shadcn/toggle-group.tsx +83 -0
  319. package/src/shadcn/toggle.tsx +46 -0
  320. package/src/shadcn/tooltip.tsx +56 -0
  321. package/src/styles/palette.css +127 -0
  322. package/src/styles/theme.css +35 -0
  323. package/src/styles/twoux.css +236 -0
  324. package/theme.json +14 -0
  325. package/README.md +0 -3
@@ -0,0 +1,93 @@
1
+ # Motion
2
+
3
+ Motion in TwoUX shows what changed and where it went: 120 to 260ms, eased by role, exits shorter than entrances, and nothing moves when people ask for reduced motion. It never decorates.
4
+
5
+ Kind: foundation · Group: Visual foundation
6
+
7
+ ## Principles
8
+
9
+ - Motion explains a change. Use it when people need to see what happened: a panel arriving, a section expanding, a value updating, a setting switching. If nothing would be lost without it, leave it out.
10
+ - Productive, not expressive. Expressive motion suits rare, significant moments; a tool used all day has none worth the delay, so TwoUX has no expressive tier.
11
+ - Ease by role. Something arriving or changing in place decelerates (--ease-entrance, --ease-standard), something moving between two places eases at both ends (--ease-move), something leaving accelerates and gets out of the way (--ease-exit). Only a changing count overshoots (--ease-pop).
12
+ - Shortest that is not jarring. Most UI animation belongs between 100 and 500ms, and animations are far more often too long than too short; TwoUX stays between 120 and 260ms for anything people trigger.
13
+ - Overlays keep shadcn's motion. Dialogs, menus, popovers and tooltips open with shadcn's fade and 95% zoom; the sheet slides 40px with a fade over 200ms. The push side panel matches it with --duration-move and --ease-move, so the list beside it eases aside.
14
+ - Changes inside a screen use the motion helpers: Swap for a label or icon that changes state, Count for a number that changes, RollingNumber for a figure on a stat card, ShimmerText for a working status, StreamingText for AI text, AnimateHeight for a card that changes size. Nothing animates on first render.
15
+ - Never the only signal. Motion never carries information alone; every change motion shows is also visible in its end state.
16
+
17
+ ## When to use
18
+
19
+ - Panels, sheets and dialogs arriving, so people see where they came from and where they will go back to.
20
+ - Sections expanding or collapsing, and cards changing height, so the content below doesn't jump.
21
+ - State changes on a control people just used: a switch, a selected segment, Copy turning into Copied.
22
+ - A value that updates while people watch, such as a stat card after a filter change or a count in a tab.
23
+ - Toasts arriving and leaving, so a confirmation is noticed without taking focus.
24
+
25
+ ## When not to use
26
+
27
+ - Hover on table rows, list items and menu items. These change many times a second as the pointer moves; a fill that lags reads as a slow app.
28
+ - Rows loading, sorting or filtering in a table. People scan the result, not the transition.
29
+ - Anything that blocks the next action: never make people wait for an animation to finish before they can click or type.
30
+ - Decoration: entrance animations on page load, gradients that drift, icons that wiggle to get attention.
31
+ - Showing that AI is working when the result is under about one second away; show the result.
32
+
33
+ ## Guidelines
34
+
35
+ - Hover, press and colour changes: --duration-fast (120ms).
36
+ - Tabs, menus, state swaps and small transforms such as a chevron turning: --duration-base (180ms).
37
+ - Panels and layout that move, such as the side panel push or the sidebar collapsing: --duration-move (200ms) with --ease-move, matching shadcn's sheet.
38
+ - Content swaps, height changes and progress fills: --duration-slow (260ms).
39
+ - Figures rolling to a new value: --duration-long (480ms), only for values people read after the change, never for something they trigger many times a minute.
40
+ - Exits are shorter than entrances and use --duration-exit with --ease-exit. Tooltips, hover cards and menus may disappear at once; nobody needs to watch them leave.
41
+ - Match the exit to the entrance: what slid in from the right leaves to the right, what faded in fades out.
42
+ - Direction follows the action. Going deeper (opening a record, the next step) moves forward; going back moves back.
43
+ - Contextual UI grows from its trigger: a menu or popover opens from the button that opened it. Global UI such as the command palette and toasts has no trigger to connect to.
44
+ - Loops (spinner 0.8s, thinking pulse 1.2s, skeleton shimmer 1.4s) always sit next to text such as 'Searching listings', and stop once content arrives. Theme tempo does not scale them.
45
+ - Animate with the tokens, never with raw values, so a theme tempo change reaches every animation: duration-(--duration-base) ease-(--ease-standard) in a class, var(--duration-base) in CSS.
46
+ - Tune motion for a whole product in the theme file: motion.tempo from 0.75 (quicker) to 1.25 (calmer) scales every duration token, and motion.character "crisp" removes the overshoot from pops. Never retime one component on one page.
47
+ - Streamed text appears without a typing animation or cursor blink; under reduced motion it arrives in sentences or paragraphs rather than word by word.
48
+
49
+ ## Accessibility
50
+
51
+ - Under prefers-reduced-motion: reduce, TwoUX sets every transition and animation to 0ms, so changes happen at once. Values that change in place, such as a progress fill, jump straight to the new value.
52
+ - WCAG 2.3.3 (AAA) asks that interaction-triggered motion can be disabled; TwoUX meets it through the operating-system setting, with no extra control to find.
53
+ - Because the reduced-motion rule also stops spinners, every loading state must have text that says what is happening.
54
+ - Nothing flashes more than three times a second.
55
+ - Test every view with reduced motion turned on, on a laptop and on a touch screen.
56
+
57
+ ## Do
58
+
59
+ - Animate a spatial change, such as a panel sliding in, so people see where it came from.
60
+ - Use the fast band for small interactions and the move or slow band for layout changes.
61
+ - Leave faster than you arrive.
62
+ - Change tempo in the theme file when the whole product should feel calmer or quicker.
63
+
64
+ ## Don't
65
+
66
+ - Don't animate hover on rows or menu items, or make any frequent interaction lag.
67
+ - Don't use bounce, spring or scale-up entrances; the only overshoot is a count that changes.
68
+ - Don't go above 300ms for anything people trigger often, or make people wait for an animation before acting.
69
+ - Don't move something in a way that contradicts where it came from or where it goes.
70
+
71
+ ## Tokens
72
+
73
+ | Token | Value | Use |
74
+ |---|---|---|
75
+ | `--duration-fast` | `120ms × tempo` | Hover, press, colour, small controls |
76
+ | `--duration-base` | `180ms × tempo` | Tabs, menus, state swaps, small transforms |
77
+ | `--duration-move` | `200ms × tempo` | Panels and layout that move (side panel, sidebar) |
78
+ | `--duration-slow` | `260ms × tempo` | Content swaps, height changes, progress fills |
79
+ | `--duration-long` | `480ms × tempo` | Figures rolling to a new value only |
80
+ | `--duration-exit` | `= --duration-fast` | Anything leaving |
81
+ | `--ease-standard` | `cubic-bezier(0.22, 1, 0.36, 1)` | State changes in place: swaps, numbers, heights, fills |
82
+ | `--ease-entrance` | `cubic-bezier(0.22, 1, 0.36, 1)` | Something arriving |
83
+ | `--ease-move` | `cubic-bezier(0.4, 0, 0.2, 1)` | Something moving between two places |
84
+ | `--ease-exit` | `cubic-bezier(0.4, 0, 1, 1)` | Something leaving. Never overshoots |
85
+ | `--ease-pop` | `cubic-bezier(0.34, 1.56, 0.64, 1)` | A count that changes. Standard character only; crisp uses --ease-standard |
86
+ | `--motion-tempo` | `1 (0.75 to 1.25)` | Set by motion.tempo in the theme file. Scales every duration token |
87
+ | `--ease-out, --ease-in-out, --ease-soft, --ease-spring` | `aliases` | Earlier names for entrance, move, standard and pop. Kept for existing code |
88
+
89
+ ## Related
90
+
91
+ - Accessibility
92
+ - Shape and elevation
93
+ - Design tokens
@@ -0,0 +1,76 @@
1
+ # Navigation: what goes in the sidebar and the top bar
2
+
3
+ The sidebar holds where you can go and the few things you always need; the top bar holds where you are and what this page does. Records never become sidebar items.
4
+
5
+ Kind: pattern · Group: Patterns: general
6
+
7
+ **Problem:** Without a rule, global links land in page headers, record pages add themselves to the sidebar, and every feature adds a top-level item. People lose track of where they are and the sidebar turns into a site map.
8
+
9
+ **Solution:** One app shell. The sidebar has three parts: the workspace menu with Search (⌘K) and Inbox at the top, the main sections in the middle, and Settings and Help at the bottom. The top bar shows the breadcrumb and page-level controls only. One sidebar item is always active, including on record pages.
10
+
11
+ **Built from:** App shell and sidebar navigation, Breadcrumb, Page header, Command palette (⌘K), Inbox (triage), Saved views, Keyboard shortcut hint
12
+
13
+ ## How it works
14
+
15
+ 1. Top of the sidebar: the workspace menu (switch workspace, your profile, Preferences, Sign out last), then Search (⌘K) and Inbox with its count.
16
+ 2. Middle: the main sections, ordered by how often a hiring team uses them: Home, Listings, Candidates, Interviews, Reports. Saved views sit under their section.
17
+ 3. Bottom: Settings and Help, set apart by space or a divider.
18
+ 4. Top bar: breadcrumb on the left, page-level controls such as the side panel toggle on the right. No global destinations.
19
+ 5. On a record page, the record's type stays active in the sidebar: viewing Priya Shah keeps Candidates active.
20
+
21
+ ## When to use
22
+
23
+ - For every screen inside the product.
24
+ - When adding a new area: add a sidebar item only if people go there on its own, not only through a record.
25
+ - When placing account and workspace utilities such as profile, preferences, Search and Inbox.
26
+
27
+ ## When not to use
28
+
29
+ - Not for sign-in, public careers pages or marketing pages. They have their own simple frame.
30
+ - Don't use the sidebar for actions. Create listing lives in the Listings page header, not the sidebar.
31
+ - Not for moving between records in a list. Use J and K with the side panel, as Record detail describes.
32
+
33
+ ## Guidelines
34
+
35
+ - Organise by the team's tasks and the things they manage, not by a site map. Create, edit and settings pages for one record are reached from the record, never from the sidebar.
36
+ - Two levels at most. Deeper pages are reached through links in the page, and the breadcrumb shows the way back.
37
+ - Group only when it helps: no group with only two links, and don't mix headed groups with expandable items in one sidebar.
38
+ - Sidebar labels, breadcrumb items and page titles use the same words.
39
+ - Counts in the sidebar are for things that need action, like Inbox. Saved views may show their live row count. No decorative counts or static labels other than New or Beta.
40
+ - The sidebar is either shown in full or hidden; TwoUX has no icon-only rail. When hidden, Inbox keeps a small button with its count in the top bar.
41
+ - Every destination is also reachable from ⌘K, so the sidebar is never the only way.
42
+
43
+ ## Content
44
+
45
+ - Section names are plural nouns for what they hold: "Listings", "Candidates". Not verbs or team jargon.
46
+ - The workspace menu shows the workspace name and the person's name, so people with two workspaces know which one is open.
47
+ - Breadcrumb items match sidebar labels: "Listings / Junior Product Designer".
48
+
49
+ ## Accessibility
50
+
51
+ - The sidebar is a nav landmark named "Main"; the breadcrumb is a nav named "Breadcrumb".
52
+ - The active item has aria-current="page"; its look does not rely on colour alone.
53
+ - A skip link to the main content is the first focusable item on every page.
54
+ - The order of sidebar items is the same on every page.
55
+
56
+ ## Do
57
+
58
+ - Do keep one sidebar item active on every page.
59
+ - Do order items from most to least used.
60
+ - Do put profile, preferences and sign out in the workspace menu, with Sign out last.
61
+
62
+ ## Don't
63
+
64
+ - Don't add a sidebar item when a record opens.
65
+ - Don't put global links in the top bar or page header.
66
+ - Don't add a top-level section for a feature people only reach from inside another one.
67
+
68
+ ## Related
69
+
70
+ - App shell and sidebar navigation
71
+ - Breadcrumb
72
+ - Page header
73
+ - Command palette (⌘K)
74
+ - Keyboard-first navigation
75
+ - Record detail: side panel, overlay or full page
76
+ - Announcing new and beta features
@@ -0,0 +1,65 @@
1
+ # Notifications: inbox, bell, toast or email digest
2
+
3
+ Route every message by one question: does it need this person to act? Things that need you go to the Inbox; your own results go in a toast; nothing gets a separate bell list.
4
+
5
+ Kind: pattern · Group: Patterns: general
6
+
7
+ **Problem:** Products grow a bell, an inbox, toasts, banners and emails that overlap, so people get the same news three times and miss the one that needed them.
8
+
9
+ **Solution:** The Inbox is the one place for things that need you (agent approvals and questions, listings about to close, mentions, assignments). Toasts only confirm your own actions. Workspace-wide problems use a banner. Email sends a digest of open Inbox items to people who are away.
10
+
11
+ **Built from:** Inbox (triage), Toast, Banner (inline alert), Notifications vs inbox vs toast, App shell and sidebar navigation, Agent run timeline
12
+
13
+ ## How it works
14
+
15
+ 1. Did this person just do it? Confirm with a toast, or with nothing if the change is already visible.
16
+ 2. Does it need this person to act or reply? Put it in the Inbox. It stays until marked done (E) or snoozed (⇧H).
17
+ 3. Does it affect everyone's use of the workspace, such as a broken connection? Show a banner on the affected pages until fixed.
18
+ 4. Is the person away from TwoUX and is something waiting on them? Send an email digest of open Inbox items, at the frequency they chose.
19
+ 5. Needs nothing from anyone? Record it in the record's activity only.
20
+
21
+ ## When to use
22
+
23
+ - Whenever a feature wants to tell someone something. Pick the channel with the steps above before designing the message.
24
+
25
+ ## When not to use
26
+
27
+ - For validation of what someone typed. Use field messages from Inline editing and validation.
28
+ - For errors that need a retry. A toast disappears and cannot hold Retry reliably; use a banner or the error state.
29
+
30
+ ## Guidelines
31
+
32
+ - No separate bell list. The Inbox count is the only unread count; when the sidebar is hidden, a small inbox button with the same count replaces it.
33
+ - Toasts never carry critical warnings or errors and never auto-dismiss something the person must act on.
34
+ - Don't resend notifications people have not acted on; let them set email frequency and channels.
35
+ - Trivial updates go nowhere but activity. Notification fatigue makes people ignore the important ones.
36
+
37
+ ## Content
38
+
39
+ - Toasts are past tense and short: "Listing published". Three lines at most.
40
+ - Banners say what is affected and what to do: "Workable is disconnected. Reconnect to keep importing candidates."
41
+
42
+ ## Accessibility
43
+
44
+ - Toasts and count changes are announced as status messages.
45
+ - Toasts must not cover the focused element; they appear in an empty corner.
46
+ - Anything shown only in a vanishing toast must also be findable elsewhere, such as activity or the Inbox.
47
+
48
+ ## Do
49
+
50
+ - Do send everything that needs action to the Inbox.
51
+ - Do give people control over email frequency.
52
+
53
+ ## Don't
54
+
55
+ - Don't add a bell next to the Inbox.
56
+ - Don't use a toast for errors that need a follow-up.
57
+
58
+ ## Related
59
+
60
+ - Agents acting on your behalf
61
+ - Undo and destructive actions
62
+ - Empty, loading and error states
63
+ - Inbox (triage)
64
+ - Toast
65
+ - Banner (inline alert)
@@ -0,0 +1,100 @@
1
+ # Notifications vs inbox vs toast
2
+
3
+ The rule for where a message goes: toast for your own action, banner for a condition on this page, inbox for anything that needs you later, activity for the rest.
4
+
5
+ Kind: component · Group: Feedback and overlays · Status: built, import from @twoux/ui
6
+
7
+ ## When to use
8
+
9
+ - Designing any new message, to choose its component before writing it.
10
+ - Reviewing a screen that shows several messages at once, to remove the ones that belong elsewhere.
11
+
12
+ ## When not to use
13
+
14
+ - Field-level validation, which always sits next to the field. Use inline field errors and the Banner strip.
15
+ - Loading feedback. Use the Loading skeleton or an inline spinner.
16
+
17
+ ## Structure
18
+
19
+ 1. Question 1: did the person just do this, here? Toast
20
+ 2. Question 2: is it a condition on the page in view? Banner
21
+ 3. Question 3: does it need someone to act, now or later? Inbox
22
+ 4. Question 4: must it stop the task before it continues? Confirmation dialog
23
+ 5. Otherwise: the record's Activity tab, no message
24
+
25
+ ## Guidelines
26
+
27
+ - Toast: passive confirmation of the person's own action in the current view; disappears; may carry Undo.
28
+ - Banner: a condition in context that persists until resolved; may carry one action.
29
+ - Inbox: events the person did not trigger, or that complete later, that need a response; persists until done or snoozed.
30
+ - Confirmation dialog: only when the task must stop for a decision that cannot be undone.
31
+ - Activity: events that need nothing. Logging them instead of notifying prevents alert fatigue.
32
+ - Errors never go in a toast.
33
+ - Keep event notifications people didn't trigger (reminders, assignments) apart from toasts. Notifications get their own component, which comes in at the top right and can stack, separate from toasts.
34
+
35
+ ## Content
36
+
37
+ - Whatever the component, say what happened and what to do next, in sentence case, without apologies.
38
+ - Use the same name for the same record across toast, inbox and activity.
39
+
40
+ ## Accessibility
41
+
42
+ - Toasts use role="status"; blocking errors use role="alert"; the dialog uses role="alertdialog" (4.1.3).
43
+ - Only dialogs take focus; everything else is announced without moving focus.
44
+ - Nothing important is only in a timed message (2.2.1); the Inbox and Activity keep a permanent record.
45
+ - Non-urgent updates can be postponed by the person (snooze), which supports 2.2.4 Interruptions.
46
+
47
+ ## Keyboard
48
+
49
+ | Keys | Action |
50
+ |---|---|
51
+ | ⌘Z | Undo after a toast |
52
+ | G I | Open the Inbox |
53
+
54
+ ## Examples
55
+
56
+ ### choose
57
+
58
+ ```tsx
59
+ import { Badge, RadioField, RadioGroup } from "@twoux/ui";
60
+ import { useState } from "react";
61
+
62
+ export const meta = { title: "Where should this message go?", description: "Answer in order. The first yes decides: toast, banner, Inbox or confirmation dialog. If none apply, log it in the record's Activity and send nothing.", order: 1 };
63
+
64
+ const Q = [
65
+ { id: "own", q: "Did the person just do this, here?", yes: "Toast", why: "Confirms their own action and disappears; can carry Undo." },
66
+ { id: "cond", q: "Is it a condition on the page in view?", yes: "Banner", why: "Stays in context until resolved; at most one action." },
67
+ { id: "act", q: "Does it need someone to act, now or later?", yes: "Inbox", why: "Persists until done or snoozed, even if they weren't here." },
68
+ { id: "stop", q: "Must it stop the task before it continues?", yes: "Confirmation dialog", why: "Only when the decision can't be undone." },
69
+ ];
70
+
71
+ export default function Example() {
72
+ const [answers, setAnswers] = useState<Record<string, string>>({});
73
+ const first = Q.find((q) => answers[q.id] === "yes");
74
+ const allNo = Q.every((q) => answers[q.id] === "no");
75
+ return (
76
+ <div className="grid w-full max-w-xl gap-3">
77
+ {Q.map((q, i) => (
78
+ <fieldset key={q.id} className="grid gap-1.5 rounded-sm border border-line bg-surface p-3" disabled={i > 0 && answers[Q[i - 1].id] !== "no"}>
79
+ <legend className="sr-only">{q.q}</legend>
80
+ <span className="text-base font-medium text-ink">{i + 1}. {q.q}</span>
81
+ <RadioGroup value={answers[q.id] ?? ""} onValueChange={(v) => setAnswers((a) => ({ ...Object.fromEntries(Object.entries(a).filter(([k]) => Q.findIndex((x) => x.id === k) < i)), [q.id]: v }))} className="flex gap-4">
82
+ <RadioField id={`${q.id}-y`} value="yes" label="Yes" />
83
+ <RadioField id={`${q.id}-n`} value="no" label="No" />
84
+ </RadioGroup>
85
+ </fieldset>
86
+ ))}
87
+ <p role="status" className="flex items-center gap-2 text-base text-ink-2">
88
+ {first ? <><Badge>{first.yes}</Badge>{first.why}</> : allNo ? <><Badge variant="secondary">Activity only</Badge>Log it on the record; don't notify anyone.</> : "Answer the questions in order."}
89
+ </p>
90
+ </div>
91
+ );
92
+ }
93
+ ```
94
+
95
+ ## Related
96
+
97
+ - Toast
98
+ - Banner (inline alert)
99
+ - Inbox (triage)
100
+ - Confirmation dialog
@@ -0,0 +1,114 @@
1
+ # Onboarding step
2
+
3
+ One step of first-run setup on a plain page: why we ask, the fields, which step this is, and Back, Skip and Continue.
4
+
5
+ Kind: component · Group: Navigation · Status: built, import from @twoux/ui
6
+
7
+ ## When to use
8
+
9
+ - A few setup steps that the product cannot guess, such as workspace name and who to invite.
10
+ - Steps that depend on each other, where each answer shapes the next.
11
+
12
+ ## When not to use
13
+
14
+ - Teaching the product with a tour or video. Show it in the product and use contextual help (workspace-onboarding).
15
+ - A long form. Ask only what is needed to start and let people finish the rest later in Settings.
16
+
17
+ ## Structure
18
+
19
+ 1. "Step n of N" text (with Optional when it is)
20
+ 2. Heading (receives focus)
21
+ 3. Why we ask, in one line
22
+ 4. Fields
23
+ 5. Back, Skip (optional steps), Continue
24
+ 6. Progress dots (decoration only)
25
+ 7. Aside panel (optional, wide screens)
26
+
27
+ ## Guidelines
28
+
29
+ - Keep it to a few steps. Each step says why it asks.
30
+ - Optional steps say Optional and offer Skip. Required steps do not offer it.
31
+ - Never disable Continue. Validate when it is pressed and put the error under the field.
32
+ - Say "Step 2 of 5" in text. Dots alone do not tell people how much is left.
33
+ - Move focus to the heading when the step changes, so a screen reader says where the person is.
34
+ - Use a plain background. No gradients, no illustration, no auto-playing video.
35
+ - Keep what was entered when going Back. Never ask for the same thing twice.
36
+
37
+ ## Content
38
+
39
+ - Heading: a short question or task, such as "Name your workspace".
40
+ - Why line: "Teammates see this name when you invite them."
41
+ - Skip, not "Do this later" or "Not now", on optional steps.
42
+
43
+ ## Accessibility
44
+
45
+ - The "Step n of N" text is real text; the dots are hidden from assistive technology (SC 1.4.1).
46
+ - The heading has tabindex -1 and receives focus on each step; the section is labelled by it (SC 2.4.3, 2.4.6).
47
+ - Errors are shown under the field and linked to it (SC 3.3.1).
48
+
49
+ ## Keyboard
50
+
51
+ | Keys | Action |
52
+ |---|---|
53
+ | Tab | Move through the fields, Back, Skip and Continue in order |
54
+ | Enter | In a text field, press Continue |
55
+ | Shift+Tab | Move back, starting from the heading on a new step |
56
+
57
+ ## Examples
58
+
59
+ ### workspace-setup
60
+
61
+ ```tsx
62
+ import { Field, FieldDescription, FieldError, FieldLabel, Input, OnboardingStep } from "@twoux/ui";
63
+ import { useState } from "react";
64
+
65
+ export const meta = { title: "Set up a workspace", description: "Three steps. The name is required, inviting is optional. What was typed stays when you go Back.", order: 1 };
66
+
67
+ export default function Example() {
68
+ const [step, setStep] = useState(1);
69
+ const [name, setName] = useState("");
70
+ const [invites, setInvites] = useState("");
71
+ const [error, setError] = useState("");
72
+ const next = () => {
73
+ if (step === 1 && !name.trim()) { setError("Enter a name for your workspace, for example TwoUX."); return; }
74
+ setError("");
75
+ setStep((s) => Math.min(3, s + 1));
76
+ };
77
+ return (
78
+ <div className="flex w-full justify-center">
79
+ {step === 1 && (
80
+ <OnboardingStep step={1} total={3} title="Name your workspace" description="Teammates see this name when you invite them." onContinue={next}>
81
+ <Field data-invalid={!!error || undefined}>
82
+ <FieldLabel htmlFor="ws-name">Workspace name</FieldLabel>
83
+ <Input id="ws-name" value={name} aria-invalid={!!error || undefined} autoComplete="organization" onChange={(e) => { setName(e.target.value); setError(""); }} />
84
+ {error && <FieldError>{error}</FieldError>}
85
+ </Field>
86
+ </OnboardingStep>
87
+ )}
88
+ {step === 2 && (
89
+ <OnboardingStep step={2} total={3} title="Invite your team" description="They get an email with a link. You can also do this later in Settings." optional onBack={() => setStep(1)} onSkip={() => setStep(3)} onContinue={next}>
90
+ <Field>
91
+ <FieldLabel htmlFor="ws-invites">Email addresses</FieldLabel>
92
+ <Input id="ws-invites" type="email" value={invites} placeholder="sam@twoux.example" onChange={(e) => setInvites(e.target.value)} />
93
+ <FieldDescription>Separate several addresses with commas.</FieldDescription>
94
+ </Field>
95
+ </OnboardingStep>
96
+ )}
97
+ {step === 3 && (
98
+ <OnboardingStep step={3} total={3} title="You are ready" description={`${name || "Your workspace"} is set up.`} continueLabel="Open workspace" onBack={() => setStep(2)} onContinue={() => setStep(1)}>
99
+ <p className="text-base text-ink-2">{invites ? `An invitation will go to ${invites}.` : "You have not invited anyone yet."}</p>
100
+ </OnboardingStep>
101
+ )}
102
+ </div>
103
+ );
104
+ }
105
+ ```
106
+
107
+ ## Related
108
+
109
+ - Onboarding to an empty workspace
110
+ - Stepper
111
+ - Text input
112
+ - Form validation
113
+ - Setup checklist
114
+ - Sign in
@@ -0,0 +1,108 @@
1
+ # Order book
2
+
3
+ Resting sell and buy orders either side of the spread, each with a bar for the running total, so people see where the price is likely to meet resistance and can pick a price to trade at.
4
+
5
+ Kind: component · Group: Data visualisation · Status: built, import from @twoux/ui
6
+
7
+ ## When to use
8
+
9
+ - A trading screen where people place limit orders and need the prices other people are offering.
10
+ - Showing how much can be bought or sold before the price moves, through the running totals.
11
+
12
+ ## When not to use
13
+
14
+ - For a price over time. Use the TwoUX Candlestick chart or Price chart.
15
+ - For a short list of prices with today's change. Use the TwoUX Change list.
16
+
17
+ ## Structure
18
+
19
+ 1. Group by: − and + to widen or narrow price groups
20
+ 2. Column headings with units: Price, Amount, Total
21
+ 3. Sell orders above, best (lowest) price next to the spread
22
+ 4. Middle line: last price with its direction, and the spread
23
+ 5. Buy orders below, best (highest) price next to the spread
24
+ 6. Depth bar behind each row: running total against the deeper side
25
+
26
+ ## Guidelines
27
+
28
+ - Sells above, buys below, best prices next to the spread; side is carried by position, the row's name and colour together.
29
+ - Sell prices are red and buy prices green, the way depth charts draw asks and bids.
30
+ - Bars show the running total against the deeper side, so walls stand out.
31
+ - Choosing a row puts its price in the order form; it never places an order.
32
+ - Rows keep their place as prices update and never flash; only the values change.
33
+ - Group sell prices up and buy prices down, so a group never shows a better price than its orders.
34
+
35
+ ## Content
36
+
37
+ - Put the unit in each heading: Price (USDC), Amount (BTC).
38
+ - Say Spread, not a symbol, and give it in the price's unit.
39
+
40
+ ## Accessibility
41
+
42
+ - 1.4.1 Use of Color: each row is named Sell or Buy with its amount, price and total, and the two sides are separate lists.
43
+ - 2.1.1 Keyboard: the book is one tab stop; arrow keys move between rows and Enter uses the price.
44
+
45
+ ## Keyboard
46
+
47
+ | Keys | Action |
48
+ |---|---|
49
+ | Tab | Focus the book |
50
+ | ↑ ↓ | Move to the previous or next price |
51
+ | Page Up Page Down | Move five prices |
52
+ | Home End | Jump to the first or last price |
53
+ | Enter Space | Use the price in the order form |
54
+
55
+ ## Examples
56
+
57
+ ### live
58
+
59
+ ```tsx
60
+ import { OrderBook, type OrderLevel, TextField } from "@twoux/ui";
61
+ import { useMemo, useState } from "react";
62
+
63
+ export const meta = { title: "Pick a price for the order form", description: "Sells above and buys below the spread, each row's bar showing the running total. Choose a row, by click or with the arrow keys and Enter, to put its price in the limit price field. The − and + buttons group prices.", order: 1 };
64
+
65
+ // Levels from a seeded random sequence, so the book looks real and stays the same on every load.
66
+ function levels(mid: number, dir: 1 | -1, seed: number): OrderLevel[] {
67
+ const rand = () => ((seed = (seed * 16807) % 2147483647) / 2147483647);
68
+ return Array.from({ length: 40 }, (_, i) => ({ price: mid + dir * 0.4 * (i + 1) * (1 + rand()), amount: Number((rand() ** 3 * 0.9 + 0.0002).toFixed(5)) }));
69
+ }
70
+ const money = (n: number) => n.toLocaleString("en-GB", { minimumFractionDigits: 2, maximumFractionDigits: 2 });
71
+ const coin = (n: number) => n.toLocaleString("en-GB", { minimumFractionDigits: 5, maximumFractionDigits: 5 });
72
+ const STEPS = [0.01, 0.1, 1, 10];
73
+
74
+ export default function Example() {
75
+ const asks = useMemo(() => levels(70_237.17, 1, 11), []);
76
+ const bids = useMemo(() => levels(70_237.17, -1, 29), []);
77
+ const [step, setStep] = useState(0.01);
78
+ const [limit, setLimit] = useState("");
79
+ return (
80
+ <div className="grid w-full max-w-3xl gap-6 sm:grid-cols-[18rem_1fr]">
81
+ <OrderBook
82
+ aria-label="BTC-USDC order book"
83
+ asks={asks}
84
+ bids={bids}
85
+ rows={8}
86
+ format={money}
87
+ formatAmount={coin}
88
+ priceLabel="Price (USDC)"
89
+ amountLabel="Amount (BTC)"
90
+ step={step}
91
+ steps={STEPS}
92
+ onStepChange={setStep}
93
+ last={{ price: 70_237.17, direction: "up" }}
94
+ onSelectPrice={(p) => setLimit(p.toFixed(2))}
95
+ />
96
+ <div className="grid content-start">
97
+ <TextField label="Limit price" suffix="USDC" inputMode="decimal" value={limit} onChange={(e) => setLimit(e.target.value)} hint="Choose a row in the order book to use its price." />
98
+ </div>
99
+ </div>
100
+ );
101
+ }
102
+ ```
103
+
104
+ ## Related
105
+
106
+ - Candlestick chart
107
+ - Change list
108
+ - Price chart
@@ -0,0 +1,96 @@
1
+ # Page header
2
+
3
+ The page title on the left and its actions on the right, with exactly one primary action per view.
4
+
5
+ Kind: component · Group: Navigation · Status: built, import from @twoux/ui
6
+
7
+ ## When to use
8
+
9
+ - At the top of every page in the canvas, to name the page and hold actions that affect the whole page.
10
+ - When a page has a main next step, such as creating a listing.
11
+
12
+ ## When not to use
13
+
14
+ - Actions on selected rows. Use the bulk action bar.
15
+ - Actions on one record shown in the side panel. Use the panel's footer.
16
+ - Global tools such as search or Ask. Those live in the top bar of the App shell.
17
+
18
+ ## Structure
19
+
20
+ 1. Title (h1)
21
+ 2. Actions, right-aligned: secondary actions ("Export ⇧E") then the one primary action ("New listing C")
22
+ 3. Optional: view tabs below (All 24, Live 14, Drafts 3, Closed 7)
23
+
24
+ ## Guidelines
25
+
26
+ - One primary button per view; every other action is secondary or in a menu.
27
+ - The primary action sits rightmost, nearest the content edge, with secondary actions to its left.
28
+ - Each button shows its shortcut (C, ⇧E) so people learn it by using it.
29
+ - Three actions at most in the header; more go into a menu.
30
+ - Title and actions wrap to two lines on narrow widths rather than truncating.
31
+ - View tabs (segmented counts) sit under the title and filter the same data; they are not navigation.
32
+
33
+ ## Content
34
+
35
+ - Title is a short noun in sentence case that matches the sidebar item: "Listings".
36
+ - Action labels start with a verb and name the object: "New listing", "Export".
37
+ - Tab labels are one or two words with a count.
38
+
39
+ ## Accessibility
40
+
41
+ - The title is the page's only h1 and matches the document title (2.4.2, 2.4.6).
42
+ - Shortcut hints are part of the button's accessible description, not its name, so voice users can say the label (4.1.2).
43
+ - Buttons are at least 24px tall (28 to 32px in TwoUX) (2.5.8).
44
+ - After navigation, focus can be moved to the title so screen reader users start at the page (2.4.3).
45
+
46
+ ## Keyboard
47
+
48
+ | Keys | Action |
49
+ |---|---|
50
+ | C | Primary create action (New listing) |
51
+ | ⇧E | Export |
52
+ | Tab | Move from title area to actions, left to right |
53
+
54
+ ## Examples
55
+
56
+ ### listings
57
+
58
+ ```tsx
59
+ import { Button, DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger, PageHeader, Tabs, TabsList, TabsTrigger } from "@twoux/ui";
60
+ import { Download, Ellipsis, Plus } from "lucide-react";
61
+
62
+ export const meta = { title: "Title, actions and view tabs", description: "The one primary action sits rightmost with its shortcut; secondary actions to its left; anything beyond three goes in a menu. Title and actions wrap rather than truncate.", order: 1 };
63
+
64
+ export default function Example() {
65
+ return (
66
+ <div className="w-full rounded-sm border border-line bg-surface">
67
+ <PageHeader
68
+ title="Listings"
69
+ description="24 listings across 6 teams"
70
+ actions={
71
+ <>
72
+ <DropdownMenu>
73
+ <DropdownMenuTrigger asChild><Button variant="ghost" size="icon" aria-label="More actions"><Ellipsis /></Button></DropdownMenuTrigger>
74
+ <DropdownMenuContent align="end"><DropdownMenuItem>Import from CSV</DropdownMenuItem><DropdownMenuItem>Archive closed listings</DropdownMenuItem></DropdownMenuContent>
75
+ </DropdownMenu>
76
+ <Button variant="outline" shortcut="⇧E"><Download />Export</Button>
77
+ <Button shortcut="C"><Plus />New listing</Button>
78
+ </>
79
+ }
80
+ tabs={
81
+ <Tabs defaultValue="all">
82
+ <TabsList aria-label="Views"><TabsTrigger value="all" count={24}>All</TabsTrigger><TabsTrigger value="live" count={14}>Live</TabsTrigger><TabsTrigger value="drafts" count={3}>Drafts</TabsTrigger><TabsTrigger value="closed" count={7}>Closed</TabsTrigger></TabsList>
83
+ </Tabs>
84
+ }
85
+ />
86
+ </div>
87
+ );
88
+ }
89
+ ```
90
+
91
+ ## Related
92
+
93
+ - App shell and sidebar navigation
94
+ - Breadcrumb
95
+ - Command palette (⌘K)
96
+ - Shortcut sheet (?)