@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,114 @@
1
+ # Sign in
2
+
3
+ Provider buttons first, then email. Single sign-on is found from the email domain, and an email link means no password to remember.
4
+
5
+ Kind: component · Group: Actions and inputs · Status: built, import from @twoux/ui
6
+
7
+ ## When to use
8
+
9
+ - The first screen of an account: sign in and create account in one flow.
10
+ - Letting people choose how they sign in: a provider, single sign-on or an email link.
11
+
12
+ ## When not to use
13
+
14
+ - Asking for anything beyond how to sign in. Setup questions belong in Onboarding step (onboarding-step).
15
+ - Gating content that people could read first. Show it before asking them to sign in.
16
+
17
+ ## Structure
18
+
19
+ 1. Provider buttons (logo and "Continue with" name)
20
+ 2. Divider ("or")
21
+ 3. Work email field
22
+ 4. Continue button (becomes "Continue with SSO" for SSO domains)
23
+ 5. Error under the field
24
+
25
+ ## Guidelines
26
+
27
+ - Put provider buttons first, then email. One primary action: the email button.
28
+ - Find single sign-on from the email domain. Say "twoux.example signs in with single sign-on" and change the button to Continue with SSO.
29
+ - Use an email link so there is no password to remember or retype.
30
+ - Put errors under the field and say what to do, for example "This email uses Google to sign in. Continue with Google instead."
31
+ - Do not reveal which part was wrong, or whether an account exists, unless the product already shows that elsewhere. Use the same message either way.
32
+ - Set autocomplete on every field (email, current-password, one-time-code) so browsers and password managers can fill them.
33
+ - Validate on press, not on every keystroke, and keep the email people typed after an error.
34
+
35
+ ## Content
36
+
37
+ - Button: "Continue with Google", "Continue with email", "Continue with SSO".
38
+ - Field error: "Enter your work email, for example sam@company.com."
39
+ - Do not write "Wrong password" or "No account found". Write what to try next.
40
+
41
+ ## Accessibility
42
+
43
+ - The email field has a visible label and the right type and autocomplete values (SC 1.3.5, 3.3.2).
44
+ - The error is linked to the field with aria-invalid and the field description, and is announced when it appears (SC 3.3.1, 4.1.3).
45
+ - Signing in never depends on remembering or transcribing a password alone: providers and email links are alternatives (SC 3.3.8).
46
+
47
+ ## Keyboard
48
+
49
+ | Keys | Action |
50
+ |---|---|
51
+ | Tab | Move through the provider buttons, the email field and the primary button |
52
+ | Enter | In the email field, submit the form |
53
+ | Space or Enter | Press the focused provider button |
54
+
55
+ ## Examples
56
+
57
+ ### error
58
+
59
+ ```tsx
60
+ import { SignInOptions } from "@twoux/ui";
61
+
62
+ export const meta = { title: "Say what to do next", description: "The error sits under the field and names the way in. It does not say which part was wrong.", order: 2 };
63
+
64
+ export default function Example() {
65
+ return (
66
+ <div className="w-full max-w-sm">
67
+ <SignInOptions
68
+ providers={["google"]}
69
+ error="This email signs in with Google. Continue with Google instead."
70
+ onProvider={() => {}}
71
+ onEmail={() => {}}
72
+ />
73
+ </div>
74
+ );
75
+ }
76
+ ```
77
+
78
+ ### sign-in
79
+
80
+ ```tsx
81
+ import { SignInOptions, toast, type ProviderId } from "@twoux/ui";
82
+ import { useState } from "react";
83
+
84
+ export const meta = { title: "Providers, then email", description: "Providers first. An email on a single sign-on domain changes the button to Continue with SSO. Try sam@twoux.example.", order: 1 };
85
+
86
+ export default function Example() {
87
+ const [busy, setBusy] = useState<ProviderId | "email" | "sso" | undefined>();
88
+ const run = (what: ProviderId | "email" | "sso", message: string) => {
89
+ setBusy(what);
90
+ setTimeout(() => { setBusy(undefined); toast.success(message); }, 900);
91
+ };
92
+ return (
93
+ <div className="grid w-full max-w-sm gap-4">
94
+ <h1 className="text-xl font-semibold text-ink">Sign in to TwoUX</h1>
95
+ <SignInOptions
96
+ providers={["google", "microsoft"]}
97
+ ssoDomains={["twoux.example"]}
98
+ busy={busy}
99
+ onProvider={(p) => run(p, "Opened the sign-in window")}
100
+ onEmail={(email) => run("email", `Sign-in link sent to ${email}. Check your inbox.`)}
101
+ onSso={(domain) => run("sso", `Continuing with ${domain}`)}
102
+ />
103
+ </div>
104
+ );
105
+ }
106
+ ```
107
+
108
+ ## Related
109
+
110
+ - Provider logo
111
+ - Onboarding step
112
+ - Text input
113
+ - Form validation
114
+ - Error messages
@@ -0,0 +1,55 @@
1
+ # Spacing
2
+
3
+ Spacing sits on a 4px grid with 2px for fine detail. Space shows relationship: closer means related. Apps use role utilities: fixed ones inside controls that never change, and region ones between parts that tighten by themselves in a workspace.
4
+
5
+ Kind: foundation · Group: Visual foundation
6
+
7
+ ## Principles
8
+
9
+ - Proximity is meaning. Related items sit closer than unrelated ones, and items of the same kind share the same gap.
10
+ - 4px grid. Spacing is built from small multiples, from 2px to 40px on a 4px grid.
11
+ - Spacing comes from components and tokens, not from pixel values typed into a page.
12
+
13
+ ## Guidelines
14
+
15
+ - Use the role utilities, not numbers. Inside a control: gap-hair (2px), gap-tight (4px), gap-control (6px, icon to label in buttons, menu items and badges only), gap-inline (8px). Between parts: gap-group. Inside a card, pane or panel: p-region. Page sides and top: px-page, pt-page-top.
16
+ - Fixed roles never change; region roles change with the layout. On an inset canvas a region is padded 16px, groups sit 16px apart and the page has 24px sides and a 20px top. In a workspace (AppShell frame="flush") regions are padded 8px, groups 12px, the page 12px and 8px, and regions meet at a 1px line.
17
+ - Inside a group (a form field's label, input and help text) use 4 to 8px; between groups use gap-group so the grouping is visible.
18
+ - Below 860px every page, workspace or not, uses the inset values with 16px page sides, so stacked panes do not crowd.
19
+ - The numeric steps (p-2, gap-3) are for TwoUX's own components. twoux check warns on them in app code and names the role to use.
20
+ - Every value sits on the 4px grid except two documented ones: 6px between an icon and its label in a row (gap-control), and the padding inside controls generated from shadcn, which stays as shadcn ships it so every button, input and menu keeps its shape.
21
+
22
+ ## Accessibility
23
+
24
+ - Spacing between small targets counts: a target under 24 by 24px still passes if a 24px circle around it does not overlap another target.
25
+ - Content must reflow at 320 CSS px; fixed gaps must not force horizontal scrolling outside tables.
26
+
27
+ ## Do
28
+
29
+ - Use the same gap between all rows of a property list.
30
+ - Let the component own its internal padding.
31
+
32
+ ## Don't
33
+
34
+ - Do not add margin to a component from outside to nudge it into place.
35
+ - Do not invent in-between values like 13px or 18px.
36
+
37
+ ## Tokens
38
+
39
+ | Token | Value | Use |
40
+ |---|---|---|
41
+ | `--spacing-hair` | `2px` | Fixed. Icon to text nudges, keycap padding, badge inset (gap-hair, p-hair) |
42
+ | `--spacing-tight` | `4px` | Fixed. Inside a control; label to help text |
43
+ | `--spacing-control` | `6px` | Fixed. Icon to label inside buttons, menu items and badges only |
44
+ | `--spacing-inline` | `8px` | Fixed. Between controls in a row; field label to input |
45
+ | `--spacing-group` | `16px · 12px in a workspace` | Region. Between groups and sections (gap-group) |
46
+ | `--spacing-region` | `16px · 8px in a workspace` | Region. Inside a card, pane or panel (p-region) |
47
+ | `--spacing-page` | `24px · 12px in a workspace` | Region. Page sides (px-page) |
48
+ | `--spacing-page-top` | `20px · 8px in a workspace` | Region. Page top (pt-page-top) |
49
+ | `--spacing-region-gap` | `16px · 1px in a workspace` | Region. Between cards on a page; in a workspace regions meet at a divider |
50
+
51
+ ## Related
52
+
53
+ - Content density
54
+ - Layout
55
+ - Design tokens
@@ -0,0 +1,100 @@
1
+ # Split button
2
+
3
+ A button joined to a chevron: the button runs the usual action (Import data), the chevron opens its variants (Import all, Import selection).
4
+
5
+ Kind: component · Group: Actions and inputs · Status: built, import from @twoux/ui
6
+
7
+ ## When to use
8
+
9
+ - One action is the usual choice and a few close variants of it are needed now and then, such as Import data with Import all and Import selection.
10
+ - Space is tight and the variants belong together, as in a table header toolbar.
11
+
12
+ ## When not to use
13
+
14
+ - For unrelated actions. Use separate buttons, or a Dropdown menu behind More actions.
15
+ - When no option is clearly the usual one. Use a menu button (Button with a chevron) so people choose every time.
16
+ - For a destructive main action. Put destructive variants in the menu, never on the main part.
17
+
18
+ ## Structure
19
+
20
+ 1. Main part: icon (optional) and label
21
+ 2. Divider
22
+ 3. Chevron part with its own name, opening a menu
23
+
24
+ ## Guidelines
25
+
26
+ - Use SplitButton instead of joining a Button and a menu by hand, so the parts, divider, names and menu alignment are the same everywhere.
27
+ - The main part runs the action at once; the chevron only opens the menu. Repeat the main action first in the menu so the list is complete.
28
+ - Outline by default. Use the primary variant only when it is the view's one primary action.
29
+ - Disable a variant that can't run yet (Import selection with nothing selected) rather than hiding it, and say why nearby.
30
+
31
+ ## Content
32
+
33
+ - Main label: a verb and an object, Import data. Menu items: short variants, Import all, Import selection.
34
+ - Name the chevron by what it opens: More import options.
35
+
36
+ ## Accessibility
37
+
38
+ - Two buttons in a group: the main button, then a menu button with aria-haspopup, aria-expanded and its own accessible name.
39
+ - 2.5.8 Target size: the chevron part is at least 24px wide.
40
+
41
+ ## Keyboard
42
+
43
+ | Keys | Action |
44
+ |---|---|
45
+ | Tab | Move from the main part to the chevron |
46
+ | Enter / Space | Run the main action, or open the menu from the chevron |
47
+ | ↓ (chevron) | Open the menu on the first item |
48
+ | Esc | Close the menu and return to the chevron |
49
+
50
+ ## Examples
51
+
52
+ ### import
53
+
54
+ ```tsx
55
+ import { DropdownMenuItem, DropdownMenuSeparator, SplitButton, toast } from "@twoux/ui";
56
+ import { FileSpreadsheet, Upload } from "lucide-react";
57
+
58
+ export const meta = { title: "Import data", description: "The main part runs the usual action. The chevron, named “More import options”, opens its variants; Import selection is disabled until rows are selected.", order: 1 };
59
+
60
+ export default function Example() {
61
+ return (
62
+ <div className="flex flex-wrap items-center gap-3">
63
+ <SplitButton
64
+ onClick={() => toast("Choose a CSV or Excel file to import")}
65
+ menuLabel="More import options"
66
+ menu={
67
+ <>
68
+ <DropdownMenuItem onSelect={() => toast("Importing all rows from the file")}>Import all</DropdownMenuItem>
69
+ <DropdownMenuItem disabled>Import selection</DropdownMenuItem>
70
+ <DropdownMenuSeparator />
71
+ <DropdownMenuItem onSelect={() => toast("Downloaded the import template")}><FileSpreadsheet />Download template</DropdownMenuItem>
72
+ </>
73
+ }
74
+ >
75
+ <Upload />Import data
76
+ </SplitButton>
77
+ <SplitButton
78
+ variant="default"
79
+ onClick={() => toast.success("Published 12 products")}
80
+ menuLabel="More publish options"
81
+ menu={
82
+ <>
83
+ <DropdownMenuItem onSelect={() => toast.success("Published 12 products")}>Publish now</DropdownMenuItem>
84
+ <DropdownMenuItem onSelect={() => toast("Scheduled for 9:00 tomorrow")}>Schedule for tomorrow</DropdownMenuItem>
85
+ </>
86
+ }
87
+ >
88
+ Publish 12 products
89
+ </SplitButton>
90
+ </div>
91
+ );
92
+ }
93
+ ```
94
+
95
+ ## Related
96
+
97
+ - Button
98
+ - Dropdown menu
99
+ - Dense table
100
+ - CSV import
@@ -0,0 +1,82 @@
1
+ # Stat card with comparison
2
+
3
+ One number that matters, with its change against a named previous period shown by arrow, sign and colour, and an optional faint previous-period line.
4
+
5
+ Kind: component · Group: Data visualisation · Status: built, import from @twoux/ui
6
+
7
+ ## When to use
8
+
9
+ - A quick view of how one metric is doing, alone or beside related stats.
10
+ - The number is the answer; a chart would add reading time without insight.
11
+
12
+ ## When not to use
13
+
14
+ - For one metric over time. Use the TwoUX Line chart card.
15
+ - For plain text or labels.
16
+
17
+ ## Structure
18
+
19
+ 1. Label with period ("Applications · last 30 days")
20
+ 2. Value
21
+ 3. Change: arrow, signed percentage, comparison text
22
+ 4. Optional mini chart: current solid, previous dashed grey
23
+ 5. Legend (This period, Previous period)
24
+
25
+ ## Guidelines
26
+
27
+ - The number leads; label and change are secondary.
28
+ - Show change with an arrow, a sign and colour together, never colour alone.
29
+ - Say what the change compares against, "vs 264 the 30 days before".
30
+ - Draw the previous period as a dashed grey line behind the current one.
31
+ - Loading shows a skeleton of the number and change; an error shows "Couldn't load" with Try again in the card; no data shows a dash and "No data for this period", never 0.
32
+ - When the stat has a target, say it in text ("18 of 25 roles filled") rather than a ring or donut.
33
+
34
+ ## Content
35
+
36
+ - Label with the metric and the period, and include units.
37
+ - Round the value for reading, keep the exact value in the tooltip.
38
+ - Keep any description to two lines; longer text is truncated with the full text in a tooltip.
39
+
40
+ ## Accessibility
41
+
42
+ - 1.4.1 Use of Color: good or bad change is also shown by arrow and sign.
43
+ - 1.1.1 Non-text Content: the mini chart has a text alternative or is hidden because the figures are in text.
44
+ - 1.4.11 Non-text Contrast: the arrow and line reach 3:1 unless the text carries the same information.
45
+ - A missing value reads "No data for this period" to screen readers, not "dash".
46
+
47
+ ## Keyboard
48
+
49
+ | Keys | Action |
50
+ |---|---|
51
+ | Tab | Focus the card when it links to a report |
52
+
53
+ ## Examples
54
+
55
+ ### kpis
56
+
57
+ ```tsx
58
+ import { StatCompare } from "@twoux/ui";
59
+
60
+ export const meta = { title: "Key numbers", description: "One number, its change against a named period, and whether that change is good, said in colour and in words. Time to hire going down is good; applications going down is not.", order: 1 };
61
+
62
+ export default function Example() {
63
+ return (
64
+ <div className="grid w-full grid-cols-3 gap-3 max-md:grid-cols-1">
65
+ {[
66
+ { label: "Applications this month", value: "595", change: "+12%", period: "October", good: true, spark: [42, 48, 51, 47, 55, 60, 58, 66] },
67
+ { label: "Median time to hire", value: "23 days", change: "-4 days", period: "last quarter", good: true, spark: [31, 30, 29, 27, 27, 25, 24, 23] },
68
+ { label: "Offer acceptance", value: "71%", change: "-6 pts", period: "last quarter", good: false, spark: [80, 79, 77, 78, 75, 74, 72, 71] },
69
+ ].map((s) => (
70
+ <div key={s.label} className="rounded-sm border border-line bg-surface p-4 shadow-card"><StatCompare {...s} /></div>
71
+ ))}
72
+ </div>
73
+ );
74
+ }
75
+ ```
76
+
77
+ ## Related
78
+
79
+ - Line chart card
80
+ - Breakdown table
81
+ - Data visualisation colours
82
+ - Usage meter
@@ -0,0 +1,75 @@
1
+ # Static dashboard
2
+
3
+ A fixed overview page where the team sees the same items in the same order, most important first, answering the day's questions at a glance.
4
+
5
+ Kind: pattern · Group: Patterns: data and dashboards
6
+
7
+ **Problem:** A hiring lead opens the overview to answer a few questions fast: how many applications came in, which listings need attention, what is stuck. If the page is a wall of charts, or each team lays it out differently, the answer takes minutes instead of seconds.
8
+
9
+ **Solution:** Lay the page out in three bands on a 12-column grid: a top band with page actions and the headline numbers, a middle band of charts and short lists that explain those numbers, and a bottom band for setup help and updates. The order is set by the product team from research and does not change per person.
10
+
11
+ **Built from:** Page header, Segmented control, Stat card with comparison, Line chart card, Funnel, List table, Status badge, Banner (inline alert), Card, Empty state, Loading skeleton, Error state (failed to load)
12
+
13
+ ## How it works
14
+
15
+ 1. Write down the three to five questions the page must answer, such as "Are applications up or down this week?" and "Which listings expire in the next 7 days?". Every item must answer one of them.
16
+ 2. Top band: the page header with page-level actions (period control, Post a listing) and a row of up to four key metrics with comparisons.
17
+ 3. Middle band: charts and short lists that give the context behind the key metrics, such as applications per week and candidates waiting over 5 days.
18
+ 4. Bottom band: getting started help for new workspaces, and short notes on product updates with a link to read more.
19
+ 5. Size items from the 12-column grid: 3 columns for a small item, 6 for medium, 9 for large, 12 for full width. Height follows the content.
20
+ 6. Give every item a way out to the full view ("View all 23 listings") so the dashboard stays a summary.
21
+
22
+ ## When to use
23
+
24
+ - For a team-wide overview where everyone should see the same numbers in the same order, such as the weekly hiring report shared with department heads.
25
+ - When the order of items matters to getting the job done, for example during a hiring campaign where the funnel must stay in view.
26
+ - As the default home of a workspace before you know enough about how people want to rearrange it.
27
+
28
+ ## When not to use
29
+
30
+ - When people in different roles need different items, such as a recruiter watching their own pipeline and a lead watching cost per hire. Use the configurable dashboard.
31
+ - To browse, filter or edit records. Link to the List table instead of embedding a full table.
32
+ - For a single metric explored in depth. Give it its own report page with a chart and a Breakdown table.
33
+
34
+ ## Guidelines
35
+
36
+ - Keep to about seven items on one dashboard and about seven series or rows inside any one item. If you need more, split into a parent overview and child dashboards, such as one per department.
37
+ - Decide whether the page is operational (what needs action today) or analytical (trends over months). A mix is fine, but put the operational items first.
38
+ - Page-level controls change every item at once and always have a value, so the period uses a segmented control or dropdown, not a filter pill.
39
+ - Each item loads, fails and shows empty on its own. One failed chart shows an error state in its card; the rest of the page still works.
40
+ - Design the narrow layout too: on a phone the bands stack into one column in priority order, and dense charts are replaced by their headline number and a link to the full chart.
41
+ - Make the dashboard the first link in the side navigation when it is the landing page for returning users.
42
+
43
+ ## Content
44
+
45
+ - Item titles name what is measured and the scope: "Applications this week", "Listings expiring in 7 days".
46
+ - Every number says what it compares against, using the Comparison over time pattern: "+12% vs previous 7 days".
47
+ - Use "previous" and "following", not "above" and "below": the layout stacks differently on narrow screens.
48
+ - Update notes are one sentence and a link, such as "You can now schedule the weekly hiring report. Set up a schedule".
49
+
50
+ ## Accessibility
51
+
52
+ - The page has one h1; each item title is an h2 and any sub-heading inside an item is an h3, so screen reader users can jump between items by heading.
53
+ - Focus order follows the visual order, band by band and left to right.
54
+ - Every chart has its data available as a table (the Chart or Table switch) and a text summary of the headline.
55
+ - Status in items is a word in a badge, never colour alone.
56
+
57
+ ## Do
58
+
59
+ - Do put the item that answers the most frequent question in the first row.
60
+ - Do link each item to the page where the work happens.
61
+ - Do show the time the data was last updated in the page header.
62
+
63
+ ## Don't
64
+
65
+ - Don't show long lists such as activity logs; show the top five and link to the rest.
66
+ - Don't put complex filtering inside items. Send people to the list or report page for that.
67
+ - Don't mix fixed items and movable items on one page.
68
+
69
+ ## Related
70
+
71
+ - Configurable dashboard
72
+ - Dashboard items
73
+ - Comparison over time
74
+ - Empty, loading and error states
75
+ - Board and board item
@@ -0,0 +1,140 @@
1
+ # Status badge
2
+
3
+ Shows the system state of a record, such as Live, Draft or Closed early, in one short word or phrase.
4
+
5
+ Kind: component · Group: Actions and inputs · Status: built, import from @twoux/ui
6
+
7
+ ## When to use
8
+
9
+ - To show workflow or system state of a listing or candidate: Live, Expires in 3 days, Closed early, Draft.
10
+ - In table rows, where a word reads faster than an icon alone.
11
+ - Next to the object it describes, never floating on its own.
12
+
13
+ ## When not to use
14
+
15
+ - For user-made categories such as Design or Graduate scheme. Use Tag.
16
+ - To start an action or change state. Use a Dropdown menu or Button; the badge is static.
17
+ - For counts on tabs or filters. Use a plain count next to the label.
18
+
19
+ ## Structure
20
+
21
+ 1. Bordered container (20px)
22
+ 2. Status text
23
+ 3. Dot or icon (optional)
24
+
25
+ ## Guidelines
26
+
27
+ - 20px high, 4px radius, 12px medium text, 1px border in the tone colour on a tinted background.
28
+ - Five tones with fixed meaning: ok (green), warn (amber), bad (red), mute (grey), accent (primary, for Featured).
29
+ - Match the tone to the meaning, not to how much attention you want.
30
+ - Keep the same tone for the same status everywhere in the product.
31
+ - Do not truncate. If the text is long, rewrite it; badges cannot be focused to reveal the rest.
32
+
33
+ ## Content
34
+
35
+ - One to three words, sentence case: Expires in 3 days, not EXPIRING SOON.
36
+ - Use plain words people use, not system codes.
37
+ - Prefer a specific state over a vague one: Closed early, not Inactive.
38
+
39
+ ## Accessibility
40
+
41
+ - The text carries the meaning; colour only reinforces it.
42
+ - Text on the tinted background needs 4.5:1; the border 3:1 is not required because the text identifies it.
43
+ - Badges are not interactive: no tabindex, no tooltip that holds needed information.
44
+ - When a status changes live (for example after an agent run), announce it with a status message.
45
+
46
+ ## Examples
47
+
48
+ ### dot
49
+
50
+ ```tsx
51
+ import { Badge } from "@twoux/ui";
52
+
53
+ export const meta = {
54
+ title: "With dot",
55
+ description: "A leading dot helps scanning down a long column. The word stays.",
56
+ order: 2,
57
+ };
58
+
59
+ export default function Example() {
60
+ return (
61
+ <div className="flex flex-wrap items-center gap-2">
62
+ <Badge variant="success" dot>Live</Badge>
63
+ <Badge variant="warning" dot>Needs you</Badge>
64
+ <Badge variant="destructive" dot>Failed</Badge>
65
+ </div>
66
+ );
67
+ }
68
+ ```
69
+
70
+ ### in-table
71
+
72
+ ```tsx
73
+ import { Badge } from "@twoux/ui";
74
+
75
+ export const meta = {
76
+ title: "In a table",
77
+ description: "One badge per row, in its own column, left aligned so the words line up.",
78
+ order: 3,
79
+ };
80
+
81
+ const rows = [
82
+ ["Junior Product Designer", "success", "Live", "86"],
83
+ ["Graduate Data Analyst", "warning", "Expires in 3 days", "54"],
84
+ ["Marketing Assistant", "secondary", "Draft", "0"],
85
+ ["Customer Success Associate", "destructive", "Closed early", "19"],
86
+ ] as const;
87
+
88
+ export default function Example() {
89
+ return (
90
+ <table className="w-full max-w-xl border-collapse overflow-hidden rounded-sm border border-line-strong bg-surface text-left">
91
+ <thead>
92
+ <tr className="h-8 bg-surface-2 text-sm text-ink-2">
93
+ <th className="px-3 font-medium">Role</th>
94
+ <th className="px-3 font-medium">Status</th>
95
+ <th className="px-3 text-right font-medium">Applies</th>
96
+ </tr>
97
+ </thead>
98
+ <tbody>
99
+ {rows.map(([role, variant, label, applies]) => (
100
+ <tr key={role} className="h-row border-t border-line">
101
+ <td className="px-3 font-medium">{role}</td>
102
+ <td className="px-3"><Badge variant={variant}>{label}</Badge></td>
103
+ <td className="tabular px-3 text-right">{applies}</td>
104
+ </tr>
105
+ ))}
106
+ </tbody>
107
+ </table>
108
+ );
109
+ }
110
+ ```
111
+
112
+ ### statuses
113
+
114
+ ```tsx
115
+ import { Badge } from "@twoux/ui";
116
+
117
+ export const meta = {
118
+ title: "Record status",
119
+ description: "Always a word. Colour repeats the meaning but never carries it alone.",
120
+ order: 1,
121
+ };
122
+
123
+ export default function Example() {
124
+ return (
125
+ <div className="flex flex-wrap items-center gap-2">
126
+ <Badge variant="success">Live</Badge>
127
+ <Badge variant="warning">Expires in 3 days</Badge>
128
+ <Badge variant="destructive">Closed early</Badge>
129
+ <Badge variant="secondary">Draft</Badge>
130
+ <Badge variant="default">Featured</Badge>
131
+ </div>
132
+ );
133
+ }
134
+ ```
135
+
136
+ ## Related
137
+
138
+ - Tag
139
+ - Tooltip
140
+ - Tabs (underline)
@@ -0,0 +1,75 @@
1
+ # Step card
2
+
3
+ The proposal turns into a step card while changes apply: each change shows a spinner then a check, with an "N / M" counter, and the table updates row by row.
4
+
5
+ Kind: component · Group: AI and agents · Status: built, import from @twoux/ui
6
+
7
+ ## When to use
8
+
9
+ - Applying approved changes that take more than an instant, so people can see each one land.
10
+ - When the table should update as each step finishes, so the result can be checked in place.
11
+
12
+ ## When not to use
13
+
14
+ - Scheduled or long agent runs with waiting steps. Use the Agent run timeline (agent-run).
15
+ - Reasoning before an answer. Use Thinking and steps (thinking-steps).
16
+
17
+ ## Structure
18
+
19
+ 1. Header ("Applying 2 changes" and counter)
20
+ 2. Step rows (spinner or check, change label)
21
+ 3. Footer note ("Working…" then "Applied 2 changes.")
22
+
23
+ ## Guidelines
24
+
25
+ - Each step has a visible state: pending with spinner, then done with a check. A failed step says why and keeps the others.
26
+ - Show the counter ("1 / 2") so progress reads without counting icons.
27
+ - Finish with a summary and an Undo toast.
28
+ - Keep the step card in the thread as a record of what was done.
29
+
30
+ ## Content
31
+
32
+ - Use the same label as the proposal row so people recognise each change.
33
+ - Finish in past tense with a count: "Applied 2 changes."
34
+
35
+ ## Accessibility
36
+
37
+ - Announce the counter and the final summary through a status region (SC 4.1.3); do not announce every spinner.
38
+ - Spinners are aria-hidden and the state is also given in text (SC 1.3.1, 1.4.1).
39
+ - Spinner motion stops when the step ends; runs over five seconds need Stop (SC 2.2.2).
40
+
41
+ ## Keyboard
42
+
43
+ | Keys | Action |
44
+ |---|---|
45
+ | ⌘Z / Ctrl+Z | Undo the applied changes from the toast |
46
+
47
+ ## Examples
48
+
49
+ ### apply
50
+
51
+ ```tsx
52
+ import { Button, StepCard } from "@twoux/ui";
53
+ import { useEffect, useState } from "react";
54
+
55
+ export const meta = { title: "Applying, step by step", description: "Each change shows pending, then done. A failed step says why and keeps the others. The counter reads progress without counting icons.", order: 1 };
56
+
57
+ export default function Example() {
58
+ const [n, setN] = useState(0);
59
+ useEffect(() => { if (n < 3) { const t = setTimeout(() => setN((x) => x + 1), 700); return () => clearTimeout(t); } }, [n]);
60
+ const state = (i: number) => (i >= n ? "pending" : i === 2 ? "failed" : "done") as "pending" | "done" | "failed";
61
+ return (
62
+ <div className="grid w-full max-w-md gap-2">
63
+ <StepCard title="Extending 3 listings" steps={[{ label: "Frontend Engineer closes 21 Dec", state: state(0) }, { label: "Data Analyst closes 12 Jan", state: state(1) }, { label: "QA Engineer", state: state(2), reason: n > 2 ? "Already closed. Reopen it first." : undefined }]} />
64
+ <Button variant="outline" size="sm" className="justify-self-start" onClick={() => setN(0)}>Replay</Button>
65
+ </div>
66
+ );
67
+ }
68
+ ```
69
+
70
+ ## Related
71
+
72
+ - Suggested changes
73
+ - Agent run timeline
74
+ - Toast
75
+ - Plan preview