@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,79 @@
1
+ # Header
2
+
3
+ Application-level chrome that keeps primary navigation and global actions in a
4
+ predictable place. Header is a layout container, not a primitive.
5
+
6
+ ## Purpose
7
+
8
+ Header orients the person and exposes a small set of destinations and actions
9
+ that remain useful across routes. It composes [Link](link.md) for navigation,
10
+ [IconButton](icon-button.md) for icon-only actions, and optionally
11
+ [DropdownMenu](dropdown-menu.md) for overflow. Do not replace those controls with clickable
12
+ containers.
13
+
14
+ ## Anatomy
15
+
16
+ ```
17
+ Header
18
+ ├── brand/home Link
19
+ ├── primary Navigation
20
+ │ └── Link(s)
21
+ └── actions
22
+ ├── IconButton(s)
23
+ └── DropdownMenu (optional, triggered by IconButton)
24
+ ```
25
+
26
+ Use `--color-surface`, `--color-foreground`, and `--color-border`;
27
+ `--spacing-md` block and `--spacing-lg` inline padding; `--spacing-md` item
28
+ gaps; the five property-qualified label typography tokens; `--radius-md` for
29
+ interactive children; `--motion-duration-fast`; and
30
+ `--motion-easing-standard`. The current Link
31
+ uses `aria-current="page"` and `--color-primary`.
32
+
33
+ ## Variants
34
+
35
+ No visual variants. Header has one structural treatment; its child Navigation
36
+ and actions determine the composition, while narrow-viewport collapse is a
37
+ state rather than a separate variant.
38
+
39
+ ## Sizes
40
+
41
+ No named sizes. Header spacing adapts responsively with semantic spacing tokens;
42
+ child Links, IconButtons, and DropdownMenu triggers keep their own sizes.
43
+
44
+ ## States
45
+
46
+ | State | Behavior |
47
+ | --- | --- |
48
+ | default | Brand, primary destinations, and global actions are visible. |
49
+ | hover | Child Links and IconButtons own hover feedback. |
50
+ | focus | Focus follows document order; each child shows `--color-focus`. |
51
+ | active | The current Link is marked; pressed actions keep their component behavior. |
52
+ | disabled | Header itself is never disabled. Unavailable children follow their own specs. |
53
+ | collapsed | At narrow viewports, preserve the home Link and essential actions; move lower-priority destinations into DropdownMenu. |
54
+
55
+ ## Accessibility
56
+
57
+ Use `<header>` and a labelled `<nav>` for the destination group. Do not add
58
+ `role="banner"` when native `<header>` already provides it. Keep one banner
59
+ landmark per page, label multiple navigation landmarks distinctly, and retain
60
+ normal Link and IconButton keyboard behavior. Opening DropdownMenu moves focus
61
+ according to its own roving-focus behavior; closing returns focus to its
62
+ IconButton trigger.
63
+
64
+ ## When to use
65
+
66
+ - Persistent product identity, top-level destinations, and global actions.
67
+ - Wide layouts where top navigation is clearer than a Sidebar.
68
+
69
+ ## When NOT to use
70
+
71
+ - For a page title and page-specific actions; use [PageHeader](page-header.md).
72
+ - As a Card header or decorative masthead.
73
+ - For a long hierarchy; use [Sidebar](sidebar.md) or Breadcrumb.
74
+
75
+ ## Radix/shadcn mapping
76
+
77
+ No Radix Header primitive. Use native `<header>` + `<nav>`, Kiso Link and
78
+ IconButton, and shadcn Dropdown Menu / Radix Dropdown Menu when overflow is
79
+ needed. shadcn Navigation Menu is not required for a simple set of Links.
@@ -0,0 +1,86 @@
1
+ # HelperText
2
+
3
+ ## Purpose
4
+
5
+ HelperText gives persistent, non-error context for a form control: expected
6
+ format, scope, consequence, or a concise example. It helps a person succeed
7
+ before validation fails.
8
+
9
+ ## Anatomy
10
+
11
+ 1. **Text** — one concise instruction or explanation.
12
+ 2. **Identifier** — stable ID referenced by the control's
13
+ `aria-describedby`.
14
+ 3. **Supplementary link (optional)** — a separately named destination when
15
+ essential documentation cannot fit in concise help.
16
+
17
+ ## Variants
18
+
19
+ - **Instruction** — explains expected format or constraints.
20
+ - **Context** — explains where or how the value is used.
21
+ - **Example** — shows a representative value without becoming a default value.
22
+ - **With link** — points to deeper documentation; the sentence remains useful
23
+ without relying on the link text alone.
24
+
25
+ HelperText is never an error. ValidationMessage owns field-level invalid
26
+ feedback.
27
+
28
+ ## Sizes
29
+
30
+ HelperText has one typography size: `--type-role-metadata-font-size` with
31
+ `--type-role-metadata-line-height`. Its line length and wrapping follow the
32
+ control width. Use `--spacing-xs` between
33
+ the control, HelperText, and ValidationMessage.
34
+
35
+ ## States
36
+
37
+ | State | Behavior |
38
+ | --- | --- |
39
+ | Default | Uses `--color-muted-foreground` and remains readable as normal text. |
40
+ | Hover | No state unless it contains a link; only the link responds. |
41
+ | Focus | No state unless it contains a link; the link receives its own focus ring. |
42
+ | Active | No independent state. |
43
+ | Disabled | Usually remains readable to explain why the control is unavailable; do not automatically reduce it to illegibility. |
44
+ | Loading | Remains stable unless the guidance itself has genuinely changed. |
45
+ | Error | Remains visible when still useful; ValidationMessage appears separately with danger semantics. |
46
+
47
+ ## Accessibility
48
+
49
+ - Give HelperText a stable ID and include it in the associated control's
50
+ `aria-describedby` value.
51
+ - When both help and validation exist, reference both IDs in meaningful DOM
52
+ order. Do not overwrite one relationship with the other.
53
+ - Do not use `role="alert"`, `aria-live`, or `aria-invalid`; the text is
54
+ descriptive, not urgent status.
55
+ - Links use descriptive text and standard keyboard behavior.
56
+ - Keep instructions concise, direct, and available before input—not only on
57
+ hover or focus.
58
+
59
+ ## When to use
60
+
61
+ - To state a format, unit, scope, consequence, or short example.
62
+ - When context reduces avoidable validation errors.
63
+ - As the supporting description in FormField.
64
+
65
+ ## When NOT to use
66
+
67
+ - Do not use for an error; use ValidationMessage.
68
+ - Do not repeat the Label or placeholder.
69
+ - Do not put essential help only in a tooltip or placeholder.
70
+ - Do not add generic advice that does not change how the field is completed.
71
+
72
+ ## Tokens
73
+
74
+ Use `--color-muted-foreground`; `--type-role-metadata-font-family`,
75
+ `--type-role-metadata-font-size`, `--type-role-metadata-font-weight`,
76
+ `--type-role-metadata-letter-spacing`, and `--type-role-metadata-line-height`;
77
+ and `--spacing-xs`. `--color-subtle-foreground` is not suitable
78
+ for ordinary small helper copy because it is restricted to large or
79
+ non-essential hints. No primitive colors or raw values.
80
+
81
+ ## Radix/shadcn mapping
82
+
83
+ Radix has no standalone HelperText primitive. In shadcn compositions it maps
84
+ to field description/form description behavior (for example, the description
85
+ part of [shadcn/ui Field](https://ui.shadcn.com/docs/components/field)). Kiso's
86
+ required behavioral contract is the explicit `aria-describedby` relationship.
@@ -0,0 +1,161 @@
1
+ # IconButton
2
+
3
+ Triggers an in-page action whose only visible content is an icon. It must
4
+ expose an accessible name with `aria-label` (or `aria-labelledby`).
5
+
6
+ ## Purpose
7
+
8
+ IconButton is Button without a visible text label: compact chrome, toolbars,
9
+ dismiss controls, and header actions where a word would not fit.
10
+
11
+ It is still an *action*. It does not navigate. If the icon takes the person
12
+ to a URL, use [Link](link.md) with an icon, not IconButton.
13
+
14
+ ### Choose the right control
15
+
16
+ | Need | Control |
17
+ | --- | --- |
18
+ | Action with a visible text label (icon optional) | **[Button](button.md)** |
19
+ | Action with *only* an icon | **IconButton** |
20
+ | Navigation (URL changes) | **[Link](link.md)** |
21
+
22
+ If the icon is not universally understood on its own, prefer Button with a
23
+ visible label. IconButton is a density choice, not a decoration choice.
24
+
25
+ [Tooltip](tooltip.md) may repeat the accessible name on hover/focus. Tooltip
26
+ is progressive enhancement: it is not a substitute for `aria-label`, and it
27
+ is not available on touch. The `aria-label` is the name; the Tooltip is
28
+ optional clarification for pointer and keyboard users.
29
+
30
+ ## Anatomy
31
+
32
+ ```
33
+ IconButton
34
+ ├── icon (required, visually only)
35
+ ├── accessible name (required, not visible: aria-label or aria-labelledby)
36
+ ├── Tooltip (optional; same words as the accessible name)
37
+ └── Spinner (loading state only; replaces the icon)
38
+ ```
39
+
40
+ - **Icon.** Decorative. `aria-hidden="true"` so the name is not announced
41
+ twice.
42
+ - **Accessible name.** Short verb phrase, same rules as Button labels:
43
+ "Dismiss", "Open command palette", "Refresh replicas". Not "Button", not
44
+ the icon's filename.
45
+ - **Tooltip.** If present, its content *equals* the accessible name. Never
46
+ add extra essential instructions in the Tooltip.
47
+
48
+ ## Variants
49
+
50
+ Same four variants as [Button](button.md). Same token mapping. Same rule:
51
+ no "link" variant.
52
+
53
+ | Variant | Typical IconButton use |
54
+ | --- | --- |
55
+ | `default` | Standalone compact action on a surface. |
56
+ | `primary` | Rare. A single icon as the region's main action (e.g. "Run"). Prefer a labeled Button when space allows. |
57
+ | `destructive` | Irreversible icon action (delete). Confirm before committing; the icon alone is easy to hit by mistake. |
58
+ | `ghost` | Default for chrome: Header actions, Card header actions, Alert dismiss, table row actions. |
59
+
60
+ Header (a later navigation slice) composes Link + IconButton. IconButton in
61
+ that role is `ghost`.
62
+
63
+ ## Sizes
64
+
65
+ Square hit targets. Icon centered. Radius `--radius-md` (`sm` size uses
66
+ `--radius-sm`).
67
+
68
+ | Size | Type / icon | Padding | Use |
69
+ | --- | --- | --- | --- |
70
+ | `sm` | Icon scaled to `--type-role-metadata-font-size` | `--spacing-xs` | Inside Alert, Badge-adjacent chrome, dense tables. |
71
+ | `md` (default) | Icon scaled to `--type-role-label-font-size` | `--spacing-sm` | Toolbars, Card actions, Header. |
72
+ | `lg` | Icon scaled to `--type-role-body-font-size` | `--spacing-md` | Rare; empty-state or touch-first primary icon. |
73
+
74
+ The hit target must remain easy to activate; do not shrink `sm` below the
75
+ spacing tokens above.
76
+
77
+ ## States
78
+
79
+ Identical in meaning to [Button](button.md):
80
+
81
+ | State | Notes |
82
+ | --- | --- |
83
+ | default | Interactive. |
84
+ | hover | Variant hover tokens. |
85
+ | focus | Visible ring using `--color-focus`. Tooltip, if any, opens on focus (see Tooltip keyboard). |
86
+ | active | Pressed. |
87
+ | disabled | Native `disabled`. `--color-disabled`. If the person needs to know *why*, put that in adjacent copy — not only in a Tooltip (Tooltip is not on touch and is never essential). |
88
+ | loading | `aria-busy="true"`. Replace the icon with [Spinner](spinner.md) of the matching size. Ignore further activations. Distinct from disabled (user story #12). |
89
+ | error | Not an IconButton state. |
90
+
91
+ ## Accessibility
92
+
93
+ - Native `<button type="button">` unless it submits (`type="submit"`).
94
+ - **Accessible name is mandatory.** Set `aria-label` or point
95
+ `aria-labelledby` at visible text elsewhere. An IconButton without a name
96
+ is a spec violation, not a styling choice.
97
+ - The SVG/icon is `aria-hidden="true"` (and `focusable="false"` if the
98
+ graphic could take focus).
99
+ - Do not use a different `aria-label` than the Tooltip text.
100
+ - Touch: there is no Tooltip. The icon must be understandable from context
101
+ (a well-known metaphor next to the thing it affects) or the control must
102
+ be a labeled Button instead.
103
+ - Focus ring uses `--color-focus`. Never omit it because "the tooltip
104
+ explains the control".
105
+
106
+ ### Keyboard
107
+
108
+ | Key | Action |
109
+ | --- | --- |
110
+ | `Enter` | Activate. |
111
+ | `Space` | Activate. |
112
+ | `Tab` / `Shift+Tab` | Move focus. |
113
+
114
+ Tooltip keyboard (open on focus, dismiss on `Escape`) is defined in
115
+ [Tooltip](tooltip.md). Activation of the IconButton dismisses the Tooltip.
116
+
117
+ ## When to use
118
+
119
+ - A compact action where a text label would add noise: dismiss, overflow
120
+ menu, refresh, copy, favorite, close.
121
+ - Header and toolbar actions.
122
+ - The accessible name is a short, specific verb, and the icon is recognizable
123
+ in context.
124
+
125
+ ## When NOT to use
126
+
127
+ - **There is room for a text label.** Use [Button](button.md). Density is
128
+ not a reason to hide the verb on a primary action.
129
+ - **Navigation.** Use [Link](link.md). A "settings gear" that goes to
130
+ `/settings` is a Link, possibly styled as an icon.
131
+ - **The only explanation of the control is a Tooltip.** Tooltip is never
132
+ essential (user story #10). If the person cannot succeed without the
133
+ Tooltip, use a labeled Button.
134
+ - **Touch-first primary actions** that are not standard metaphors (close,
135
+ search, add). Prefer Button with text.
136
+ - **Toggle with two lasting states** (on/off). Use Switch (form slice).
137
+
138
+ ## Radix/shadcn mapping
139
+
140
+ No dedicated Radix or shadcn `IconButton` primitive. Implement as shadcn
141
+ [Button](https://ui.shadcn.com/docs/components/button) with an icon size:
142
+
143
+ | Kiso | shadcn |
144
+ | --- | --- |
145
+ | IconButton `sm` | `size="icon-sm"` (or `icon-xs` only if it still uses `--spacing-xs` padding) |
146
+ | IconButton `md` | `size="icon"` |
147
+ | IconButton `lg` | `size="icon-lg"` |
148
+ | variants | Same Button mapping as [Button](button.md) (Kiso `primary` and `default` both start from shadcn `outline`, restyled) |
149
+
150
+ Always pass `aria-label`. Do not rely on the icon's title or a Tooltip
151
+ alone.
152
+
153
+ Radix [Slot](https://www.radix-ui.com/primitives/docs/utilities/slot)
154
+ (`asChild`) is allowed only to merge IconButton props onto a native
155
+ `<button>`. That is the shadcn Button `asChild` path. It must not target
156
+ an `<a>` — that is [Link](link.md).
157
+
158
+ Optional Tooltip: wrap with shadcn/Radix [Tooltip](tooltip.md) and set the
159
+ content to the same string as `aria-label`.
160
+
161
+ Do not use shadcn `variant="link"`.
@@ -0,0 +1,99 @@
1
+ # Input
2
+
3
+ ## Purpose
4
+
5
+ Input collects a single-line text-like value such as a name, email address,
6
+ search term, URL, number, or password. Use the native input type that best
7
+ describes the value so browsers and assistive technology can provide the right
8
+ behavior.
9
+
10
+ ## Anatomy
11
+
12
+ 1. **Native input** — the editable value and browser semantics.
13
+ 2. **Leading affordance (optional)** — contextual icon or fixed prefix; never
14
+ the only label.
15
+ 3. **Trailing affordance (optional)** — fixed suffix, clear action, or status.
16
+ 4. **Loading affordance (optional)** — Spinner adjacent to the value without
17
+ replacing it or changing the field width.
18
+
19
+ Label, HelperText, and ValidationMessage belong to FormField rather than the
20
+ Input root.
21
+
22
+ ## Variants
23
+
24
+ - **Text** — general single-line entry.
25
+ - **Email, URL, telephone, number** — uses the corresponding native `type` and
26
+ appropriate `inputmode`; client validation does not replace server validation.
27
+ - **Password** — obscures the value; an optional show/hide action has an
28
+ accessible name and preserves focus.
29
+ - **Search** — use `type="search"` for a query field. Search behavior is defined
30
+ by the later Search component, not by Input alone.
31
+ - **Read-only** — value can be focused, selected, and copied but not edited.
32
+
33
+ ## Sizes
34
+
35
+ - **Small** — compact toolbars and dense technical forms.
36
+ - **Medium** — default for most forms.
37
+ - **Large** — rare, high-emphasis entry points.
38
+
39
+ Size changes internal padding from `--spacing-xs` / `--spacing-sm` for small,
40
+ to `--spacing-sm` / `--spacing-md` for medium, to `--spacing-md` / `--spacing-lg`
41
+ for large. It does not reduce text or target size below accessible product norms.
42
+
43
+ ## States
44
+
45
+ | State | Behavior |
46
+ | --- | --- |
47
+ | Default | `--color-surface` background, `--color-border` outline, `--color-foreground` value; placeholder uses `--color-subtle-foreground`. |
48
+ | Hover | Border emphasis may increase without changing layout or implying focus. |
49
+ | Focus | Visible `--color-focus` ring; do not rely on border color alone. |
50
+ | Active | Native text selection and editing behavior; no separate persistent visual state. |
51
+ | Disabled | Native `disabled`; not focusable or submitted, uses `--color-disabled`. |
52
+ | Loading | Remains readable and normally focusable; shows a Spinner, exposes busy status, and prevents conflicting submission-side edits only when necessary. |
53
+ | Error | Uses `aria-invalid="true"`, visible danger treatment with `--color-danger`, and a linked ValidationMessage. |
54
+
55
+ Disabled and loading are not interchangeable. Disabled means the field is
56
+ unavailable. Loading means work is in progress and must be communicated; do not
57
+ silently disable a field merely to show activity.
58
+
59
+ ## Accessibility
60
+
61
+ - Associate a visible Label through matching `for` and `id`.
62
+ - Use the correct `type`, `name`, `autocomplete`, `inputmode`, `required`,
63
+ `min`, `max`, and other native attributes for the data.
64
+ - Link HelperText and ValidationMessage IDs through a space-separated
65
+ `aria-describedby`. Add `aria-invalid="true"` only when invalid.
66
+ - If loading changes what the person can do, expose it with `aria-busy` on the
67
+ field or a nearby status region. Spinner alone is not an accessible status.
68
+ - Standard text-input keyboard behavior applies. Do not override selection,
69
+ cursor, undo, paste, or platform shortcuts. Any trailing action is separately
70
+ keyboard reachable and named.
71
+
72
+ ## When to use
73
+
74
+ - For one line of free-form or constrained text-like data.
75
+ - When native input semantics match the requested value.
76
+ - Inside FormField when a label, help, or validation feedback is needed.
77
+
78
+ ## When NOT to use
79
+
80
+ - Do not use for multi-line prose; use Textarea.
81
+ - Do not use for choosing one predefined option from many; use Select.
82
+ - Do not use for a boolean setting; use Switch, or Checkbox in a list/multi-select.
83
+ - Do not put essential instructions only in placeholder text.
84
+
85
+ ## Tokens
86
+
87
+ Use only semantic roles: `--color-surface`, `--color-foreground`,
88
+ `--color-subtle-foreground`, `--color-border`, `--color-focus`,
89
+ `--color-disabled`, and `--color-danger`; the five property-qualified body
90
+ typography tokens; `--spacing-xs`, `--spacing-sm`, `--spacing-md`, and
91
+ `--spacing-lg` as mapped above; `--radius-md`; `--shadow-sm` for elevated
92
+ contexts only; `--motion-duration-fast`; and `--motion-easing-standard`. Never
93
+ use palette primitives or raw values.
94
+
95
+ ## Radix/shadcn mapping
96
+
97
+ Maps to [shadcn/ui Input](https://ui.shadcn.com/docs/components/input), which
98
+ styles the native HTML `input`. Radix has no Input primitive; retain native HTML
99
+ semantics rather than introducing a custom interaction model.
@@ -0,0 +1,88 @@
1
+ # Label
2
+
3
+ ## Purpose
4
+
5
+ Label names a form control. It tells a person what value to provide and gives
6
+ the control its accessible name. Every visible form control has a persistent,
7
+ specific label; placeholder text is never a substitute.
8
+
9
+ ## Anatomy
10
+
11
+ 1. **Label text** — short, direct name for the value.
12
+ 2. **Required indicator (optional)** — textual or otherwise exposed to
13
+ assistive technology; color alone is insufficient.
14
+ 3. **Supplementary text (optional)** — a compact qualifier such as “Optional.”
15
+
16
+ The label and control are associated with matching `for` and `id` values. A
17
+ control wrapped by a label is valid HTML, but explicit association is the Kiso
18
+ default because it remains clear across composed layouts.
19
+
20
+ ## Variants
21
+
22
+ - **Default** — names one editable control.
23
+ - **Required** — identifies a required value. Prefer marking the smaller set:
24
+ if most fields are required, mark optional fields instead.
25
+ - **Optional** — appends a quiet “Optional” qualifier when that distinction is
26
+ useful.
27
+ - **Group label** — use `legend` inside `fieldset`, not Label, for a related set
28
+ of Checkbox controls.
29
+
30
+ ## Sizes
31
+
32
+ Label follows the associated control size rather than exposing an independent
33
+ size API. Use all five property-qualified label typography tokens. Keep the
34
+ label-to-control gap at `--spacing-sm`.
35
+
36
+ ## States
37
+
38
+ | State | Behavior |
39
+ | --- | --- |
40
+ | Default | Uses `--color-foreground`. |
41
+ | Hover | No independent hover treatment; clicking focuses or activates the associated control. |
42
+ | Focus | The control owns the visible focus indicator. |
43
+ | Active | No independent active treatment. |
44
+ | Disabled | Uses `--color-disabled` and matches the control's disabled semantics. |
45
+ | Loading | Remains readable while the control is loading. |
46
+ | Error | Remains readable; error meaning belongs to ValidationMessage and the control state, not color on the label alone. |
47
+
48
+ ## Accessibility
49
+
50
+ - Set `for` to the exact `id` of the associated control.
51
+ - Do not hide the only accessible name. A visually hidden label is acceptable
52
+ only when the visual context is unambiguous and the text remains available
53
+ to assistive technology.
54
+ - Required controls use native `required` when appropriate or
55
+ `aria-required="true"`; the visible indicator must also be explained.
56
+ - Clicking or tapping Label moves focus to text controls and toggles Checkbox
57
+ or Switch through their native/Radix behavior.
58
+ - Label adds no keyboard interaction of its own.
59
+
60
+ ## When to use
61
+
62
+ - To name Input, Textarea, Select, Checkbox, or Switch.
63
+ - As the Label part of FormField.
64
+ - When a visible prompt must remain available after a value is entered.
65
+
66
+ ## When NOT to use
67
+
68
+ - Do not use placeholder text as a label.
69
+ - Do not use Label as a section heading or explanatory paragraph.
70
+ - Do not use one Label for a group of controls; use `fieldset` and `legend`.
71
+ - Do not place instructions or errors in Label; use HelperText and
72
+ ValidationMessage.
73
+
74
+ ## Tokens
75
+
76
+ Use `--color-foreground` for label text, `--color-muted-foreground` for an
77
+ optional qualifier, `--color-disabled` for disabled text;
78
+ `--type-role-label-font-family`, `--type-role-label-font-size`,
79
+ `--type-role-label-font-weight`, `--type-role-label-letter-spacing`, and
80
+ `--type-role-label-line-height`; and `--spacing-sm`. Do not use primitive colors
81
+ or raw values.
82
+
83
+ ## Radix/shadcn mapping
84
+
85
+ Maps to [Radix Label](https://www.radix-ui.com/primitives/docs/components/label)
86
+ and [shadcn/ui Label](https://ui.shadcn.com/docs/components/label). Preserve
87
+ Radix Label's control association and prevention of accidental text selection;
88
+ the Kiso contract additionally requires explicit `for`/`id` association.
@@ -0,0 +1,152 @@
1
+ # Link
2
+
3
+ Navigates to a URL. It does not trigger in-page behavior the way a Button
4
+ does.
5
+
6
+ ## Purpose
7
+
8
+ Link is the control for *going somewhere*: another route, a hash on this
9
+ page, an external document. The browser (or router) changes location.
10
+
11
+ That is a different job from [Button](button.md) (action) and
12
+ [IconButton](icon-button.md) (icon-only action). User stories #1 and #29.
13
+
14
+ ### Choose the right control
15
+
16
+ | Need | Control | Why |
17
+ | --- | --- | --- |
18
+ | Trigger behavior; URL stays put | **[Button](button.md)** | Action. |
19
+ | Icon-only action; URL stays put | **[IconButton](icon-button.md)** | Action without visible text. |
20
+ | Change the URL | **Link** | Navigation. Open in new tab, copy URL, and middle-click must work. |
21
+
22
+ If it has an `href`, it is a Link. If it only has an `onClick`, it is a
23
+ Button (or it is a broken Link). Do not fake navigation with a Button, and
24
+ do not fake actions with an `<a>` that has no `href`.
25
+
26
+ A Link may be *styled* to look like a Button (prominent "Open dashboard"
27
+ call to action that still goes to a route). It remains a Link: real `href`,
28
+ link semantics, no `role="button"`.
29
+
30
+ ## Anatomy
31
+
32
+ ```
33
+ Link
34
+ ├── leading icon (optional)
35
+ ├── text (required, unless the icon Link has an accessible name)
36
+ └── trailing icon (optional; external-indicator is a trailing icon)
37
+ ```
38
+
39
+ - **Text.** The destination or the thing opened, not "click here". In-app:
40
+ "Replicas", "Query history". External: the resource name.
41
+ - **Icon.** Optional. Decorative (`aria-hidden="true"`) when text is present.
42
+ An icon-only Link still needs an accessible name (`aria-label`) *and* is
43
+ still a Link, not IconButton.
44
+ - **External indicator.** When the destination leaves the product, show it
45
+ visually (trailing icon) and in the accessible name if the visual cue is
46
+ not announced ("Docs (opens in a new tab)" when `target="_blank"`).
47
+
48
+ ## Variants
49
+
50
+ Links are distinguished by *context*, not by a Button-like variant enum.
51
+
52
+ | Variant | Appearance | Tokens |
53
+ | --- | --- | --- |
54
+ | `inline` (default) | In body copy or metadata. | Text `--color-primary`. Underline on hover at minimum; underline always in running body text so color is not the only cue (WCAG 1.4.1). |
55
+ | `standalone` | Nav items, lists of destinations. | Text `--color-foreground` at rest; `--color-primary` on hover/current. No underline required if the placement is unambiguously navigation (Header, sidebar). Focus ring still `--color-focus`. |
56
+ | `button-look` | A destination that must match Button visual weight. | Apply [Button](button.md) visual tokens (`primary` / `default` / `ghost`) to the Link. Keep `<a>` / router link semantics. |
57
+
58
+ Current-route indication (Header, nav): `aria-current="page"` on the Link
59
+ that matches the location. Color may use `--color-primary`; do not invent a
60
+ "current" token.
61
+
62
+ Visited: there is no visited semantic token. Keep `--color-primary` (or
63
+ `--color-foreground` for standalone). Do not reach into the primitive
64
+ palette for a visited purple.
65
+
66
+ ## Sizes
67
+
68
+ Links inherit the surrounding type role. Do not invent a parallel size scale.
69
+
70
+ | Context | Type role |
71
+ | --- | --- |
72
+ | Body copy | All five property-qualified body typography tokens |
73
+ | Navigation, labels | All five property-qualified label typography tokens |
74
+ | Chrome / metadata | All five property-qualified metadata typography tokens |
75
+ | `button-look` | Same padding, radius, and five property-qualified label typography tokens as the matching [Button](button.md) size (`sm` / `md` / `lg`) |
76
+
77
+ ## States
78
+
79
+ | State | Behavior | Tokens / notes |
80
+ | --- | --- | --- |
81
+ | default | Navigable. | Variant tokens above. |
82
+ | hover | Pointer over the link. | `--color-primary` (or `--color-accent` on an already-primary inline link). Underline for `inline`. Transition `--motion-duration-fast` / `--motion-easing-standard`. |
83
+ | focus | Keyboard focus. | Visible ring `--color-focus`. Never rely on underline alone for focus. |
84
+ | active | Activation. | Brief press; then navigation proceeds. |
85
+ | disabled | Rare. A destination that exists but is unavailable. | Prefer omitting the Link and explaining why. If it must remain for layout, use `aria-disabled="true"`, remove `href` (or prevent navigation), `--color-disabled`, and keep it out of the tab order. A Link with `href` that does nothing is a trap. |
86
+ | loading | Optional, for client-side transitions. | `aria-busy="true"` on the Link or its region. Do not replace the Link with a [Spinner](spinner.md) that loses the href. |
87
+ | error | Not a Link state. | Failed navigation belongs in [Alert](alert.md). |
88
+
89
+ ## Accessibility
90
+
91
+ - Render a real `<a href="…">` or the framework equivalent that still
92
+ produces one (e.g. Next.js `Link`). No `div` + click handler.
93
+ - `href` is required. `#` as a fake href for an action is a Button in
94
+ disguise — rewrite it as Button.
95
+ - Accessible name: visible text, or `aria-label` for icon-only Links.
96
+ - `target="_blank"` requires `rel="noopener noreferrer"` and an indication
97
+ that a new context opens.
98
+ - `aria-current="page"` for the current location in a nav set.
99
+ - Do not set `role="button"` on a Link. Do not handle `Space` as activation
100
+ to mimic Button; Space scrolls, Enter follows the link.
101
+ - Underline (or another non-color cue) in running text.
102
+
103
+ ### Keyboard
104
+
105
+ | Key | Action |
106
+ | --- | --- |
107
+ | `Enter` | Follow the link. |
108
+ | `Tab` / `Shift+Tab` | Move focus. |
109
+ | `Space` | Does **not** follow the link (page scroll). |
110
+
111
+ Browser shortcuts (new tab, copy link address, back) must keep working —
112
+ another reason this cannot be a `<button>`.
113
+
114
+ ## When to use
115
+
116
+ - In-app routing: another view, a record, settings, docs hosted in-product.
117
+ - External URLs.
118
+ - In-page anchors (`#anatomy`).
119
+ - Header navigation. Header composes Link + IconButton (later slice).
120
+ - Any control the person should be able to bookmark, share, or open in a
121
+ new tab.
122
+
123
+ ## When NOT to use
124
+
125
+ - **Actions.** Submit, save, delete, open a dialog, run a job —
126
+ [Button](button.md) or [IconButton](icon-button.md).
127
+ - **A control whose `href` is unknown until click.** If there is no URL
128
+ until after a side effect, it is an action (Button), then optionally
129
+ navigate.
130
+ - **Toggles, disclosures, tabs.** Those have their own components; a Link
131
+ that only swaps a panel without a URL is a Button or a Tab.
132
+ - **Breadcrumb separators or decorative slashes.** Those are not Links;
133
+ only the path segments that go somewhere are.
134
+
135
+ ## Radix/shadcn mapping
136
+
137
+ No Radix Link primitive. No shadcn Link component.
138
+
139
+ | Kiso | Reference |
140
+ | --- | --- |
141
+ | Semantics and keyboard | Native `<a>` / platform router Link |
142
+ | `button-look` visuals | shadcn [Button](https://ui.shadcn.com/docs/components/button) `buttonVariants` (or equivalent classes) applied to `<a>` |
143
+ | Icon-only Link name | Same `aria-label` rule as [IconButton](icon-button.md), but the element is still `<a href>` |
144
+
145
+ shadcn documents an "As Link" pattern and warns that some Button
146
+ implementations force `role="button"`, which **overrides** link semantics.
147
+ If the chosen Button primitive does that, do not wrap the Link in it. Apply
148
+ visual classes to the anchor instead.
149
+
150
+ Do not use shadcn `variant="link"` on a `<button>`. That produces a Button
151
+ that looks like a Link — the inverse of this spec, and the usual agent
152
+ mistake for user stories #1 and #29.