@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,74 @@
|
|
|
1
|
+
# Sidebar
|
|
2
|
+
|
|
3
|
+
A persistent navigation region for products with several destinations or
|
|
4
|
+
collapsible groups.
|
|
5
|
+
|
|
6
|
+
## Purpose
|
|
7
|
+
|
|
8
|
+
Sidebar makes a broad product structure scannable without turning the Header
|
|
9
|
+
into a menu bar. It is a layout container whose items remain Links; disclosure
|
|
10
|
+
controls only expand or collapse sections.
|
|
11
|
+
|
|
12
|
+
## Anatomy
|
|
13
|
+
|
|
14
|
+
```
|
|
15
|
+
Sidebar
|
|
16
|
+
├── label (accessible, may be visually hidden)
|
|
17
|
+
├── Navigation
|
|
18
|
+
│ ├── Link(s)
|
|
19
|
+
│ └── section(s)
|
|
20
|
+
│ ├── disclosure Button
|
|
21
|
+
│ └── Link(s)
|
|
22
|
+
└── collapse control (optional IconButton)
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
Surface uses `--color-surface`, divider `--color-border`, primary text
|
|
26
|
+
`--color-foreground`, and current items `--color-primary`. Layout uses
|
|
27
|
+
`--spacing-sm` between items, `--spacing-md` section gaps, and the five
|
|
28
|
+
property-qualified label typography tokens.
|
|
29
|
+
|
|
30
|
+
## Variants
|
|
31
|
+
|
|
32
|
+
No visual variants. Sidebar has one navigation treatment; expanded and
|
|
33
|
+
collapsed are states of the same component, not separate variants.
|
|
34
|
+
|
|
35
|
+
## Sizes
|
|
36
|
+
|
|
37
|
+
No `sm` / `md` / `lg` sizes. Width belongs to the host layout, and internal
|
|
38
|
+
spacing uses semantic tokens without changing Link or disclosure-control sizes.
|
|
39
|
+
|
|
40
|
+
## States
|
|
41
|
+
|
|
42
|
+
| State | Behavior |
|
|
43
|
+
| --- | --- |
|
|
44
|
+
| default | Expanded groups and their current state are visible. |
|
|
45
|
+
| hover | Interactive children own hover feedback. |
|
|
46
|
+
| focus | Links and disclosure controls show `--color-focus`. |
|
|
47
|
+
| active | Current destination has `aria-current="page"`; expanded disclosures have `aria-expanded="true"`. |
|
|
48
|
+
| disabled | Sidebar is not disabled; omit unavailable destinations or explain them adjacent to a disabled child. |
|
|
49
|
+
| collapsed | Sections reduce to an explicit compact navigation; essential labels must remain available without Tooltip. |
|
|
50
|
+
|
|
51
|
+
## Accessibility
|
|
52
|
+
|
|
53
|
+
Wrap the links in a labelled `<nav>`. Use real Buttons with `aria-expanded`
|
|
54
|
+
and `aria-controls` for section disclosures. Collapsing the whole Sidebar must
|
|
55
|
+
not strand focus in hidden content; move it to the collapse control. Keep DOM
|
|
56
|
+
and visual order aligned. `Tab` moves among controls; `Enter`/`Space` toggles a
|
|
57
|
+
focused disclosure; Links retain native behavior.
|
|
58
|
+
|
|
59
|
+
## When to use
|
|
60
|
+
|
|
61
|
+
- Products with several stable top-level destinations or grouped sections.
|
|
62
|
+
- Dense technical tools where persistent wayfinding reduces context switching.
|
|
63
|
+
|
|
64
|
+
## When NOT to use
|
|
65
|
+
|
|
66
|
+
- A handful of global destinations that fit in [Header](header.md).
|
|
67
|
+
- A hierarchical path to the current page; use [Breadcrumb](breadcrumb.md).
|
|
68
|
+
- Temporary controls unrelated to navigation.
|
|
69
|
+
|
|
70
|
+
## Radix/shadcn mapping
|
|
71
|
+
|
|
72
|
+
No Radix Sidebar primitive. shadcn Sidebar is a structural reference, but Kiso
|
|
73
|
+
keeps native nav/Link semantics and semantic tokens. Disclosure behavior may
|
|
74
|
+
use Radix Collapsible / shadcn Collapsible.
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
# Skeleton
|
|
2
|
+
|
|
3
|
+
A layout-preserving loading placeholder. It occupies the space of the
|
|
4
|
+
content that will arrive, so the page does not jump.
|
|
5
|
+
|
|
6
|
+
## Purpose
|
|
7
|
+
|
|
8
|
+
Skeleton is for *known* structure and *unknown* data: the table will have
|
|
9
|
+
rows, the Card will have a title and two lines, the form will have four
|
|
10
|
+
fields. Draw that shape now; swap in the real content when it exists.
|
|
11
|
+
|
|
12
|
+
User story #22: [Spinner](spinner.md) is indeterminate; Skeleton preserves
|
|
13
|
+
layout.
|
|
14
|
+
|
|
15
|
+
Table/DataTable (data slice) composes Skeleton for loading rows, together
|
|
16
|
+
with [EmptyState](empty-state.md) (no data) and [Pagination](pagination.md)
|
|
17
|
+
(known data). This doc defines the
|
|
18
|
+
placeholder; it does not define the table.
|
|
19
|
+
|
|
20
|
+
## Anatomy
|
|
21
|
+
|
|
22
|
+
```
|
|
23
|
+
Skeleton (one placeholder)
|
|
24
|
+
└── shape (text line | block | circle)
|
|
25
|
+
|
|
26
|
+
Region (the thing that is loading)
|
|
27
|
+
├── aria-busy="true"
|
|
28
|
+
├── accessible name / live status ("Loading replicas")
|
|
29
|
+
└── one or more Skeletons matching the eventual layout
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
- **Shape.** Geometric stand-in. No letters, no shimmering brand mark, no
|
|
33
|
+
fake data.
|
|
34
|
+
- **Region.** The parent is the accessible loading state. Individual
|
|
35
|
+
Skeletons are decorative.
|
|
36
|
+
|
|
37
|
+
## Variants
|
|
38
|
+
|
|
39
|
+
Variants are shapes, not colors.
|
|
40
|
+
|
|
41
|
+
| Variant | Shape tokens | Stands in for |
|
|
42
|
+
| --- | --- | --- |
|
|
43
|
+
| `text` | Height of the target role's `--type-role-body-line-height`, `--type-role-label-line-height`, or `--type-role-heading-line-height`; width a fraction of the column; radius `--radius-sm` | Titles, labels, table cells, descriptions. |
|
|
44
|
+
| `block` | Radius `--radius-md` (or `--radius-lg` when replacing a Card) | Cards, images, chart frames, table bodies as a whole. |
|
|
45
|
+
| `circle` | Radius `--radius-full`, equal width and height | Avatars and circular IconButtons. |
|
|
46
|
+
|
|
47
|
+
Fill is `--color-border` on the surrounding `--color-surface` or
|
|
48
|
+
`--color-background` (whichever the real content sits on). Do not use
|
|
49
|
+
`--color-primary` or status colors — loading is not a status.
|
|
50
|
+
|
|
51
|
+
Pulse (if any) interpolates opacity between the fill and `--color-surface`
|
|
52
|
+
using `--motion-duration-normal` and `--motion-easing-standard`.
|
|
53
|
+
|
|
54
|
+
**Reduced motion:** no pulse. Static `--color-border` placeholders. The
|
|
55
|
+
tokens already zero out `--motion-duration-fast` and
|
|
56
|
+
`--motion-duration-normal`; do not add a second
|
|
57
|
+
animation that ignores them.
|
|
58
|
+
|
|
59
|
+
## Sizes
|
|
60
|
+
|
|
61
|
+
Skeleton has no independent size scale. It **matches the content it
|
|
62
|
+
replaces**:
|
|
63
|
+
|
|
64
|
+
| Replacing | Skeleton |
|
|
65
|
+
| --- | --- |
|
|
66
|
+
| Body line | `text` at `--type-role-body-line-height`, width ~ ⅔ of the column |
|
|
67
|
+
| Label / heading | `text` at that role's height, shorter width |
|
|
68
|
+
| Button | `block` with the Button size's padding box |
|
|
69
|
+
| Table row | A row of `text` cells aligned to column widths |
|
|
70
|
+
| Card | Header `text` + Content `block` or stacked `text` |
|
|
71
|
+
|
|
72
|
+
Approximate widths are layout choices, not new tokens. Do not specify
|
|
73
|
+
placeholder geometry in raw pixels; size against type roles and spacing.
|
|
74
|
+
|
|
75
|
+
## States
|
|
76
|
+
|
|
77
|
+
| State | Behavior |
|
|
78
|
+
| --- | --- |
|
|
79
|
+
| default | Visible placeholder while `aria-busy` is true. |
|
|
80
|
+
| hover / focus / active | N/A. Skeletons are not controls and must not be in the tab order. |
|
|
81
|
+
| disabled | N/A. |
|
|
82
|
+
| loading | Skeleton *is* the loading presentation. |
|
|
83
|
+
| error | Remove Skeletons; show [Alert](alert.md) (and optionally a retry Button). Do not leave placeholders up after failure. |
|
|
84
|
+
|
|
85
|
+
When data arrives, replace the Skeleton region with the real content in
|
|
86
|
+
one swap. Do not leave a Skeleton sitting next to the loaded widget.
|
|
87
|
+
|
|
88
|
+
## Accessibility
|
|
89
|
+
|
|
90
|
+
- Each Skeleton graphic is `aria-hidden="true"`.
|
|
91
|
+
- The **region** communicates loading: `aria-busy="true"` and a polite
|
|
92
|
+
status (`role="status"` or `aria-live="polite"`) with a short label
|
|
93
|
+
("Loading queries"). Announce once, not once per row.
|
|
94
|
+
- Do not put focus inside the placeholder. Focus stays on the control that
|
|
95
|
+
triggered the load, or on the page heading for initial page load.
|
|
96
|
+
- When loading finishes, set `aria-busy="false"` and let the status
|
|
97
|
+
announce completion only if the person would otherwise miss it (e.g.
|
|
98
|
+
the region was empty). Prefer the content itself over a "Done loading"
|
|
99
|
+
toast.
|
|
100
|
+
- Skeleton is not a progressbar.
|
|
101
|
+
|
|
102
|
+
### Keyboard
|
|
103
|
+
|
|
104
|
+
No keymap. Placeholders are skipped.
|
|
105
|
+
|
|
106
|
+
## When to use
|
|
107
|
+
|
|
108
|
+
- First load of a list, table, Card, or form whose structure is known.
|
|
109
|
+
- Pagination and refetch of Table/DataTable rows (compose Skeleton rows
|
|
110
|
+
in place of data rows; keep table chrome).
|
|
111
|
+
- Replacing a known block inside a Card while the rest of the page stays
|
|
112
|
+
put.
|
|
113
|
+
|
|
114
|
+
## When NOT to use
|
|
115
|
+
|
|
116
|
+
- **Indeterminate, structure-unknown waits** (submit, connect, a one-off
|
|
117
|
+
action). [Spinner](spinner.md), usually inside the Button.
|
|
118
|
+
- **Empty results.** That is EmptyState, not an eternal Skeleton.
|
|
119
|
+
- **Errors.** Alert, not a grey box.
|
|
120
|
+
- **Content that is already on screen.** Do not skeletonize a field the
|
|
121
|
+
person is editing.
|
|
122
|
+
- **Fake completed UI.** Never put real-looking numbers or names in a
|
|
123
|
+
Skeleton; placeholders stay empty shapes.
|
|
124
|
+
|
|
125
|
+
## Radix/shadcn mapping
|
|
126
|
+
|
|
127
|
+
No Radix Skeleton primitive.
|
|
128
|
+
|
|
129
|
+
| Kiso | Reference |
|
|
130
|
+
| --- | --- |
|
|
131
|
+
| Placeholder primitive | shadcn [Skeleton](https://ui.shadcn.com/docs/components/skeleton) |
|
|
132
|
+
| Table rows | shadcn Skeleton "Table" example — restyle to `--color-border` / `--color-surface` and type-role heights |
|
|
133
|
+
| Card / text / avatar | shadcn Card / Text / Avatar examples as shape references only |
|
|
134
|
+
|
|
135
|
+
Do not copy shadcn examples that set raw pixel widths. Map height to type
|
|
136
|
+
roles and padding to one of `--spacing-xs`, `--spacing-sm`, `--spacing-md`,
|
|
137
|
+
`--spacing-lg`, or `--spacing-xl`, matching the content being replaced.
|
|
138
|
+
|
|
139
|
+
Compose with Table/DataTable in the data slice: loading → Skeleton rows;
|
|
140
|
+
empty → EmptyState; error → Alert; populated → rows + Pagination.
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
# Spinner
|
|
2
|
+
|
|
3
|
+
An indeterminate loading indicator. It tells the person that work is
|
|
4
|
+
happening when the shape or duration of the result is not yet known.
|
|
5
|
+
|
|
6
|
+
## Purpose
|
|
7
|
+
|
|
8
|
+
Spinner is for *unknown* progress: a request has started, there is nothing
|
|
9
|
+
honest to draw yet, and the person must wait.
|
|
10
|
+
|
|
11
|
+
User story #22: Spinner is indeterminate; [Skeleton](skeleton.md) is
|
|
12
|
+
layout-preserving. If you already know the layout of what will appear
|
|
13
|
+
(table rows, a Card body, a form), use Skeleton. If you do not (a
|
|
14
|
+
submission, a reconnect, a short inline wait inside a Button), use Spinner.
|
|
15
|
+
|
|
16
|
+
## Anatomy
|
|
17
|
+
|
|
18
|
+
```
|
|
19
|
+
Spinner
|
|
20
|
+
├── graphic (required)
|
|
21
|
+
└── label (required in accessible name; visible when the wait is the
|
|
22
|
+
primary thing on screen)
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
- **Graphic.** A circular (or otherwise looping) indicator. Color
|
|
26
|
+
`--color-primary` on a track of `--color-border`. It is decorative once
|
|
27
|
+
the name exists: the accessible name carries "Loading".
|
|
28
|
+
- **Label.** Visible text when Spinner is the main content of a region:
|
|
29
|
+
"Loading queries…", "Connecting to database…". Follow
|
|
30
|
+
[voice-and-tone](../voice-and-tone.md): state what is happening, no
|
|
31
|
+
chatter. When Spinner is inside a Button, the Button label is the name
|
|
32
|
+
("Saving…") and the graphic has `aria-hidden="true"`.
|
|
33
|
+
|
|
34
|
+
## Variants
|
|
35
|
+
|
|
36
|
+
One visual variant. Meaning comes from placement, not color.
|
|
37
|
+
|
|
38
|
+
| Placement | Label |
|
|
39
|
+
| --- | --- |
|
|
40
|
+
| Inside [Button](button.md) / [IconButton](icon-button.md) | Button keeps a loading verb; graphic is decorative. |
|
|
41
|
+
| Inline next to a value or [Badge](badge.md) | Visible short word ("syncing") or `aria-label` on the Spinner. |
|
|
42
|
+
| Region / page | Visible label required. Centered in the region, not over unrelated chrome. |
|
|
43
|
+
|
|
44
|
+
Do not recolor Spinner to `--color-danger` to mean failed — a failed wait
|
|
45
|
+
is an [Alert](alert.md). Do not use `--color-success` to mean done — hide
|
|
46
|
+
the Spinner.
|
|
47
|
+
|
|
48
|
+
## Sizes
|
|
49
|
+
|
|
50
|
+
| Size | Scale | Use |
|
|
51
|
+
| --- | --- | --- |
|
|
52
|
+
| `sm` | Matches `--type-role-metadata-font-size` | Inside Button `sm`, Badge, inline meta. |
|
|
53
|
+
| `md` (default) | Matches `--type-role-label-font-size` | Inside Button `md`, IconButton `md`, inline waits. |
|
|
54
|
+
| `lg` | Matches `--type-role-body-font-size` | Region-level wait when layout is unknown. |
|
|
55
|
+
|
|
56
|
+
## States
|
|
57
|
+
|
|
58
|
+
| State | Behavior |
|
|
59
|
+
| --- | --- |
|
|
60
|
+
| default | Animating (unless reduced motion). |
|
|
61
|
+
| hover / focus / active | N/A. Spinner is not a control. |
|
|
62
|
+
| disabled | N/A. |
|
|
63
|
+
| loading | Spinner *is* the loading state of something else. It has no nested loading state. |
|
|
64
|
+
| error | Replace Spinner with [Alert](alert.md). Do not freeze a Spinner as an error cue. |
|
|
65
|
+
|
|
66
|
+
Motion uses `--motion-duration-normal` and `--motion-easing-standard` for
|
|
67
|
+
fade-in. The spin loop itself is continuous.
|
|
68
|
+
|
|
69
|
+
**Reduced motion:** the generated tokens set `--motion-duration-fast` and
|
|
70
|
+
`--motion-duration-normal` to `0s`
|
|
71
|
+
under `prefers-reduced-motion: reduce`. A spinning loop with duration zero
|
|
72
|
+
is invisible or broken. In that case show a **static** indicator (the same
|
|
73
|
+
graphic, not rotating) plus the label. Never communicate loading only with
|
|
74
|
+
motion.
|
|
75
|
+
|
|
76
|
+
## Accessibility
|
|
77
|
+
|
|
78
|
+
- When the Spinner is the only loading cue in a region: `role="status"`,
|
|
79
|
+
`aria-live="polite"`, `aria-label` (or visible text) that says what is
|
|
80
|
+
loading. `aria-busy="true"` on the region that is waiting.
|
|
81
|
+
- When composed inside a busy Button: the Button has `aria-busy="true"`;
|
|
82
|
+
the graphic is `aria-hidden="true"` so "Loading" is not announced twice.
|
|
83
|
+
- Do not use `role="progressbar"` unless you have a real value. Spinner is
|
|
84
|
+
indeterminate; a progress bar with no `aria-valuenow` is the wrong
|
|
85
|
+
promise.
|
|
86
|
+
- Color is not the only cue; the label (visible or `aria-label`) is.
|
|
87
|
+
|
|
88
|
+
### Keyboard
|
|
89
|
+
|
|
90
|
+
No keymap. Focus stays on the control that started the wait (the Button),
|
|
91
|
+
or in the region if the whole view is replacing. Do not move focus to the
|
|
92
|
+
Spinner graphic.
|
|
93
|
+
|
|
94
|
+
## When to use
|
|
95
|
+
|
|
96
|
+
- A short, indeterminate wait: submitting a form, retrying a connection,
|
|
97
|
+
refreshing one value.
|
|
98
|
+
- Inside a Button or IconButton loading state (user story #12).
|
|
99
|
+
- A region whose forthcoming layout is genuinely unknown (first paint of a
|
|
100
|
+
custom view with no stable structure).
|
|
101
|
+
|
|
102
|
+
## When NOT to use
|
|
103
|
+
|
|
104
|
+
- **The layout is known.** [Skeleton](skeleton.md) — especially table rows,
|
|
105
|
+
Card bodies, and form stacks. Table/DataTable (data slice) composes
|
|
106
|
+
Skeleton for loading rows, not a Spinner over an empty table.
|
|
107
|
+
- **Progress is measurable** (percent, step n of m). Use a determinate
|
|
108
|
+
progress pattern, not Spinner.
|
|
109
|
+
- **The wait is done or failed.** Hide Spinner; show content or Alert.
|
|
110
|
+
- **Decoration.** A never-ending Spinner next to idle content is a lie.
|
|
111
|
+
- **Blocking the whole app by default.** A page-level Spinner is a last
|
|
112
|
+
resort when nothing else can render. Prefer Skeleton of the shell.
|
|
113
|
+
|
|
114
|
+
## Radix/shadcn mapping
|
|
115
|
+
|
|
116
|
+
No Radix Spinner primitive.
|
|
117
|
+
|
|
118
|
+
| Kiso | Reference |
|
|
119
|
+
| --- | --- |
|
|
120
|
+
| Graphic + `role="status"` + `aria-label="Loading"` | shadcn [Spinner](https://ui.shadcn.com/docs/components/spinner) |
|
|
121
|
+
| Inside Button | shadcn Button "Spinner" example, composed with Kiso Button loading rules |
|
|
122
|
+
| Size | shadcn size utilities restyled to the type-role matching sizes above, not arbitrary `size-*` pixels |
|
|
123
|
+
|
|
124
|
+
Replace shadcn's default icon color with `--color-primary`. Honor reduced
|
|
125
|
+
motion as specified above; do not rely on `animate-spin` alone.
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
# Switch
|
|
2
|
+
|
|
3
|
+
## Purpose
|
|
4
|
+
|
|
5
|
+
Switch changes one boolean setting between on and off. The change normally
|
|
6
|
+
takes effect immediately, like a physical switch, rather than waiting for form
|
|
7
|
+
submission.
|
|
8
|
+
|
|
9
|
+
## Anatomy
|
|
10
|
+
|
|
11
|
+
1. **Root/track** — focusable control and on/off state owner.
|
|
12
|
+
2. **Thumb** — moves to make state visible; motion is supportive, not the only
|
|
13
|
+
signal.
|
|
14
|
+
3. **Label** — names the setting, not the action (for example, “Query logging,”
|
|
15
|
+
not “Enable query logging”).
|
|
16
|
+
4. **Description (optional)** — explains effect or scope.
|
|
17
|
+
|
|
18
|
+
## Variants
|
|
19
|
+
|
|
20
|
+
- **Off / on** — the two stable setting values.
|
|
21
|
+
- **With description** — for settings whose effect is not obvious from Label.
|
|
22
|
+
- **Controlled** — application owns state, including persistence and rollback.
|
|
23
|
+
- **Uncontrolled** — primitive owns initial state; use only when persistence and
|
|
24
|
+
external synchronization are unnecessary.
|
|
25
|
+
|
|
26
|
+
Switch is one immediate boolean setting. Checkbox is an independent form choice
|
|
27
|
+
or member of a multi-select list; Select chooses one value from many.
|
|
28
|
+
|
|
29
|
+
## Sizes
|
|
30
|
+
|
|
31
|
+
- **Small** — dense settings tables, with a full-sized Label target.
|
|
32
|
+
- **Medium** — default.
|
|
33
|
+
|
|
34
|
+
Track, thumb, and gap scale as a unit with semantic tokens. Do not encode state
|
|
35
|
+
only through thumb position or color; the accessible state remains required.
|
|
36
|
+
|
|
37
|
+
## States
|
|
38
|
+
|
|
39
|
+
| State | Behavior |
|
|
40
|
+
| --- | --- |
|
|
41
|
+
| Default | Clearly presents off or on state. |
|
|
42
|
+
| Hover | Track/label pair receives quiet interactive emphasis. |
|
|
43
|
+
| Focus | Root has a visible `--color-focus` ring. |
|
|
44
|
+
| Active | Brief pressed feedback; thumb movement uses `--motion-duration-fast` and `--motion-easing-standard`. |
|
|
45
|
+
| Disabled | Cannot toggle and uses `--color-disabled`. |
|
|
46
|
+
| Loading | Retains the intended or confirmed state, shows pending status, and prevents duplicate changes only when necessary. |
|
|
47
|
+
| Error | Persistence failure is explained with `--color-danger` feedback and a recovery action; do not leave the displayed state ambiguous. |
|
|
48
|
+
|
|
49
|
+
Loading differs from disabled: it communicates an in-progress state change.
|
|
50
|
+
If the request fails, either revert to the confirmed state or retain the choice
|
|
51
|
+
with an explicit retry path.
|
|
52
|
+
|
|
53
|
+
## Accessibility
|
|
54
|
+
|
|
55
|
+
- Radix supplies `role="switch"` and `aria-checked`; preserve these semantics.
|
|
56
|
+
- Associate Label using matching `for`/`id` or a single, unambiguous
|
|
57
|
+
`aria-labelledby` relationship.
|
|
58
|
+
- Expose disabled and busy status. Link description or failure feedback via
|
|
59
|
+
`aria-describedby`.
|
|
60
|
+
- Keyboard: `Tab` focuses; `Space` toggles. `Enter` may toggle where the
|
|
61
|
+
primitive/browser contract supports it consistently. Do not require drag.
|
|
62
|
+
- State must be announced as on/off and remain discernible without color or
|
|
63
|
+
animation. Honor reduced-motion tokens.
|
|
64
|
+
|
|
65
|
+
## When to use
|
|
66
|
+
|
|
67
|
+
- For a single preference that applies immediately.
|
|
68
|
+
- In settings surfaces where current on/off state must remain visible.
|
|
69
|
+
- When toggling does not require a separate Save action.
|
|
70
|
+
|
|
71
|
+
## When NOT to use
|
|
72
|
+
|
|
73
|
+
- Do not use for a value submitted only with the rest of a form; use Checkbox.
|
|
74
|
+
- Do not use for multiple related selections; use a Checkbox group.
|
|
75
|
+
- Do not use for choosing among three or more values; use Select.
|
|
76
|
+
- Do not use when changing state has a destructive or complex consequence that
|
|
77
|
+
needs explicit confirmation; use a clearly named action flow.
|
|
78
|
+
|
|
79
|
+
## Tokens
|
|
80
|
+
|
|
81
|
+
Use `--color-border`, `--color-surface`, `--color-foreground`,
|
|
82
|
+
`--color-primary`, `--color-focus`, `--color-disabled`, and `--color-danger`,
|
|
83
|
+
plus `--spacing-sm` label gap, `--radius-full`, the five property-qualified
|
|
84
|
+
label typography tokens, `--motion-duration-fast`, and
|
|
85
|
+
`--motion-easing-standard`.
|
|
86
|
+
|
|
87
|
+
## Radix/shadcn mapping
|
|
88
|
+
|
|
89
|
+
Maps to [Radix Switch](https://www.radix-ui.com/primitives/docs/components/switch)
|
|
90
|
+
and [shadcn/ui Switch](https://ui.shadcn.com/docs/components/switch). Keep
|
|
91
|
+
Radix's switch role, checked state, form behavior, and keyboard interaction as
|
|
92
|
+
the behavioral reference.
|