@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.
- package/LICENSE +21 -0
- package/README.md +63 -0
- package/kiso/AGENTS.md +50 -0
- package/kiso/README.md +62 -0
- package/kiso/docs/accessibility.md +87 -0
- package/kiso/docs/brand.md +95 -0
- package/kiso/docs/components/README.md +58 -0
- package/kiso/docs/components/alert.md +158 -0
- package/kiso/docs/components/badge.md +135 -0
- package/kiso/docs/components/breadcrumb.md +66 -0
- package/kiso/docs/components/button.md +168 -0
- package/kiso/docs/components/card.md +154 -0
- package/kiso/docs/components/checkbox.md +91 -0
- package/kiso/docs/components/command-palette.md +165 -0
- package/kiso/docs/components/drawer.md +79 -0
- package/kiso/docs/components/dropdown-menu.md +178 -0
- package/kiso/docs/components/empty-state.md +142 -0
- package/kiso/docs/components/form-field.md +115 -0
- package/kiso/docs/components/header.md +79 -0
- package/kiso/docs/components/helper-text.md +86 -0
- package/kiso/docs/components/icon-button.md +161 -0
- package/kiso/docs/components/input.md +99 -0
- package/kiso/docs/components/label.md +88 -0
- package/kiso/docs/components/link.md +152 -0
- package/kiso/docs/components/modal-dialog.md +82 -0
- package/kiso/docs/components/navigation.md +68 -0
- package/kiso/docs/components/page-header.md +70 -0
- package/kiso/docs/components/pagination.md +129 -0
- package/kiso/docs/components/popover.md +74 -0
- package/kiso/docs/components/search.md +147 -0
- package/kiso/docs/components/select.md +105 -0
- package/kiso/docs/components/sidebar.md +74 -0
- package/kiso/docs/components/skeleton.md +140 -0
- package/kiso/docs/components/spinner.md +125 -0
- package/kiso/docs/components/switch.md +92 -0
- package/kiso/docs/components/table.md +255 -0
- package/kiso/docs/components/tabs.md +69 -0
- package/kiso/docs/components/textarea.md +91 -0
- package/kiso/docs/components/toast.md +80 -0
- package/kiso/docs/components/tooltip.md +162 -0
- package/kiso/docs/components/validation-message.md +96 -0
- package/kiso/docs/data-interfaces.md +309 -0
- package/kiso/docs/evolution.md +35 -0
- package/kiso/docs/patterns/README.md +40 -0
- package/kiso/docs/patterns/application-shell.md +106 -0
- package/kiso/docs/patterns/command-palette.md +142 -0
- package/kiso/docs/patterns/confirmations.md +158 -0
- package/kiso/docs/patterns/crud.md +139 -0
- package/kiso/docs/patterns/dashboard.md +102 -0
- package/kiso/docs/patterns/destructive-actions.md +137 -0
- package/kiso/docs/patterns/developer-oriented-interfaces.md +162 -0
- package/kiso/docs/patterns/empty-states.md +76 -0
- package/kiso/docs/patterns/errors.md +93 -0
- package/kiso/docs/patterns/filtering.md +147 -0
- package/kiso/docs/patterns/keyboard-shortcuts.md +155 -0
- package/kiso/docs/patterns/large-data-tables.md +182 -0
- package/kiso/docs/patterns/list-detail.md +118 -0
- package/kiso/docs/patterns/loading.md +80 -0
- package/kiso/docs/patterns/login-authentication.md +101 -0
- package/kiso/docs/patterns/onboarding.md +94 -0
- package/kiso/docs/patterns/pagination.md +121 -0
- package/kiso/docs/patterns/permission-denied.md +84 -0
- package/kiso/docs/patterns/search.md +150 -0
- package/kiso/docs/patterns/settings.md +100 -0
- package/kiso/docs/patterns/sorting.md +121 -0
- package/kiso/docs/principles.md +122 -0
- package/kiso/docs/tokens.md +95 -0
- package/kiso/docs/voice-and-tone.md +154 -0
- package/package.json +42 -0
- package/tokens/build/tokens.css +143 -0
- package/tokens/build/tokens.d.ts +160 -0
- package/tokens/build/tokens.json +88 -0
- 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.
|