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