@momoi-labs/kiso 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 (73) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +63 -0
  3. package/kiso/AGENTS.md +50 -0
  4. package/kiso/README.md +62 -0
  5. package/kiso/docs/accessibility.md +87 -0
  6. package/kiso/docs/brand.md +95 -0
  7. package/kiso/docs/components/README.md +58 -0
  8. package/kiso/docs/components/alert.md +158 -0
  9. package/kiso/docs/components/badge.md +135 -0
  10. package/kiso/docs/components/breadcrumb.md +66 -0
  11. package/kiso/docs/components/button.md +168 -0
  12. package/kiso/docs/components/card.md +154 -0
  13. package/kiso/docs/components/checkbox.md +91 -0
  14. package/kiso/docs/components/command-palette.md +165 -0
  15. package/kiso/docs/components/drawer.md +79 -0
  16. package/kiso/docs/components/dropdown-menu.md +178 -0
  17. package/kiso/docs/components/empty-state.md +142 -0
  18. package/kiso/docs/components/form-field.md +115 -0
  19. package/kiso/docs/components/header.md +79 -0
  20. package/kiso/docs/components/helper-text.md +86 -0
  21. package/kiso/docs/components/icon-button.md +161 -0
  22. package/kiso/docs/components/input.md +99 -0
  23. package/kiso/docs/components/label.md +88 -0
  24. package/kiso/docs/components/link.md +152 -0
  25. package/kiso/docs/components/modal-dialog.md +82 -0
  26. package/kiso/docs/components/navigation.md +68 -0
  27. package/kiso/docs/components/page-header.md +70 -0
  28. package/kiso/docs/components/pagination.md +129 -0
  29. package/kiso/docs/components/popover.md +74 -0
  30. package/kiso/docs/components/search.md +147 -0
  31. package/kiso/docs/components/select.md +105 -0
  32. package/kiso/docs/components/sidebar.md +74 -0
  33. package/kiso/docs/components/skeleton.md +140 -0
  34. package/kiso/docs/components/spinner.md +125 -0
  35. package/kiso/docs/components/switch.md +92 -0
  36. package/kiso/docs/components/table.md +255 -0
  37. package/kiso/docs/components/tabs.md +69 -0
  38. package/kiso/docs/components/textarea.md +91 -0
  39. package/kiso/docs/components/toast.md +80 -0
  40. package/kiso/docs/components/tooltip.md +162 -0
  41. package/kiso/docs/components/validation-message.md +96 -0
  42. package/kiso/docs/data-interfaces.md +309 -0
  43. package/kiso/docs/evolution.md +35 -0
  44. package/kiso/docs/patterns/README.md +40 -0
  45. package/kiso/docs/patterns/application-shell.md +106 -0
  46. package/kiso/docs/patterns/command-palette.md +142 -0
  47. package/kiso/docs/patterns/confirmations.md +158 -0
  48. package/kiso/docs/patterns/crud.md +139 -0
  49. package/kiso/docs/patterns/dashboard.md +102 -0
  50. package/kiso/docs/patterns/destructive-actions.md +137 -0
  51. package/kiso/docs/patterns/developer-oriented-interfaces.md +162 -0
  52. package/kiso/docs/patterns/empty-states.md +76 -0
  53. package/kiso/docs/patterns/errors.md +93 -0
  54. package/kiso/docs/patterns/filtering.md +147 -0
  55. package/kiso/docs/patterns/keyboard-shortcuts.md +155 -0
  56. package/kiso/docs/patterns/large-data-tables.md +182 -0
  57. package/kiso/docs/patterns/list-detail.md +118 -0
  58. package/kiso/docs/patterns/loading.md +80 -0
  59. package/kiso/docs/patterns/login-authentication.md +101 -0
  60. package/kiso/docs/patterns/onboarding.md +94 -0
  61. package/kiso/docs/patterns/pagination.md +121 -0
  62. package/kiso/docs/patterns/permission-denied.md +84 -0
  63. package/kiso/docs/patterns/search.md +150 -0
  64. package/kiso/docs/patterns/settings.md +100 -0
  65. package/kiso/docs/patterns/sorting.md +121 -0
  66. package/kiso/docs/principles.md +122 -0
  67. package/kiso/docs/tokens.md +95 -0
  68. package/kiso/docs/voice-and-tone.md +154 -0
  69. package/package.json +42 -0
  70. package/tokens/build/tokens.css +143 -0
  71. package/tokens/build/tokens.d.ts +160 -0
  72. package/tokens/build/tokens.json +88 -0
  73. package/tokens/build/tokens.scss +89 -0
@@ -0,0 +1,165 @@
1
+ # CommandPalette
2
+
3
+ A global, keyboard-first overlay for running actions and navigating the
4
+ product by searching commands and destinations.
5
+
6
+ ## Purpose
7
+
8
+ CommandPalette is the "do or go anywhere" surface: open a query editor, jump
9
+ to a replica, switch project, run a frequent action — without hunting through
10
+ menus. It is invoked by a documented shortcut, filters a command list as the
11
+ person types, and is fully operable from the keyboard.
12
+
13
+ User stories #5, #19, and #20.
14
+
15
+ ### Choose the right command surface
16
+
17
+ | Need | Control | Why |
18
+ | --- | --- | --- |
19
+ | Global actions and navigation | **CommandPalette** | App-scoped; search-as-you-type commands. |
20
+ | Filter items in a visible list/table | **[Search](search.md)** | Scoped to that collection; not an overlay of commands. |
21
+ | Actions on one specific element | **[DropdownMenu](dropdown-menu.md)** | Anchored to that control; contextual, not global. |
22
+
23
+ Search vs CommandPalette (user story #19): Search filters *content already
24
+ in view*. CommandPalette finds *actions and destinations* across the app.
25
+
26
+ DropdownMenu vs CommandPalette (user story #20): DropdownMenu is
27
+ *contextual* to a trigger. CommandPalette is *global*.
28
+
29
+ ## Anatomy
30
+
31
+ ```
32
+ CommandPalette
33
+ ├── Overlay / dialog surface (modal or non-modal per product; usually modal)
34
+ ├── Input (command filter; autofocused on open)
35
+ ├── List
36
+ │ ├── Group (optional) × N
37
+ │ │ ├── Group heading
38
+ │ │ └── Item × N
39
+ │ │ ├── Icon (optional)
40
+ │ │ ├── Label (required)
41
+ │ │ ├── Shortcut hint (optional; visual only if not a real keybinding)
42
+ │ │ └── Description (optional)
43
+ │ └── Empty message (no matching commands)
44
+ └── Footer hints (optional; keyboard legend)
45
+ ```
46
+
47
+ - **Input.** Filters commands; not a general document Search. Placeholder
48
+ like "Type a command or destination…".
49
+ - **Item.** One command or destination. Activating runs the action or
50
+ navigates.
51
+ - **Group.** Optional categories ("Navigation", "Replicas", "Settings").
52
+ - **Empty.** In-palette message when the filter matches nothing — not the
53
+ page-level [EmptyState](empty-state.md).
54
+
55
+ ## Variants
56
+
57
+ | Variant | Behavior |
58
+ | --- | --- |
59
+ | `commands` (default) | Mixed actions and destinations in one palette. |
60
+ | `navigation` | Destinations only (rare; prefer one palette with groups). |
61
+
62
+ Do not ship separate palettes per page unless the product truly scopes
63
+ commands; default is one app-level palette.
64
+
65
+ Surface: `--color-elevated-surface`, border `--color-border`, shadow
66
+ `--shadow-sm`, radius `--radius-lg`. Input and items use foreground /
67
+ muted-foreground roles. Active (highlighted) item uses `--color-surface` or
68
+ a quiet `--color-primary` indicator without filling the row in primary ink.
69
+
70
+ ## Sizes
71
+
72
+ One size. The palette is a centered (or top-anchored) elevated panel with
73
+ max width from layout tokens / spacing rhythm — not Button `sm|md|lg`.
74
+ Item row padding `--spacing-sm` block, `--spacing-md` inline. Type:
75
+ the five body typography properties for items; the five label typography
76
+ properties for group headings.
77
+
78
+ ## States
79
+
80
+ | State | Behavior |
81
+ | --- | --- |
82
+ | default (closed) | Not in the tree, or inert. Shortcut available. |
83
+ | open | Input focused; list visible; focus trapped within the palette while open (if modal). |
84
+ | hover | Item under pointer highlighted; keyboard highlight is source of truth when last input was keyboard. |
85
+ | focus | Input focus ring `--color-focus`. Highlighted item is the active descendant, not a second tab stop per row. |
86
+ | active | Item activation (Enter / click) runs the command and usually closes the palette. |
87
+ | disabled | Individual items may be disabled with `--color-disabled` and an explanation in description; prefer omitting unavailable commands. |
88
+ | loading | Optional: Spinner in the list while command providers resolve. Keep the input usable. `aria-busy` on the list region. |
89
+ | error | Provider failure: short in-palette message or Alert pattern inside the panel; do not fail silently to an empty list that looks like "no matches". |
90
+
91
+ ### Invocation (user story #5)
92
+
93
+ | Mechanism | Behavior |
94
+ | --- | --- |
95
+ | Shortcut | Default recommendation: `⌘K` (macOS) / `Ctrl+K` (Windows/Linux), documented in product chrome. Do not bind Search fields to this shortcut. |
96
+ | Explicit trigger | Optional Button/IconButton in the Header ("Search commands…") that opens the same palette. |
97
+ | Search-as-you-type | Filter updates the list as the person types. |
98
+ | Keyboard navigation | Arrow keys move the highlight; Enter activates; Escape closes. |
99
+
100
+ ## Accessibility
101
+
102
+ - Prefer a modal dialog pattern (`role="dialog"`, `aria-modal="true"`) with
103
+ an accessible name ("Command palette" / product-specific).
104
+ - Input has a visible or programmatically associated label.
105
+ - List uses `role="listbox"` (or cmdk's list semantics) with items as options;
106
+ the active item is exposed via `aria-activedescendant` on the input **or**
107
+ an equivalent pattern preserved from the reference library — do not invent
108
+ a broken tab-per-item list.
109
+ - Focus moves to the Input on open; on close, focus returns to the previously
110
+ focused element (or the trigger).
111
+ - Shortcut hints in items are decorative unless they document real bindings;
112
+ real bindings must work even when the palette is closed (where claimed).
113
+ - `Escape` closes without running a command.
114
+ - Reduced motion: no gratuitous entrance animation beyond token durations.
115
+
116
+ ### Keyboard
117
+
118
+ | Key | Action |
119
+ | --- | --- |
120
+ | `⌘K` / `Ctrl+K` | Toggle open/close (product default). |
121
+ | Printable keys | Filter commands (input focused). |
122
+ | `ArrowDown` / `ArrowUp` | Move highlight through items (and across groups). |
123
+ | `Enter` | Activate highlighted item. |
124
+ | `Escape` | Close without activating. |
125
+ | `Tab` | Generally stays within the palette (focus trap) while open; do not tab into the page behind a modal palette. |
126
+
127
+ ## When to use
128
+
129
+ - App-wide command and navigation entry (user stories #5, #19, #20).
130
+ - Power-user shortcuts to frequent destinations in data tools.
131
+ - Discoverability for actions that would otherwise hide in nested menus.
132
+
133
+ ## When NOT to use
134
+
135
+ - **Filtering a table or list in place.** [Search](search.md).
136
+ - **Actions on one row/button/avatar.** [DropdownMenu](dropdown-menu.md).
137
+ - **Confirming a destructive action.** Modal/Dialog (overlay slice) after
138
+ the command is chosen, if confirmation is required.
139
+ - **Form data entry.** Inputs and FormField — the palette is not a form.
140
+ - **Teaching the only path to a critical action.** Palette is acceleration;
141
+ critical actions still need a visible control somewhere.
142
+
143
+ ## Tokens
144
+
145
+ `--color-elevated-surface`, `--color-surface`, `--color-foreground`,
146
+ `--color-muted-foreground`, `--color-subtle-foreground`, `--color-border`,
147
+ `--color-primary` (highlight affordance only), `--color-focus`,
148
+ `--color-disabled`, `--shadow-sm`, `--spacing-sm` / `--spacing-md`,
149
+ `--radius-lg`, `--motion-duration-fast`, and `--motion-easing-standard`.
150
+ Items use `--type-role-body-font-family`, `--type-role-body-font-size`,
151
+ `--type-role-body-font-weight`, `--type-role-body-letter-spacing`, and
152
+ `--type-role-body-line-height`; group headings use the equivalent five
153
+ property-qualified label tokens. No raw hex/px.
154
+
155
+ ## Radix/shadcn mapping
156
+
157
+ | Kiso | Reference |
158
+ | --- | --- |
159
+ | Behavior | [cmdk](https://cmdk.paco.me/) via shadcn [Command](https://ui.shadcn.com/docs/components/command) |
160
+ | Dialog shell | shadcn Command Dialog example (Radix Dialog) for modal presentation |
161
+ | Input + list + groups + items | `Command`, `CommandInput`, `CommandList`, `CommandEmpty`, `CommandGroup`, `CommandItem`, `CommandShortcut` |
162
+
163
+ Preserve cmdk keyboard and filter behavior. Restyle surfaces and text to Kiso
164
+ semantic tokens. Do not treat Command as Search, and do not use Command as a
165
+ DropdownMenu replacement for row actions.
@@ -0,0 +1,79 @@
1
+ # Drawer
2
+
3
+ A viewport-adaptive overlay that enters from an edge and can replace a Modal
4
+ when a small viewport needs a more usable layout.
5
+
6
+ ## Purpose
7
+
8
+ Drawer preserves context while giving forms, details, or focused tasks more
9
+ vertical room. Use it as the small-viewport presentation of the same task that
10
+ may appear in [Modal/Dialog](modal-dialog.md) on larger viewports; behavior and
11
+ accessible name stay consistent across the switch.
12
+
13
+ ## Anatomy
14
+
15
+ ```
16
+ Drawer Root
17
+ ├── Trigger
18
+ ├── Overlay
19
+ └── Content
20
+ ├── handle (optional, decorative)
21
+ ├── Title
22
+ ├── Description (optional)
23
+ ├── body
24
+ ├── actions
25
+ └── Close control
26
+ ```
27
+
28
+ Content uses `--color-elevated-surface`, `--color-foreground`,
29
+ `--color-border`, `--spacing-lg` padding, `--radius-lg`, and `--shadow-md`.
30
+ Entry/exit uses `--motion-duration-normal` and `--motion-easing-standard`, with
31
+ no travel under reduced motion.
32
+
33
+ ## Variants
34
+
35
+ Two placements: bottom (default for small-viewport task adaptation) and side
36
+ for contextual detail or editing. Placement must not change Dialog semantics or
37
+ the task's accessible name.
38
+
39
+ ## Sizes
40
+
41
+ One responsive size per placement. Content is bounded by the viewport and the
42
+ host layout; do not introduce `sm` / `md` / `lg` Drawer widths.
43
+
44
+ ## States
45
+
46
+ | State | Behavior |
47
+ | --- | --- |
48
+ | closed (default) | Content is absent; trigger remains available. |
49
+ | hover | Trigger and child controls own hover. |
50
+ | focus | Opening moves focus inside; focused controls show `--color-focus`. |
51
+ | active/open | Page behind is inert and focus is trapped. |
52
+ | disabled | Disabled trigger does not open; Drawer itself is not disabled. |
53
+ | dragging | Optional touch dismissal follows the pointer and cancels below the component's deliberate threshold. |
54
+ | loading/error | Same task behavior as Modal/Dialog; do not dismiss on failure. |
55
+
56
+ ## Accessibility
57
+
58
+ Use Dialog semantics (`role="dialog"`, `aria-modal="true"`, labelled title),
59
+ focus trap, background inertness, and focus return. `Escape` closes when safe;
60
+ `Tab` stays inside. Swipe/drag dismissal must have an equivalent Close Button,
61
+ must not be the only way out, and must not discard work accidentally.
62
+
63
+ ## When to use
64
+
65
+ - A Modal task that needs a small-viewport, edge-to-edge presentation.
66
+ - Contextual detail or editing where retaining the underlying page matters.
67
+
68
+ ## When NOT to use
69
+
70
+ - Merely because the design wants animation from an edge.
71
+ - Primary application navigation that should remain persistent; use Sidebar.
72
+ - A short anchored choice; use Popover or DropdownMenu.
73
+ - A full workflow that deserves its own route.
74
+
75
+ ## Radix/shadcn mapping
76
+
77
+ Maps behavior to Radix Dialog and presentation to shadcn Sheet. Keep Dialog
78
+ focus management. A gesture-oriented drawer library may supply drag behavior,
79
+ but it must preserve these semantics and semantic tokens.
@@ -0,0 +1,178 @@
1
+ # DropdownMenu
2
+
3
+ A contextual menu of actions anchored to a specific control. It opens on
4
+ demand, stays near its trigger, and closes after a choice or dismissal.
5
+
6
+ ## Purpose
7
+
8
+ DropdownMenu offers actions *about this thing*: a row, an avatar, a kebab in
9
+ a Card header, a column of overflow actions. The trigger owns the context;
10
+ the menu does not search the whole app.
11
+
12
+ User story #20.
13
+
14
+ ### Choose the right action menu
15
+
16
+ | Need | Control | Why |
17
+ | --- | --- | --- |
18
+ | Actions on a specific element | **DropdownMenu** | Anchored; contextual. |
19
+ | Global actions / navigation by query | **[CommandPalette](command-palette.md)** | Not anchored to one element. |
20
+ | Single choice that sets a value | **[Select](select.md)** | Value selection, not a list of verbs. |
21
+ | Navigate to a URL as primary affordance | **[Link](link.md)** | Real navigation; menu items may still contain links when appropriate. |
22
+
23
+ DropdownMenu vs CommandPalette (user story #20): if the person must first
24
+ find the object, then open its menu, use DropdownMenu. If they are running
25
+ a global command without a local trigger, use CommandPalette.
26
+
27
+ Header (navigation slice) may compose DropdownMenu for account or overflow
28
+ actions. [Table / DataTable](table.md) uses it for row actions.
29
+
30
+ ## Anatomy
31
+
32
+ ```
33
+ DropdownMenu
34
+ ├── Trigger (Button, IconButton, or other focusable control)
35
+ └── Content (portaled elevated surface)
36
+ ├── Label (optional section label)
37
+ ├── Item × N
38
+ │ ├── leading icon (optional)
39
+ │ ├── label (required)
40
+ │ ├── shortcut hint (optional)
41
+ │ └── destructive styling (optional)
42
+ ├── Separator (optional)
43
+ ├── Checkbox item (optional; rare)
44
+ ├── Radio group (optional; rare)
45
+ └── Submenu (optional)
46
+ ├── Sub-trigger
47
+ └── Sub-content
48
+ ```
49
+
50
+ - **Trigger.** Usually [IconButton](icon-button.md) (`ghost` `sm`) with an
51
+ accessible name ("Actions for {row}", "Open account menu"). Never an
52
+ unnamed icon.
53
+ - **Item.** A verb or destination ("Edit", "Duplicate", "Delete", "View
54
+ logs"). Prefer verbs for actions.
55
+ - **Separator.** Groups related items; decorative.
56
+ - **Destructive item.** Irreversible actions; use danger treatment on the
57
+ item label/icon, not a filled danger panel. Confirm with Modal when stakes
58
+ are high (overlay slice).
59
+
60
+ ## Variants
61
+
62
+ Presentation is one menu system; item *kinds* vary:
63
+
64
+ | Kind | When |
65
+ | --- | --- |
66
+ | `action` (default) | Runs a command or opens a follow-on UI. |
67
+ | `link` | Navigates; implement as a real link item when the reference supports it so open-in-new-tab works. |
68
+ | `destructive` | Destructive/irreversible action. |
69
+ | `checkbox` / `radio` | Rare in Kiso v1; only when the menu is the established pattern for a compact multi/one option set. Prefer Select/Checkbox in forms. |
70
+
71
+ Trigger variants come from Button/IconButton — DropdownMenu does not define
72
+ a parallel size/color system for the trigger.
73
+
74
+ Content tokens: background `--color-elevated-surface`, border
75
+ `--color-border`, text `--color-foreground`, muted hints
76
+ `--color-muted-foreground`, destructive `--color-danger`, focus/highlight
77
+ `--color-focus` / quiet `--color-surface` for the highlighted item. Radius
78
+ `--radius-md`. Padding `--spacing-xs` around the list; item padding
79
+ `--spacing-sm` / `--spacing-md`.
80
+
81
+ ## Sizes
82
+
83
+ | Axis | Rule |
84
+ | --- | --- |
85
+ | Trigger | IconButton/Button `sm` in tables and dense chrome; `md` in headers. |
86
+ | Content | Min width fits labels; match trigger width only when it helps. No raw px: measure from the body/label typography properties and add the `--spacing-md` inline item padding. |
87
+ | Items | One density; do not ship `sm`/`lg` item scales. |
88
+
89
+ ## States
90
+
91
+ | State | Behavior |
92
+ | --- | --- |
93
+ | default (closed) | Trigger at rest; content unmounted or hidden. |
94
+ | hover | Trigger and highlighted item show quiet emphasis. |
95
+ | focus | Trigger shows `--color-focus` when focused. Open content uses highlighted item semantics. |
96
+ | open / active | `data-state="open"` on trigger; content visible; focus moves into the menu per Radix model. |
97
+ | disabled | Trigger cannot open, or individual items disabled with `--color-disabled`. Prefer omitting unavailable items when absence is clear. |
98
+ | loading | Rare on the menu itself. A trigger IconButton may show loading after an item was chosen and the menu has closed. Do not leave a stuck open menu in a loading limbo. |
99
+ | error | Not a menu chrome state. Failures after an action use Alert/Toast (as appropriate) on the page. |
100
+
101
+ ### Open / close
102
+
103
+ - Open on click / Enter / Space / ArrowDown on the trigger (per Radix).
104
+ - Close on item select, `Escape`, focus loss outside, or opening another
105
+ overlay — preserve Radix dismiss behavior.
106
+ - Pointer: hover may highlight items; selection commits on click, not merely
107
+ on hover.
108
+
109
+ ## Accessibility
110
+
111
+ - Trigger must have an accessible name. Icon-only triggers use `aria-label`
112
+ or `aria-labelledby`.
113
+ - Use Radix Dropdown Menu roles (`menu`, `menuitem`, etc.) — do not fake a
114
+ menu with a div list lacking keyboard support.
115
+ - `aria-expanded` / `aria-controls` (or the library equivalents) reflect open
116
+ state.
117
+ - Destructive items must not rely on color alone; include clear labeling
118
+ ("Delete replica").
119
+ - Submenus: follow Radix focus movement; do not invent a second Escape
120
+ model.
121
+ - Do not place essential instructions only inside a closed menu.
122
+
123
+ ### Keyboard
124
+
125
+ | Key | Action |
126
+ | --- | --- |
127
+ | `Enter` / `Space` | Open from trigger; activate focused item when open. |
128
+ | `ArrowDown` / `ArrowUp` | Open from trigger (where supported) or move between items. |
129
+ | `Home` / `End` | Move to first / last item when supported. |
130
+ | `ArrowRight` / `ArrowLeft` | Open / close submenu when present. |
131
+ | `Escape` | Close menu; return focus to trigger. |
132
+ | Typeahead | Focus the item matching typed characters when supported. |
133
+
134
+ ## When to use
135
+
136
+ - Overflow / kebab actions on a table row, Card, or list item.
137
+ - Account or session menus in Header chrome.
138
+ - A small set of contextual verbs that would clutter the layout if always
139
+ visible (user story #20).
140
+
141
+ ## When NOT to use
142
+
143
+ - **Global command search.** [CommandPalette](command-palette.md).
144
+ - **Filtering visible content.** [Search](search.md).
145
+ - **Primary page actions.** Prefer visible [Button](button.md)s in
146
+ PageHeader; menus are secondary/overflow.
147
+ - **Choosing a single form value from many.** [Select](select.md).
148
+ - **Navigation that should look like a menu of destinations as the main IA.**
149
+ Prefer Sidebar / Navigation (navigation slice); a DropdownMenu of links is
150
+ fine for compact account/overflow only.
151
+ - **Tooltips.** Tooltips are non-essential hints ([Tooltip](tooltip.md));
152
+ menus are actionable.
153
+
154
+ ## Tokens
155
+
156
+ `--color-elevated-surface`, `--color-surface`, `--color-foreground`,
157
+ `--color-muted-foreground`, `--color-border`, `--color-danger`,
158
+ `--color-focus`, `--color-disabled`, `--spacing-xs` list padding,
159
+ `--spacing-sm` / `--spacing-md` item padding, `--radius-md`, `--shadow-sm`,
160
+ the five property-qualified body typography tokens for items, the five label
161
+ typography tokens for group labels, `--motion-duration-fast`, and
162
+ `--motion-easing-standard`. Trigger consumes Button/IconButton tokens. No raw
163
+ hex/px.
164
+
165
+ ## Radix/shadcn mapping
166
+
167
+ | Kiso | Reference |
168
+ | --- | --- |
169
+ | Behavior | Radix [Dropdown Menu](https://www.radix-ui.com/primitives/docs/components/dropdown-menu) |
170
+ | Styling / composition | shadcn [Dropdown Menu](https://ui.shadcn.com/docs/components/dropdown-menu) |
171
+ | Trigger | Kiso [Button](button.md) / [IconButton](icon-button.md) via `asChild` / Slot when needed |
172
+ | Destructive item | shadcn `destructive` item class → `--color-danger` text/icon, not filled |
173
+
174
+ Preserve Radix focus management, typeahead, submenu behavior, and portal
175
+ positioning. Restyle with Kiso semantic tokens only.
176
+
177
+ Do **not** map row actions to Command/cmdk. Do **not** use Select to fake an
178
+ action menu.
@@ -0,0 +1,142 @@
1
+ # EmptyState
2
+
3
+ A deliberate placeholder when a region has nothing to show. It explains
4
+ why the space is empty and may offer a next action.
5
+
6
+ ## Purpose
7
+
8
+ EmptyState replaces an expected list, table body, or collection when there
9
+ are zero items — either because the person has never created any, or because
10
+ filters/search matched none.
11
+
12
+ User story #13: the action is **optional**. Informational-only EmptyStates are
13
+ valid when there is nothing useful to do yet.
14
+
15
+ ### Choose the right empty
16
+
17
+ | Situation | Control | Why |
18
+ | --- | --- | --- |
19
+ | Zero items in a collection / table body | **EmptyState** | Explains emptiness; optional create/clear action. |
20
+ | Still loading | [Skeleton](skeleton.md) | Not empty — pending. |
21
+ | Failed to load | [Alert](alert.md) | Error, not emptiness. |
22
+ | A single field with no value | Placeholder / HelperText | Not a page-level empty. |
23
+
24
+ [Table / DataTable](table.md) composes EmptyState for its empty data state.
25
+
26
+ ## Anatomy
27
+
28
+ ```
29
+ EmptyState
30
+ ├── Illustration / icon (optional)
31
+ ├── Title (required)
32
+ ├── Description (optional; recommended when the title is not enough)
33
+ └── Action (optional: Button or Link)
34
+ ```
35
+
36
+ - **Illustration / icon.** Optional. Decorative (`aria-hidden`) when Title
37
+ carries the meaning. Prefer a simple icon over a large marketing
38
+ illustration in product UI.
39
+ - **Title.** What is empty, in product language ("No replicas yet", "No
40
+ queries match"). Follow [voice-and-tone](../voice-and-tone.md).
41
+ - **Description.** One or two short sentences: why, and what to do if there
42
+ is no Action control.
43
+ - **Action.** Optional [Button](button.md) (create, import, clear filters)
44
+ or [Link](link.md) when the next step is navigation. Never required.
45
+
46
+ ## Variants
47
+
48
+ | Variant | When | Action |
49
+ | --- | --- | --- |
50
+ | `first-run` | The collection has never had items. | Usually a primary Button ("Create replica"). |
51
+ | `no-results` | Filters or Search exclude everything. | Often "Clear filters" (default/ghost) or adjust Search; not "Create". |
52
+ | `informational` | Empty is expected and there is no useful action. | No Action. Title + Description only. |
53
+
54
+ Do not add severity variants (info/warning/error). Emptiness is not an
55
+ error — errors are Alert. Do not color the whole EmptyState with status
56
+ tokens.
57
+
58
+ Surface tokens: text `--color-foreground` / `--color-muted-foreground` on
59
+ the surrounding `--color-surface` or `--color-background`. Icon uses
60
+ `--color-muted-foreground` unless it is purely decorative brand chrome.
61
+
62
+ ## Sizes
63
+
64
+ | Size | Use | Tokens |
65
+ | --- | --- | --- |
66
+ | `md` (default) | Table bodies, Card content, list panels. | Title uses the five heading typography properties, or the five label typography properties for label emphasis; description uses the five body typography properties; padding `--spacing-lg`; gap `--spacing-sm`. |
67
+ | `sm` | Narrow side panels or compact nested regions. | Tighter padding `--spacing-md`; smaller icon; same type roles if readable. |
68
+
69
+ Action Buttons use Button `md` by default; `sm` only inside `sm` EmptyState
70
+ in dense chrome. Do not invent an `lg` EmptyState that competes with
71
+ PageHeader.
72
+
73
+ ## States
74
+
75
+ | State | Behavior |
76
+ | --- | --- |
77
+ | default | Visible empty region. |
78
+ | hover / focus / active | EmptyState itself is not a control. Focus goes to Action if present. |
79
+ | disabled | N/A. Hide the region or show a different state. |
80
+ | loading | Do not show EmptyState while loading — use Skeleton. If the Action triggers creation, that Button may enter loading. |
81
+ | error | Not an EmptyState state. Switch to Alert. |
82
+
83
+ ## Accessibility
84
+
85
+ - EmptyState is a region with an accessible name from Title
86
+ (`aria-labelledby`) and optional `aria-describedby` for Description.
87
+ - Prefer landmark/region semantics only when the empty area is a major
88
+ page section; inside a table body, keep table structure and place
89
+ EmptyState content in a single full-width cell or a documented
90
+ replacement region announced as the table's status.
91
+ - Icon/illustration: `aria-hidden="true"` when Title is present.
92
+ - Action is a real Button or Link with its own accessible name. Do not make
93
+ the entire EmptyState clickable.
94
+ - When EmptyState appears because Search filtered everything, ensure the
95
+ Search field remains reachable and that the empty message is findable
96
+ by screen reader users (polite live update only if the change is not
97
+ obvious from focus).
98
+
99
+ ### Keyboard
100
+
101
+ | Key | Action |
102
+ | --- | --- |
103
+ | `Tab` / `Shift+Tab` | Reach the Action if present. |
104
+ | `Enter` / `Space` | Activate the focused Action. |
105
+
106
+ ## When to use
107
+
108
+ - A list, table body, or collection has zero items to show.
109
+ - First-run onboarding for a createable resource (with Action).
110
+ - No Search/filter matches (with copy that reflects filters).
111
+ - Informational empty when no action exists (user story #13).
112
+
113
+ ## When NOT to use
114
+
115
+ - **Loading.** Skeleton (or Spinner for indeterminate non-layout waits).
116
+ - **Errors.** Alert with retry.
117
+ - **Permission denied.** Alert or a dedicated locked state — not "No items".
118
+ - **Marketing empty.** Product EmptyState is operational, not a billboard.
119
+ - **Replacing a whole app shell.** Page-level emptiness still sits inside the
120
+ shell; do not delete navigation to show EmptyState.
121
+
122
+ ## Tokens
123
+
124
+ `--color-foreground`, `--color-muted-foreground`, `--color-surface` /
125
+ `--color-background`, optional icon `--color-muted-foreground`, `--spacing-lg`
126
+ padding (`--spacing-md` for `sm`), `--spacing-sm` gap, `--radius-md`, and
127
+ `--motion-duration-fast` / `--motion-easing-standard`. Typography uses every
128
+ property of the selected heading, label, and body roles named above. Action consumes Button/Link
129
+ tokens. No raw hex/px.
130
+
131
+ ## Radix/shadcn mapping
132
+
133
+ No Radix EmptyState primitive.
134
+
135
+ | Kiso | Reference |
136
+ | --- | --- |
137
+ | Composition pattern | shadcn has no dedicated Empty component in core; compose layout + typography + [Button](button.md) like common "empty" blocks in shadcn Data Table examples |
138
+ | Action | shadcn / Kiso [Button](button.md) or [Link](link.md) |
139
+ | Inside tables | shadcn Data Table empty row patterns → replace with this anatomy |
140
+
141
+ Do not copy illustration-heavy marketing empties. Keep Kiso EmptyState
142
+ quiet and actionable.
@@ -0,0 +1,115 @@
1
+ # FormField
2
+
3
+ ## Purpose
4
+
5
+ FormField is the standard composition for one labeled form control. It aligns
6
+ identification, entry, guidance, and field-level feedback so their visual and
7
+ accessible relationships remain intact. FormField is not a primitive and does
8
+ not replace the semantics of its children.
9
+
10
+ ## Anatomy
11
+
12
+ The canonical composition is:
13
+
14
+ 1. **Label** — required; names the control through matching `for`/`id`.
15
+ 2. **Input** — the default control in this composition. Textarea, Select,
16
+ Checkbox, or Switch may occupy the control slot when appropriate.
17
+ 3. **HelperText** — optional; provides persistent context, format, or scope.
18
+ 4. **ValidationMessage** — optional until invalid; explains a field-level error
19
+ and recovery.
20
+
21
+ For the issue's foundational chain, read this literally as **Label + Input +
22
+ HelperText + ValidationMessage**. FormField owns layout and ID wiring; each
23
+ child retains its own behavior.
24
+
25
+ ```text
26
+ Label
27
+ Input
28
+ HelperText
29
+ ValidationMessage
30
+ ```
31
+
32
+ ## Variants
33
+
34
+ - **Standard** — Label above Input, supporting text below.
35
+ - **Required** — control exposes required semantics and the visible convention
36
+ is consistent across the form.
37
+ - **Optional** — Label carries an “Optional” qualifier when useful.
38
+ - **Horizontal** — Label and control columns for wide, dense settings pages;
39
+ collapses without changing reading order.
40
+ - **Control substitution** — replaces Input with another Kiso form primitive
41
+ while preserving Label and description/error wiring.
42
+
43
+ ## Sizes
44
+
45
+ - **Small** — inherits the small size of its control and compact semantic gaps.
46
+ - **Medium** — default.
47
+ - **Large** — inherits the large control size where that control supports it.
48
+
49
+ FormField does not scale text independently. Label uses the five
50
+ property-qualified label typography tokens; supporting text uses the five
51
+ property-qualified metadata typography tokens. Use `--spacing-xs` between a
52
+ control and supporting text and `--spacing-sm` between the label and control.
53
+
54
+ ## States
55
+
56
+ | State | Behavior |
57
+ | --- | --- |
58
+ | Default | Label, control, and optional HelperText form one readable group. |
59
+ | Hover | Delegated to the interactive control; layout does not change. |
60
+ | Focus | Control owns the visible focus ring; supporting content remains stable. |
61
+ | Active | Delegated to the control. |
62
+ | Disabled | Control is disabled; Label and supporting text communicate unavailability without hiding context. |
63
+ | Loading | Control exposes busy status and loading affordance while Label/help remain readable. |
64
+ | Error | Control has `aria-invalid="true"`; ValidationMessage appears without removing useful HelperText. |
65
+
66
+ Disabled and loading remain distinct at composition level. Loading is a live
67
+ process; disabled is unavailable. Showing ValidationMessage must not cause the
68
+ control, Label, or existing help to lose their associations.
69
+
70
+ ## Accessibility
71
+
72
+ - Generate stable, collision-free IDs. Label `for` points to the control `id`.
73
+ - HelperText and ValidationMessage each have an ID. The control's
74
+ `aria-describedby` contains the IDs of every present description, separated
75
+ by spaces; preserve HelperText when an error appears if it is still useful.
76
+ - Invalid controls set `aria-invalid="true"`. ValidationMessage may use a live
77
+ region for errors introduced after interaction, but avoid duplicate
78
+ announcements caused by simultaneous alert and description behavior.
79
+ - Required, disabled, read-only, and busy semantics belong on the control.
80
+ - DOM reading order follows Label → control → HelperText → ValidationMessage,
81
+ even in a horizontal visual layout.
82
+ - FormField adds no keyboard interaction. The contained control keeps its native
83
+ or Radix keyboard contract, and clicking Label targets that control.
84
+
85
+ ## When to use
86
+
87
+ - For nearly every standalone labeled form control.
88
+ - To make accessible ID wiring and vertical rhythm consistent.
89
+ - When a control needs help, validation, required/optional status, or all three.
90
+
91
+ ## When NOT to use
92
+
93
+ - Do not use as a generic layout wrapper or fieldset for unrelated controls.
94
+ - Do not duplicate a Label or description already supplied by a composite
95
+ control.
96
+ - Do not render an empty ValidationMessage merely to reserve space unless the
97
+ product has measured layout-stability needs.
98
+ - Do not put form-level or page-level errors here; ValidationMessage is
99
+ field-level feedback.
100
+
101
+ ## Tokens
102
+
103
+ FormField consumes `--spacing-xs`, `--spacing-sm`, and the label and metadata
104
+ typography properties named above for layout. Its
105
+ children own colors: foreground/muted text, surface/border, focus, disabled,
106
+ and danger. The composition introduces no primitive token or raw value.
107
+
108
+ ## Radix/shadcn mapping
109
+
110
+ There is no single Radix FormField primitive. The composition uses Radix Label
111
+ and the relevant Radix control when one exists. It maps behaviorally to the
112
+ [shadcn/ui Field](https://ui.shadcn.com/docs/components/field) composition and
113
+ the form patterns documented by shadcn, while Kiso's explicit contract remains
114
+ Label + Input + HelperText + ValidationMessage with deterministic IDs and ARIA
115
+ wiring.