@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,74 @@
1
+ # Drag and drop
2
+
3
+ Dragging is a shortcut, never the only way: every drag in TwoUX has a keyboard path and a menu path that do the same thing.
4
+
5
+ Kind: pattern · Group: Patterns: general
6
+
7
+ **Problem:** Dragging is quick for some people and impossible for others, including keyboard, switch and tremor users. Drags without a visible handle go unnoticed, and drops that land somewhere unexpected move a candidate to the wrong stage.
8
+
9
+ **Solution:** Mark draggable things with a handle, show where the item will land while it moves, and send an invalid drop back to where it started. Offer the same move from the keyboard (pick up, arrows, drop) and from a menu item such as Move to stage.
10
+
11
+ **Built from:** Board and board item, Columns menu, Grouped list, Dropdown menu, Confirmation dialog, Toast
12
+
13
+ ## How it works
14
+
15
+ 1. Decide whether the drag is a shortcut (reordering columns, uploading a CV by dropping it) or the main way to do the task (arranging a dashboard). Main-way drags need the strongest signifiers and alternatives.
16
+ 2. Give each draggable item a 16px grip handle in a 24px target, and change the cursor to grab on hover; resize handles use the matching resize cursor.
17
+ 3. While dragging, lift the item with a shadow and show a drop preview the size of the item where it will land.
18
+ 4. If the drop target is not allowed, show it as unavailable during the drag and return the item to its start on release.
19
+ 5. After a drop, confirm the result in place and in a toast with Undo, unless the move is irreversible (such as moving a candidate to Rejected, which emails them); that opens a confirmation dialog first.
20
+
21
+ ## When to use
22
+
23
+ - Reordering things whose order matters: pipeline stages, columns in the Columns menu, dashboard items on a board.
24
+ - Moving a candidate card between stages on a pipeline board, alongside the Move to stage menu.
25
+ - Dropping files, such as a CV or a job description document, onto an upload area that also has a Choose file button.
26
+
27
+ ## When not to use
28
+
29
+ - For sorting a table. Use sortable column headers; order there comes from the data, not from the person.
30
+ - For bulk moves of many records. Select them and use the bulk action bar.
31
+ - As the only way to do anything (WCAG 2.5.7).
32
+
33
+ ## Guidelines
34
+
35
+ - Every drag has a single-pointer alternative: arrow buttons or menu items such as Move up, Move down, Move to stage.
36
+ - Every drag has a keyboard path: Tab to the handle, Enter or Space to pick up, arrows to move, Enter to drop, Esc to cancel.
37
+ - Items of equal size swap places; otherwise the moving item pushes the others along. Never let items overlap.
38
+ - Moves save per person or per team as the context says, and the result is visible at once: no Save button for a reorder.
39
+ - Scroll the list or board automatically when the dragged item nears its edge.
40
+ - Respect reduced motion: drop items into place without the slide animation.
41
+ - Show every stage of a drag. Ready: the grip darkens on hover and focus, with a grab cursor and a tooltip saying how to move it. Dragging: a lifted copy follows the pointer, the board tints to show where drops are allowed, and a dashed slot shows exactly where the item will land.
42
+ - Other items glide to make room (180ms, off with reduced motion) instead of jumping. On drop, a short highlight settles on the moved item and the new position is announced. Esc cancels and puts it back.
43
+
44
+ ## Content
45
+
46
+ - Handle accessible name: "Move Junior Product Designer", with a description of the keys.
47
+ - Live announcements say position in words: "Picked up Interview. Stage 3 of 6." then "Interview moved to stage 2 of 6."
48
+ - Upload area text names both paths: "Drop a CV here or choose a file".
49
+
50
+ ## Accessibility
51
+
52
+ - Dragging is never the only way to move, reorder or resize (2.5.7 Dragging Movements).
53
+ - Handles meet the 24 by 24px target size (2.5.8).
54
+ - Pick-up, each move, drop and cancel are announced in a polite live region (4.1.3).
55
+ - Esc cancels and returns the item and focus to where they started.
56
+
57
+ ## Do
58
+
59
+ - Do show a drop preview where the item will land.
60
+ - Do offer the same move as a menu item.
61
+ - Do use the same handle icon for every drag in the product.
62
+
63
+ ## Don't
64
+
65
+ - Don't make a whole row draggable with no handle; it fights text selection and click to open.
66
+ - Don't drop silently into an invalid place; send the item back.
67
+ - Don't trigger an email to a candidate from a drop without confirming first.
68
+
69
+ ## Related
70
+
71
+ - Board and board item
72
+ - Columns menu
73
+ - Undo and destructive actions
74
+ - Keyboard-first navigation
@@ -0,0 +1,137 @@
1
+ # Dropdown menu
2
+
3
+ Opens a short list of actions or options from a button, with optional groups and one level of submenus.
4
+
5
+ Kind: component · Group: Actions and inputs · Status: built, import from @twoux/ui
6
+
7
+ ## When to use
8
+
9
+ - To group related actions behind one button: Export, row actions, Add context.
10
+ - For about 5 to 15 commands, ordered by how often they are used.
11
+ - For settings with a current value, shown on the right and changed in a one-level submenu: Writing style Concise.
12
+
13
+ ## When not to use
14
+
15
+ - For one or two actions. Show them as Buttons.
16
+ - When people must search or pick from a long list of values. Use a select or combobox.
17
+ - For the main action in a view. Use a primary Button.
18
+ - For rich content or forms. Use a popover or dialog.
19
+
20
+ ## Structure
21
+
22
+ 1. Trigger button with chevron
23
+ 2. Menu panel
24
+ 3. Group label (optional)
25
+ 4. Menu item: icon, label, shortcut or current value
26
+ 5. Separator
27
+ 6. Submenu caret
28
+ 7. Destructive item (last)
29
+
30
+ ## Guidelines
31
+
32
+ - Panel 200px minimum, 10px radius, 4px padding, shadow. Items 30px high, 6px radius, hover in grey.
33
+ - Group with uppercase group labels and separators; put destructive items last, in red, after a separator.
34
+ - At most one level of submenu. Submenus open on hover, focus or Right arrow, and flip to the side with room.
35
+ - Icons on items: none by default. Add them only when every item in a group has a well-known icon from the icon meaning table, and then on all items in that group; never on one item alone. The label always stays, and the icon is 14px and muted.
36
+ - Show each item's shortcut on the right.
37
+ - Disable items that do not apply rather than hide them, so people can still find them.
38
+ - Keep the list under about 12 items.
39
+
40
+ ## Content
41
+
42
+ - Action items start with a verb; navigation items are nouns.
43
+ - Sentence case, one line, no articles: Duplicate listing.
44
+ - Front-load the key word so the list scans: Export CSV, Export PDF.
45
+ - Ellipsis for items that open more input: Extend to a date…
46
+
47
+ ## Accessibility
48
+
49
+ - Trigger has aria-haspopup="menu" and aria-expanded. Enter, Space or Down opens and focuses the first item.
50
+ - role="menu" with menuitem, menuitemcheckbox or menuitemradio; checked items use aria-checked.
51
+ - Up and Down move, Right opens a submenu, Left closes it, Esc closes and returns focus to the trigger.
52
+ - Items are 30px high, above the 24px target minimum.
53
+ - Focus returns to the trigger on close, so the person is not lost.
54
+
55
+ ## Keyboard
56
+
57
+ | Keys | Action |
58
+ |---|---|
59
+ | Enter / Space / Down | Open the menu and focus the first item |
60
+ | Up / Down | Move between items |
61
+ | Right | Open a submenu |
62
+ | Left | Close a submenu |
63
+ | Home / End | First or last item |
64
+ | Type a letter | Jump to the next item starting with it |
65
+ | Esc | Close and return focus to the trigger |
66
+
67
+ ## Examples
68
+
69
+ ### actions
70
+
71
+ ```tsx
72
+ import { Button, DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuLabel, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger, Kbd } from "@twoux/ui";
73
+ import { Copy, ExternalLink, MoreHorizontal, Pencil, Trash2, UserRound } from "lucide-react";
74
+ export const meta = {"title": "Actions on a record", "description": "Shortcuts on the right, destructive last and separated. Submenus open on the side with room. Arrow keys and typeahead work.", "order": 1};
75
+
76
+ export default function Example() {
77
+ return (
78
+ <DropdownMenu>
79
+ <DropdownMenuTrigger asChild>
80
+ <Button variant="outline" aria-label="Actions for Junior Product Designer" size="icon"><MoreHorizontal /></Button>
81
+ </DropdownMenuTrigger>
82
+ <DropdownMenuContent align="start">
83
+ <DropdownMenuLabel>Listing</DropdownMenuLabel>
84
+ <DropdownMenuItem><Pencil />Edit<DropdownMenuShortcut><Kbd>E</Kbd></DropdownMenuShortcut></DropdownMenuItem>
85
+ <DropdownMenuItem><Copy />Duplicate<DropdownMenuShortcut><Kbd>D</Kbd></DropdownMenuShortcut></DropdownMenuItem>
86
+ <DropdownMenuSub>
87
+ <DropdownMenuSubTrigger><UserRound />Change owner</DropdownMenuSubTrigger>
88
+ <DropdownMenuSubContent>
89
+ <DropdownMenuItem>Grace L.</DropdownMenuItem>
90
+ <DropdownMenuItem>Rahul S.</DropdownMenuItem>
91
+ <DropdownMenuItem>Jess M.</DropdownMenuItem>
92
+ </DropdownMenuSubContent>
93
+ </DropdownMenuSub>
94
+ <DropdownMenuItem><ExternalLink />View public page</DropdownMenuItem>
95
+ <DropdownMenuSeparator />
96
+ <DropdownMenuItem variant="destructive"><Trash2 />Delete listing</DropdownMenuItem>
97
+ </DropdownMenuContent>
98
+ </DropdownMenu>
99
+ );
100
+ }
101
+ ```
102
+
103
+ ### options
104
+
105
+ ```tsx
106
+ import { Button, DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuLabel, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuTrigger } from "@twoux/ui";
107
+ import { ChevronDown } from "lucide-react";
108
+ import { useState } from "react";
109
+ export const meta = {"title": "Options with a check", "description": "Radio items for one choice, checkbox items for several. The menu stays a menu: it does not replace a form.", "order": 2};
110
+
111
+ export default function Example() {
112
+ const [sort, setSort] = useState("posted");
113
+ const [cols, setCols] = useState({ salary: true, owner: false });
114
+ return (
115
+ <DropdownMenu>
116
+ <DropdownMenuTrigger asChild><Button variant="outline">Display<ChevronDown /></Button></DropdownMenuTrigger>
117
+ <DropdownMenuContent align="start">
118
+ <DropdownMenuLabel>Sort by</DropdownMenuLabel>
119
+ <DropdownMenuRadioGroup value={sort} onValueChange={setSort}>
120
+ <DropdownMenuRadioItem value="posted">Posted date</DropdownMenuRadioItem>
121
+ <DropdownMenuRadioItem value="applies">Applies</DropdownMenuRadioItem>
122
+ </DropdownMenuRadioGroup>
123
+ <DropdownMenuSeparator />
124
+ <DropdownMenuLabel>Columns</DropdownMenuLabel>
125
+ <DropdownMenuCheckboxItem checked={cols.salary} onCheckedChange={(v) => setCols({ ...cols, salary: v })}>Salary</DropdownMenuCheckboxItem>
126
+ <DropdownMenuCheckboxItem checked={cols.owner} onCheckedChange={(v) => setCols({ ...cols, owner: v })}>Owner</DropdownMenuCheckboxItem>
127
+ </DropdownMenuContent>
128
+ </DropdownMenu>
129
+ );
130
+ }
131
+ ```
132
+
133
+ ## Related
134
+
135
+ - Button
136
+ - Popover rules
137
+ - Keyboard shortcut hint
@@ -0,0 +1,73 @@
1
+ # Editing a record: inline, section or whole page
2
+
3
+ Change one value where it is shown, one section in place, or many linked settings on an edit page that mirrors the create form. Every saved edit can be undone.
4
+
5
+ Kind: pattern · Group: Patterns: general
6
+
7
+ **Problem:** Some screens open a full form to change a closing date; others edit linked settings one cell at a time and leave the record half changed. Edit forms drift away from the create form, so people meet the same field in two shapes.
8
+
9
+ **Solution:** Edit one value inline. Edit one section in place: it turns into a form with Save and Cancel. Use an edit page only when several settings depend on each other, and build it from the create form with the same fields in the same order. Saving shows a toast with Undo.
10
+
11
+ **Built from:** List table, Data grid, Card, Text input, Form validation, Page header, Breadcrumb, Button, Toast, Side panel (drawer)
12
+
13
+ ## How it works
14
+
15
+ 1. Ask what is changing. One value on one or many records: inline. Several values in one section of a record: section edit. Settings that depend on each other across sections: edit page.
16
+ 2. Inline: follow Inline editing and validation. Enter edits, Enter saves, Esc cancels.
17
+ 3. Section edit: an Edit button in the section header turns that section into a form with Save and Cancel at its end. Other sections stay read-only.
18
+ 4. Edit page: title "Edit Junior Product Designer", breadcrumb ending in "Edit", the create form's fields and sections in the same order, Save changes and Cancel at the end.
19
+ 5. On save, return to where the edit started and show "Listing updated. Undo". With no changes, return and show "No changes made".
20
+
21
+ ## When to use
22
+
23
+ - Inline: values people change often or across many rows, such as stage, owner or closing date.
24
+ - Section edit: a group that belongs together, such as Location and pay on a listing.
25
+ - Edit page: changes where one setting limits another, such as switching a listing from Internal to Public, which adds job board and salary rules.
26
+
27
+ ## When not to use
28
+
29
+ - Don't open an edit page to change one value.
30
+ - Don't mix create and edit in one view, such as adding a new listing from inside the edit page of another.
31
+ - Don't use edit for actions with their own consequence, such as Close listing or Reject candidate. They are actions with their own button and, where needed, confirmation.
32
+
33
+ ## Guidelines
34
+
35
+ - The edit page reuses the create form component, so labels, order, hints and validation never drift apart.
36
+ - Save changes stays enabled. Validate on submit, keep the person's input, and show server errors above the buttons so nothing has to be typed again.
37
+ - An edited row that no longer matches the filters or sort stays in place, marked, until the list refreshes, so it doesn't vanish under the cursor.
38
+ - Lists of repeated values, such as screening questions or interview stages, use rows with Add and Remove. Removing a row takes effect on save and says so: "Removed when you save".
39
+ - Leaving an edit with unsaved changes asks "Discard changes?" with Keep editing focused.
40
+ - Every saved edit is reversible with Undo in the toast, ⌘Z and the record's activity.
41
+
42
+ ## Content
43
+
44
+ - Edit page title: "Edit" plus the record name. Submit button: "Save changes".
45
+ - Section headings match the create form word for word.
46
+ - Limits on repeated rows say what is left: "You can add 3 more questions."
47
+
48
+ ## Accessibility
49
+
50
+ - When a section switches to edit, focus moves to its first field; on Save or Cancel it returns to the section's Edit button.
51
+ - Each Edit button names its section: aria-label "Edit location and pay".
52
+ - When a repeated row is added, focus moves to its first input; when removed, focus moves to the next row or the Add button.
53
+
54
+ ## Do
55
+
56
+ - Do edit in place when the change is small.
57
+ - Do mirror the create form exactly on the edit page.
58
+ - Do offer Undo after every saved edit.
59
+
60
+ ## Don't
61
+
62
+ - Don't disable Save changes until something changes.
63
+ - Don't put two Edit modes on the screen at once; one section edits at a time.
64
+ - Don't open a dialog to edit a section of a record that is already in a side panel.
65
+
66
+ ## Related
67
+
68
+ - Creating a record: dialog, single page or steps
69
+ - Inline editing and validation
70
+ - Undo and destructive actions
71
+ - Record detail: side panel, overlay or full page
72
+ - Form validation
73
+ - Data grid
@@ -0,0 +1,78 @@
1
+ # Empty, loading and error states
2
+
3
+ One rule set for the three states every view has before or instead of data: loading, nothing to show, and failed.
4
+
5
+ Kind: pattern · Group: Patterns: general
6
+
7
+ **Problem:** A blank table can mean loading, no data, no matches or a failure. When screens handle these differently, people wait for data that never comes or give up on data that was about to arrive.
8
+
9
+ **Solution:** Skeleton rows while loading, an empty state with one next step when there is nothing, a zero results state when filters match nothing, and an error state that keeps the filters and offers Try again.
10
+
11
+ **Built from:** Loading skeleton, Empty state, Error state (failed to load), Banner (inline alert), List table, Data grid
12
+
13
+ ## How it works
14
+
15
+ 1. Under about one second, show nothing extra. Beyond that, keep the table's shape with skeleton rows.
16
+ 2. If the load succeeds with no records, replace the table with an empty state: what goes here, and one action to add it.
17
+ 3. If records exist but filters match none, show the zero results state with Clear filters.
18
+ 4. If the load fails, replace the table with an error state that says what failed, keeps the filters and offers Try again (R).
19
+ 5. When a refresh fails after data has loaded, keep the old data on screen and show a warning beside the refresh time.
20
+
21
+ ## When to use
22
+
23
+ - On every table, grid, chart card, panel and assistant answer that loads data.
24
+
25
+ ## When not to use
26
+
27
+ - For a brand new workspace with nothing set up. Use Onboarding to an empty workspace with the setup checklist.
28
+
29
+ ## Guidelines
30
+
31
+ - Never show "No listings" before loading has finished; people stop waiting and miss the data.
32
+ - Skeletons for structured content (tables, cards); a spinner for a single element such as a button or expanding row; a progress bar beyond about ten seconds.
33
+ - Empty and zero results states replace the table and its headers, so screen readers don't meet an empty table.
34
+ - Every state offers a next step. No dead ends.
35
+ - Errors never use the empty state component; they use the error state or a banner.
36
+ - Show when data was last updated after each refresh.
37
+ - Charts follow the same states with chart-specific shapes: the plot skeleton while loading, an empty state for no data, a no match state with Clear filters after filtering, and an error state with Try again, each inside the chart card so the title and controls stay.
38
+ - Manual refresh lives in the table header as an icon button labelled "Refresh listings", next to "Last updated 2 minutes ago". Add a refresh button only where data changes while people watch; other views refresh on load.
39
+ - During a refresh keep the current rows on screen and usable; only the refresh button shows a spinner.
40
+ - If a refresh takes longer than about ten seconds, show a "Taking longer than usual" status beside the refresh time; if it partly fails, show a warning status there with what failed and Try again.
41
+ - Wrap each region (table, chart card, side panel) in its own error boundary, so a crash shows an error state in that region and the rest of the page still works.
42
+ - Inside a table or read-only details, an empty value shows a muted hyphen (-) with the accessible name "Not set". Never use a dash for a real zero; show 0.
43
+ - Keep the count in the table heading even when empty: "Listings (0)".
44
+
45
+ ## Content
46
+
47
+ - Empty: "No listings yet" plus "Create listing". Zero results: "No listings match these filters" plus "Clear filters".
48
+ - Error: "Couldn't load listings", then the likeliest cause, then "Your filters are kept." Avoid "sorry" and error codes.
49
+ - Loading text names the thing without articles or end punctuation: "Loading listings".
50
+ - The empty state's action uses the same label as the header button it mirrors ("Create listing") and is a secondary button, since the header already has the primary.
51
+ - An empty state description explains why it is empty and never repeats the heading or button text.
52
+
53
+ ## Accessibility
54
+
55
+ - Announce loading, loaded counts and failures as status messages without moving focus.
56
+ - Hide decorative empty state illustrations from screen readers with empty alt text.
57
+ - Error states are identified in text, not only with a red icon.
58
+ - The refresh button has loading text ("Refreshing listings"), and the new "Last updated" time is announced politely.
59
+
60
+ ## Do
61
+
62
+ - Do keep the layout stable from skeleton to data.
63
+ - Do keep filters when a load fails.
64
+
65
+ ## Don't
66
+
67
+ - Don't use a full-page spinner for a table load.
68
+ - Don't show a frame-only skeleton with no placeholder rows.
69
+ - Don't write jokes into error messages.
70
+
71
+ ## Related
72
+
73
+ - Filtering and search
74
+ - Onboarding to an empty workspace
75
+ - Generated table columns and enrichment
76
+ - Empty state
77
+ - Loading skeleton
78
+ - Error state (failed to load)
@@ -0,0 +1,128 @@
1
+ # Empty state
2
+
3
+ What a list, table or panel shows when it loaded correctly but has nothing to show, with the next step.
4
+
5
+ Kind: component · Group: Feedback and overlays · Status: built, import from @twoux/ui
6
+
7
+ ## When to use
8
+
9
+ - Filters or search match nothing: "No listings match these filters".
10
+ - Nothing has been created yet, such as a team with no listings.
11
+ - A queue is cleared, such as the Inbox: "All caught up".
12
+
13
+ ## When not to use
14
+
15
+ - Data is still loading. Use the Loading skeleton.
16
+ - Loading failed. Use the Error state, never an empty message.
17
+ - Only part of a record is missing, such as one field. Show "Add" in the field itself.
18
+
19
+ ## Structure
20
+
21
+ 1. Icon tile (32px, neutral)
22
+ 2. Title: the state, sentence case, no full stop
23
+ 3. Description: why, and what to try (max 36 characters wide per line block)
24
+ 4. One action (secondary button), or none for a finished state
25
+ 5. Dashed container sized to the region it replaces
26
+
27
+ ## Guidelines
28
+
29
+ - Replace the region that would hold the data, inside the same container, so filters and the result count stay in place.
30
+ - Distinguish no data (nothing created) from no results (filters too narrow); they need different actions.
31
+ - Give one next step: Clear filters for no results, the create action for no data. Never leave a dead end.
32
+ - Keep the result count visible ("0 of 24") so the person sees the filters, not the data, caused the empty view.
33
+ - Show the empty state only after loading finishes; never as placeholder text that is later replaced.
34
+
35
+ ## Content
36
+
37
+ - Title names the state plainly and briefly: "No listings match these filters".
38
+ - Description says why and what to try, without repeating the title: "Try a wider date range, or clear the location filter."
39
+ - Button label starts with a verb and matches the next step: "Clear filters", "New listing".
40
+ - Neutral tone for no results; a short positive line for a finished queue. No jokes, no apologies.
41
+
42
+ ## Accessibility
43
+
44
+ - Replace the whole table, headers included, so screen readers do not read an empty structure first (1.3.1).
45
+ - When filtering produces zero results, announce the new count through a polite live region (4.1.3).
46
+ - The icon is decorative and has empty alt text.
47
+
48
+ ## Keyboard
49
+
50
+ | Keys | Action |
51
+ |---|---|
52
+ | Tab | Reaches the single action |
53
+ | F | Opens the filter picker to change filters instead of clearing them |
54
+
55
+ ## Examples
56
+
57
+ ### no-data
58
+
59
+ ```tsx
60
+ import { Button, Empty, EmptyContent, EmptyDescription, EmptyHeader, EmptyMedia, EmptyTitle } from "@twoux/ui";
61
+ import { BriefcaseBusiness, Plus } from "lucide-react";
62
+
63
+ export const meta = { title: "No data yet", description: "Nothing has been created. The one action is the create action, not Clear filters.", order: 2 };
64
+
65
+ export default function Example() {
66
+ return (
67
+ <Empty className="w-full max-w-xl">
68
+ <EmptyHeader>
69
+ <EmptyMedia variant="icon"><BriefcaseBusiness /></EmptyMedia>
70
+ <EmptyTitle>No listings yet</EmptyTitle>
71
+ <EmptyDescription>Listings you publish appear here, with their applicants.</EmptyDescription>
72
+ </EmptyHeader>
73
+ <EmptyContent>
74
+ <Button variant="outline" size="sm"><Plus />New listing</Button>
75
+ </EmptyContent>
76
+ </Empty>
77
+ );
78
+ }
79
+ ```
80
+
81
+ ### no-results
82
+
83
+ ```tsx
84
+ import { Badge, Button, Empty, EmptyContent, EmptyDescription, EmptyHeader, EmptyMedia, EmptyTitle } from "@twoux/ui";
85
+ import { SearchX, X } from "lucide-react";
86
+ import { useState } from "react";
87
+
88
+ export const meta = { title: "No results", description: "Filters are too narrow. The empty state replaces the table only; the filters and the count stay, so it is clear the filters caused it. One action: Clear filters.", order: 1 };
89
+
90
+ export default function Example() {
91
+ const [filtered, setFiltered] = useState(true);
92
+ return (
93
+ <div className="grid w-full max-w-xl gap-2">
94
+ <div className="flex items-center gap-2">
95
+ <span className="text-base font-medium text-ink">Listings</span>
96
+ {filtered && (
97
+ <Badge variant="secondary" className="gap-1">
98
+ Location: Inverness
99
+ <Button variant="ghost" size="icon-xs" aria-label="Remove filter Location: Inverness" onClick={() => setFiltered(false)} className="-mr-1 size-4"><X className="size-3" /></Button>
100
+ </Badge>
101
+ )}
102
+ <span className="ml-auto text-sm text-ink-3 tabular">{filtered ? "0 of 24" : "24 of 24"}</span>
103
+ </div>
104
+ {filtered ? (
105
+ <Empty className="min-h-56">
106
+ <EmptyHeader>
107
+ <EmptyMedia variant="icon"><SearchX /></EmptyMedia>
108
+ <EmptyTitle>No listings match these filters</EmptyTitle>
109
+ <EmptyDescription>Try another location, or clear the filters to see all 24.</EmptyDescription>
110
+ </EmptyHeader>
111
+ <EmptyContent>
112
+ <Button variant="outline" size="sm" onClick={() => setFiltered(false)}>Clear filters</Button>
113
+ </EmptyContent>
114
+ </Empty>
115
+ ) : (
116
+ <div className="grid min-h-56 place-items-center rounded-sm border border-line text-base text-ink-2">24 listings</div>
117
+ )}
118
+ </div>
119
+ );
120
+ }
121
+ ```
122
+
123
+ ## Related
124
+
125
+ - Loading skeleton
126
+ - Error state (failed to load)
127
+ - Inbox (triage)
128
+ - Setup checklist
@@ -0,0 +1,75 @@
1
+ # Error messages
2
+
3
+ Put every error where it happened, at the smallest scope that contains it, and say what went wrong and what to do next in plain words.
4
+
5
+ Kind: pattern · Group: Patterns: general
6
+
7
+ **Problem:** Errors shown far from their cause, raw API text, and messages that blame the person or promise help that never comes all erode trust. When one chart fails and the whole page goes blank, people lose work they could have kept doing.
8
+
9
+ **Solution:** Match the place to the scope: a field error under the field, a form error in a banner above the form actions, a failed region in an error state that replaces only that region, a failed page in a page-level error state. Every message has a cause if known and a next step.
10
+
11
+ **Built from:** Form validation, Banner (inline alert), Error state (failed to load), Toast, Text input, Code block
12
+
13
+ ## How it works
14
+
15
+ 1. Find the smallest scope that contains the failure: one field, one form, one region (a chart card, the side panel, a table), or the whole page.
16
+ 2. Field: message under the field. Form or server error with no field: banner above the form's buttons. Region: error state in place of that region only. Page: error state in place of the page content.
17
+ 3. For an action that failed after the person left the screen, such as an export or a bulk close, put a banner on the page they are on and an Inbox item; never a toast that fades.
18
+ 4. Write the message: what happened, why if known, what to do now. Add Try again when retrying could work.
19
+ 5. Put technical detail (request ID, error code) in a collapsed Details section the person can copy into a support request.
20
+
21
+ ## When to use
22
+
23
+ - Any time something failed and the person needs to know: a value is invalid, a save failed, data didn't load, an action was refused.
24
+ - Unexpected crashes in one part of the page: wrap each region in an error boundary so the rest stays usable.
25
+ - When an action partly worked, such as a bulk close where some listings were already closed by someone else.
26
+
27
+ ## When not to use
28
+
29
+ - For warnings or advice, such as a salary below the advertised range for similar roles. Use a warning banner or field warning; errors mean something must be fixed now.
30
+ - For a list that loaded fine and is empty. Use the empty state.
31
+ - For a reversible action that worked. Use a toast.
32
+
33
+ ## Guidelines
34
+
35
+ - Use the built-in error state of a component (a combobox that failed to load options, a chart that failed to fetch) before inventing a new one.
36
+ - Errors never auto-dismiss. A failed save stays visible until it succeeds or the person dismisses it.
37
+ - Keep what the person entered. A failed submit leaves every field filled and stays on the page until the result is known.
38
+ - Show the count when it helps: "3 of 12 listings couldn't be closed", with a link that filters to those three.
39
+ - Never claim the team has been told unless errors really are reported automatically.
40
+ - Report unexpected errors to the error tracker with the request ID shown in Details, so a support request can be matched.
41
+
42
+ ## Content
43
+
44
+ - Pattern: what happened, then what to do. "Couldn't save the closing date. Check your connection and try again."
45
+ - Don't blame: "Enter a closing date after today", not "You entered an invalid date".
46
+ - Plain words over codes: "This listing was changed by Tom Reid while you were editing" rather than "409 Conflict".
47
+ - No "Oops", no jokes, no exclamation marks, no "sorry" in every message.
48
+
49
+ ## Accessibility
50
+
51
+ - Errors are text and an icon, never colour alone.
52
+ - Field errors link to their field with aria-describedby and set aria-invalid.
53
+ - A banner that appears after submit moves focus to itself, or to the first field in error; a region error is announced as a status without stealing focus.
54
+ - Suggest the fix when it is known (3.3.3).
55
+
56
+ ## Do
57
+
58
+ - Do keep the failure inside the region that failed.
59
+ - Do offer Try again when a retry could work.
60
+ - Do keep the person's input after any failure.
61
+
62
+ ## Don't
63
+
64
+ - Don't show raw API or stack trace text as the main message.
65
+ - Don't report a failure in a toast that disappears after 6 seconds.
66
+ - Don't use red error styling for a warning.
67
+
68
+ ## Related
69
+
70
+ - Inline editing and validation
71
+ - Empty, loading and error states
72
+ - Notifications: inbox, bell, toast or email digest
73
+ - Banner (inline alert)
74
+ - Error state (failed to load)
75
+ - Form validation