@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,135 @@
1
+ # Badge
2
+
3
+ A small inline status label. Not a button, not a card, not a count of
4
+ notifications by itself.
5
+
6
+ ## Purpose
7
+
8
+ Badge names a state or classification next to something else: "New",
9
+ "Beta", "live", "primary", "read replica". It is read, not clicked.
10
+
11
+ User story #18: Badge is for status labels; [Card](card.md) is for grouping
12
+ related content.
13
+
14
+ If the person must act on the status, pair the Badge with a
15
+ [Button](button.md) / [Link](link.md). Do not make the Badge the control.
16
+
17
+ ## Anatomy
18
+
19
+ ```
20
+ Badge
21
+ ├── leading icon or dot (optional)
22
+ ├── label (required)
23
+ └── trailing icon (optional; rarely needed)
24
+ ```
25
+
26
+ - **Label.** One or two words. Lowercase product nouns are fine ("live",
27
+ "beta"); proper names keep their form. Follow
28
+ [voice-and-tone](../voice-and-tone.md): direct, no filler.
29
+ - **Dot / icon.** Optional severity or category cue. Decorative
30
+ (`aria-hidden="true"`) when the label already says the state. If the Badge
31
+ is icon-only (avoid this), it needs `aria-label`.
32
+ - **No dismiss control.** A dismissible status is an [Alert](alert.md) or a
33
+ filter chip (not in this slice). Badge is not closable.
34
+
35
+ ## Variants
36
+
37
+ Variants encode *meaning*, using status and foreground roles. Do not invent
38
+ ad-hoc colors.
39
+
40
+ | Variant | Meaning | Tokens |
41
+ | --- | --- | --- |
42
+ | `neutral` (default) | Classification without severity: "Beta", "read", "v2". | Text `--color-foreground`, background `--color-surface`, border `--color-border`. |
43
+ | `info` | Informational state: "New", "preview". | Text `--color-info`, border `--color-info`, background `--color-surface`. |
44
+ | `success` | Healthy / complete: "live", "connected", "healthy". | Text `--color-success`, border `--color-success`, background `--color-surface`. |
45
+ | `warning` | Needs attention: "degraded", "stale". | Text `--color-warning`, border `--color-warning`, background `--color-surface`. |
46
+ | `danger` | Failed / blocked: "down", "unhealthy". | Text `--color-danger`, border `--color-danger`, background `--color-surface`. |
47
+
48
+ Fill stays `--color-surface` so the status color is the text and border.
49
+ That keeps contrast on the AA gate (status roles are checked against
50
+ `surface`). Do not paint the whole Badge `--color-danger` and then guess a
51
+ foreground.
52
+
53
+ Do not add a `primary` Badge to shout marketing emphasis. If the thing is
54
+ the main object, that is typography and hierarchy, not a Badge.
55
+
56
+ An optional `outline` look is `neutral` with transparent background — still
57
+ `--color-border`, not a new variant.
58
+
59
+ ## Sizes
60
+
61
+ | Size | Type role | Padding | Radius |
62
+ | --- | --- | --- | --- |
63
+ | `sm` (default) | Metadata properties (`--type-role-metadata-font-family`, `--type-role-metadata-font-size`, `--type-role-metadata-font-weight`, `--type-role-metadata-letter-spacing`, `--type-role-metadata-line-height`) | `--spacing-xs` | `--radius-sm` |
64
+ | `md` | Label properties (`--type-role-label-font-family`, `--type-role-label-font-size`, `--type-role-label-font-weight`, `--type-role-label-letter-spacing`, `--type-role-label-line-height`) | `--spacing-xs` block, `--spacing-sm` inline | `--radius-sm` |
65
+
66
+ Do not use `--radius-full` (pill). Badges are compact labels, not tags in
67
+ a marketing cluster.
68
+
69
+ ## States
70
+
71
+ Badge is not interactive by default.
72
+
73
+ | State | Behavior |
74
+ | --- | --- |
75
+ | default | Static label. |
76
+ | hover / focus / active | None, unless the Badge is wrapped by a Link (see below). |
77
+ | disabled | N/A. Hide a Badge that no longer applies; do not grey it. |
78
+ | loading | Optional: replace the label with a tiny [Spinner](spinner.md) plus a word ("syncing"). Prefer Skeleton on the parent region if the whole status is unknown. |
79
+ | error | Use `danger` as the *meaning*, not a separate error state. |
80
+
81
+ If a Badge is a Link (filter that navigates to a tagged view), the Link is
82
+ the interactive element: Badge provides appearance only. Focus ring
83
+ `--color-focus` on the Link. Do not put `onClick` on a `span` Badge.
84
+
85
+ ## Accessibility
86
+
87
+ - Default element: `<span>`. No `role="status"` on every Badge — that would
88
+ shout every "Beta" as a live region. The surrounding content already
89
+ includes the object; the Badge is extra words in that name.
90
+ - If the Badge is the only indication of a *changing* live state that the
91
+ person must notice (connection dropped), the *region* should update via
92
+ [Alert](alert.md) or `aria-live`, not a silent Badge swap.
93
+ - Color is not the only cue: the label text carries the meaning. A red
94
+ empty Badge is a fail.
95
+ - Icon-only: `aria-label` required. Prefer text.
96
+
97
+ ### Keyboard
98
+
99
+ No keymap. A Badge that is a Link follows [Link](link.md) keyboard rules.
100
+
101
+ ## When to use
102
+
103
+ - A short status or classification next to a title, table cell, or Card
104
+ title: "New", "Beta", "live", "primary".
105
+ - Several orthogonal labels ("live" + "read replica") — each is its own
106
+ Badge, not one concatenated string.
107
+
108
+ ## When NOT to use
109
+
110
+ - **Grouping content.** [Card](card.md).
111
+ - **In-page warnings with explanation and recovery.** [Alert](alert.md).
112
+ - **Actions.** Buttons, IconButtons, and Links. A "Delete" Badge is a
113
+ mistake.
114
+ - **Counts, notifications, or numeric attention dots.** If Kiso needs a
115
+ notification count later, that is not this component. A numeric "3" Badge
116
+ on an icon is chrome, not a status label.
117
+ - **Section headings or filters that look like a pile of pills.** Filters
118
+ are Controls; they belong with Input/Select or a later pattern.
119
+ - **Long sentences.** If it needs punctuation, it is copy, not a Badge.
120
+
121
+ ## Radix/shadcn mapping
122
+
123
+ No Radix Badge primitive. Visual reference: shadcn
124
+ [Badge](https://ui.shadcn.com/docs/components/badge).
125
+
126
+ | Kiso | shadcn |
127
+ | --- | --- |
128
+ | `neutral` | `secondary` or `outline` restyled to `--color-surface` / `--color-border` / `--color-foreground` |
129
+ | `info` / `success` / `warning` / `danger` | Do not use arbitrary `bg-green-*` utilities from the shadcn "Custom Colors" example. Map respectively to `--color-info`, `--color-success`, `--color-warning`, or `--color-danger`. |
130
+ | shadcn `destructive` | Closest to Kiso `danger` |
131
+ | shadcn `default` (accent fill) | Do not use as a status; it competes with primary actions |
132
+ | shadcn `variant="link"` | Do not use; navigation is [Link](link.md) |
133
+
134
+ If the Badge navigates, compose shadcn Badge visuals on an `<a>` the same
135
+ way Button-look Links do — do not use a Button.
@@ -0,0 +1,66 @@
1
+ # Breadcrumb
2
+
3
+ Shows the current page's position in a hierarchy and links to its ancestors.
4
+
5
+ ## Purpose
6
+
7
+ Breadcrumb answers “where am I?” across nested routes. Unlike [Tabs](tabs.md),
8
+ it does not switch peer content within the current page.
9
+
10
+ ## Anatomy
11
+
12
+ ```
13
+ Breadcrumb navigation
14
+ └── ordered list
15
+ ├── ancestor Link
16
+ ├── separator (decorative)
17
+ └── current page (text)
18
+ ```
19
+
20
+ Use `--color-muted-foreground` for ancestors, `--color-foreground` for the
21
+ current page, `--color-primary` for Link interaction, and `--color-focus` for
22
+ focus. Separators use the muted role and are hidden from assistive technology.
23
+
24
+ ## Variants
25
+
26
+ No visual variants. Breadcrumb always represents one hierarchy; overflow is a
27
+ state that collapses middle ancestors, not a separate variant.
28
+
29
+ ## Sizes
30
+
31
+ One size. Use the standard body/link treatment and semantic spacing; do not
32
+ create compact or large breadcrumb scales.
33
+
34
+ ## States
35
+
36
+ | State | Behavior |
37
+ | --- | --- |
38
+ | default | Ancestors are Links; the current page is plain text. |
39
+ | hover | Ancestor Link shows its Link hover treatment. |
40
+ | focus | Focused ancestor shows `--color-focus`. |
41
+ | active | Pressing an ancestor follows its URL. Current page carries `aria-current="page"`. |
42
+ | disabled | N/A. Do not render a disabled breadcrumb Link. |
43
+ | overflow | Collapse middle ancestors into an accessible menu; keep the root and current page visible. |
44
+
45
+ ## Accessibility
46
+
47
+ Use `<nav aria-label="Breadcrumb">` with an `<ol>`. Separators are CSS or
48
+ `aria-hidden="true"`. Do not make the current page a self-link. Keyboard
49
+ behavior is native Link/menu behavior; `Tab` visits Links, not separators.
50
+
51
+ ## When to use
52
+
53
+ - Routes at least two meaningful levels deep.
54
+ - Technical products where parent context is useful for returning upward.
55
+
56
+ ## When NOT to use
57
+
58
+ - Switching panels on one page; use Tabs.
59
+ - A flat product with no hierarchy.
60
+ - As a substitute for the page title or browser history.
61
+
62
+ ## Radix/shadcn mapping
63
+
64
+ Radix has no Breadcrumb primitive. Map structure to shadcn Breadcrumb while
65
+ preserving native `<nav>`, ordered-list, and Link semantics. Use DropdownMenu
66
+ for collapsed middle items.
@@ -0,0 +1,168 @@
1
+ # Button
2
+
3
+ Triggers an in-page action. It does not change the URL.
4
+
5
+ ## Purpose
6
+
7
+ Button is the control for *doing something*: submit a form, run a query, open
8
+ a dialog, save, delete. The person activates it; the application performs
9
+ behavior.
10
+
11
+ If the destination is a URL, use [Link](link.md). If the action is an icon
12
+ with no visible text, use [IconButton](icon-button.md).
13
+
14
+ ### Choose the right control
15
+
16
+ | Need | Control | Why |
17
+ | --- | --- | --- |
18
+ | Trigger behavior (submit, save, delete, open) | **Button** | Action; URL does not change. |
19
+ | Icon-only action (toolbar, compact chrome) | **[IconButton](icon-button.md)** | Same as Button, but the accessible name is not visible text. |
20
+ | Go to a URL (in-app or external) | **[Link](link.md)** | Navigation; must remain a real link (open in new tab, copy URL). |
21
+
22
+ A Button that looks like a link is still a Button and is the wrong control for
23
+ navigation. A Link that looks like a Button is still a Link: visual style does
24
+ not change the semantics.
25
+
26
+ A Button may include an icon *plus* a text label. That is still Button, not
27
+ IconButton.
28
+
29
+ ## Anatomy
30
+
31
+ ```
32
+ Button
33
+ ├── leading icon (optional)
34
+ ├── label (required visible text)
35
+ ├── trailing icon (optional)
36
+ └── Spinner (loading state only; replaces or precedes the label)
37
+ ```
38
+
39
+ - **Label.** Direct verb phrase. Follow
40
+ [voice-and-tone](../voice-and-tone.md): "Save query", "Delete replica",
41
+ "Run". Not "Click here", not "Submit" when a specific verb exists.
42
+ - **Icon.** Optional reinforcement of the label. Never the only name — that
43
+ is IconButton.
44
+ - **Spinner.** Only while the action is pending. See
45
+ [Spinner](spinner.md).
46
+
47
+ ## Variants
48
+
49
+ Four variants. Do not add a "link" variant; navigation is [Link](link.md).
50
+
51
+ | Variant | When | Tokens |
52
+ | --- | --- | --- |
53
+ | `default` | Secondary action on the current task. Most buttons. | Background `--color-surface`, text `--color-foreground`, border `--color-border`. Hover background `--color-elevated-surface`. |
54
+ | `primary` | The one action that advances the current task. At most one primary Button per region. | Background `--color-surface`, text and border `--color-primary`, label `--type-weight-semibold`. Hover background `--color-elevated-surface`. |
55
+ | `destructive` | Irreversible or destructive action (delete, drop, disconnect). User story #4. | Background `--color-surface`, text and border `--color-danger`. Hover background `--color-elevated-surface`. Match confirmation weight to stakes; see voice-and-tone. |
56
+ | `ghost` | Low-emphasis action in chrome, toolbars, or inside a Card. | Transparent background and border. Text `--color-foreground`. Hover background `--color-surface`. |
57
+
58
+ `primary` and `danger` are text roles (checked against surfaces in the AA
59
+ gate). `--color-background` is canvas and is never text, so these variants
60
+ are **not** filled inversions. A filled "on-primary" treatment would need a
61
+ new semantic role; do not invent one here and do not borrow the canvas
62
+ role as ink.
63
+
64
+ If a destructive action is not irreversible (archive, disable, hide), use
65
+ `default` or `ghost`, not `destructive`.
66
+
67
+ ## Sizes
68
+
69
+ | Size | Type role | Padding | Radius | Use |
70
+ | --- | --- | --- | --- | --- |
71
+ | `sm` | Label properties (`--type-role-label-font-family`, `--type-role-label-font-size`, `--type-role-label-font-weight`, `--type-role-label-letter-spacing`, `--type-role-label-line-height`) | `--spacing-xs` block, `--spacing-sm` inline | `--radius-sm` | Dense tables, Card footers, compact filters. |
72
+ | `md` (default) | Same label properties | `--spacing-sm` block, `--spacing-md` inline | `--radius-md` | Forms, page actions, dialogs. |
73
+ | `lg` | Same label properties | `--spacing-md` block, `--spacing-lg` inline | `--radius-md` | Rare; empty-state or onboarding primary actions. |
74
+
75
+ Do not invent a fourth size. Page-level calls to action still use `md` or
76
+ `lg`. PageHeader (navigation slice) composes Buttons; it is not a Button
77
+ size.
78
+
79
+ ## States
80
+
81
+ | State | Behavior | Tokens / notes |
82
+ | --- | --- | --- |
83
+ | default | Interactive. | Variant tokens above. |
84
+ | hover | Pointer over the control. | See variant hover. Cursor indicates affordance. Transition: `--motion-duration-fast` / `--motion-easing-standard`. |
85
+ | focus | Keyboard focus. | Visible ring using `--color-focus`. Never remove the ring without an equivalent. |
86
+ | active | Pointer down / activation. | Slightly pressed; keep the same semantic colors. |
87
+ | disabled | Interaction blocked. The person must see *that* it is blocked. User story #12. | Text and chrome `--color-disabled`. Not in the tab order (`disabled` on `<button>`). If the reason is not obvious from context, state it in adjacent copy — not inside a Tooltip. |
88
+ | loading | Interaction pending. User story #12. | `aria-busy="true"`. Show [Spinner](spinner.md); keep the original label or replace with a specific loading verb ("Saving…"). Ignore further activations. Do not swap loading for disabled: disabled means "cannot", loading means "working". |
89
+ | error | Not a Button state. | Failure belongs on [Alert](alert.md) or a field ValidationMessage, not on the Button. After failure, return the Button to default so the person can retry. |
90
+
91
+ Disabled is exempt from the AA text-contrast gate (`--color-disabled` is
92
+ intentionally outside it). Loading is not exempt: Spinner and remaining text
93
+ must still meet contrast.
94
+
95
+ ## Accessibility
96
+
97
+ - Native `<button>` (or a component that renders one). Do not put
98
+ `role="button"` on a link or a `div`.
99
+ - `type="button"` unless the Button submits a form (`type="submit"`) or
100
+ resets it (`type="reset"`). A Button inside a form without an explicit
101
+ type is a silent submit — that is a bug.
102
+ - Accessible name is the visible label. Do not override it with a different
103
+ `aria-label`.
104
+ - Focus visible at all times using `--color-focus`.
105
+ - Disabled uses the native `disabled` attribute (removed from tab order).
106
+ Loading may also set `disabled` to prevent double-submit; keep `aria-busy`
107
+ so the pending state is announced.
108
+ - Icon + text: the icon is decorative (`aria-hidden="true"`); the text is the
109
+ name.
110
+
111
+ ### Keyboard
112
+
113
+ | Key | Action |
114
+ | --- | --- |
115
+ | `Enter` | Activate. |
116
+ | `Space` | Activate. |
117
+ | `Tab` / `Shift+Tab` | Move focus to the next / previous control. |
118
+
119
+ ## When to use
120
+
121
+ - The person is causing an action: save, run, create, delete, confirm, retry.
122
+ - The action stays on this URL (including opening a dialog or drawer).
123
+ - Form submit and form-reset controls.
124
+ - PageHeader actions (title + subtitle + Buttons — composition lands in a
125
+ later slice).
126
+
127
+ ## When NOT to use
128
+
129
+ - **Navigation.** Anything that should change the URL, support open-in-new-tab,
130
+ or be copyable as a link — use [Link](link.md), even if it is styled to look
131
+ like a Button.
132
+ - **Icon with no text.** Use [IconButton](icon-button.md).
133
+ - **A row of mutually exclusive choices.** That is Tabs or a Select, not a
134
+ Button group pretending to be navigation.
135
+ - **Toggling a boolean.** Use Switch or Checkbox (form primitives), not a
136
+ Button whose label flips.
137
+ - **In-page warnings.** Use [Alert](alert.md) for the message; the Alert may
138
+ *contain* a Button for recovery.
139
+
140
+ ## Radix/shadcn mapping
141
+
142
+ There is no Radix Button primitive. Behavior is the native `<button>`.
143
+
144
+ | Kiso | Reference |
145
+ | --- | --- |
146
+ | Composition onto a child (rare; prefer a real `<button>`) | Radix [Slot](https://www.radix-ui.com/primitives/docs/utilities/slot) (`asChild`) |
147
+ | Visual system, sizes, variants | shadcn [Button](https://ui.shadcn.com/docs/components/button) |
148
+
149
+ Map Kiso variants onto shadcn *by intent*, not by name:
150
+
151
+ | Kiso variant | shadcn `variant` |
152
+ | --- | --- |
153
+ | `primary` | `outline`, restyled: `--color-primary` text and border, `--type-weight-semibold`. Do not use shadcn's filled `default` — that needs an on-primary text role Kiso does not have. |
154
+ | `default` | `outline` with `--color-foreground` / `--color-border` |
155
+ | `destructive` | `outline` restyled with `--color-danger` text and border, not filled `destructive` |
156
+ | `ghost` | `ghost` |
157
+
158
+ Do **not** use shadcn `variant="link"`. That style is [Link](link.md).
159
+
160
+ Do **not** use shadcn `size="icon"` here. That is [IconButton](icon-button.md).
161
+
162
+ shadcn's "As Link" / `buttonVariants` helper is valid when a *navigation*
163
+ control must *look* like a Button: apply the visual classes to a
164
+ [Link](link.md) (`<a>` / router link). Do not render `<button>` or a
165
+ component that forces `role="button"` for navigation.
166
+
167
+ Loading: compose shadcn Button with shadcn/Kiso [Spinner](spinner.md), as in
168
+ the shadcn Button "Spinner" example. Keep `aria-busy`.
@@ -0,0 +1,154 @@
1
+ # Card
2
+
3
+ Groups related content with visual separation from the surrounding canvas.
4
+
5
+ ## Purpose
6
+
7
+ Card is a surface for one unit of related content: a cluster of fields, a
8
+ summary of a replica, a setting group, a login form. It creates hierarchy by
9
+ sitting on `--color-surface` against `--color-background`.
10
+
11
+ It is not a status label ([Badge](badge.md)), not an in-page warning
12
+ ([Alert](alert.md)), and not a layout primitive for the whole app (Header,
13
+ Sidebar, PageHeader belong to a later slice).
14
+
15
+ User story #18: Badge labels status; Card groups content.
16
+
17
+ ## Anatomy
18
+
19
+ ```
20
+ Card
21
+ ├── media (optional, edge-to-edge at the start)
22
+ ├── Header (optional)
23
+ │ ├── Title
24
+ │ ├── Description
25
+ │ └── Action (optional: Button, IconButton, Badge, or Link)
26
+ ├── Content (optional if Header/Footer already carry the payload)
27
+ └── Footer (optional: actions or supporting meta)
28
+ ```
29
+
30
+ Every part except the root is optional, but a Card with no content is
31
+ decorative and does not belong. Prefer Title + Content as the minimum useful
32
+ shape.
33
+
34
+ - **Title.** Use the heading typography properties (`--type-role-heading-font-family`,
35
+ `--type-role-heading-font-size`, `--type-role-heading-font-weight`,
36
+ `--type-role-heading-letter-spacing`, `--type-role-heading-line-height`) only
37
+ when the Card is a true section; otherwise use the equivalent five
38
+ `--type-role-label-font-family`, `--type-role-label-font-size`,
39
+ `--type-role-label-font-weight`, `--type-role-label-letter-spacing`, and
40
+ `--type-role-label-line-height` properties. One title.
41
+ - **Description.** Use the five body typography properties or the five metadata
42
+ typography properties listed in `tokens/build/tokens.css`. Secondary;
43
+ `--color-muted-foreground`.
44
+ - **Action.** One compact control aligned with the title — not a toolbar.
45
+ - **Content.** The payload. Default type uses `--type-role-body-font-family`,
46
+ `--type-role-body-font-size`, `--type-role-body-font-weight`,
47
+ `--type-role-body-letter-spacing`, and `--type-role-body-line-height`; color
48
+ uses `--color-foreground`.
49
+ - **Footer.** Actions that apply to the whole Card, typically
50
+ [Button](button.md) `sm` / `md`.
51
+
52
+ ## Variants
53
+
54
+ | Variant | When | Tokens |
55
+ | --- | --- | --- |
56
+ | `plain` (default) | Grouping on the page canvas. | Background `--color-surface`, border `--color-border`, radius `--radius-lg`, padding `--spacing-lg`. No shadow. |
57
+ | `elevated` | The grouping must lift above nearby surfaces (a floating picker, a featured summary). | Background `--color-elevated-surface`, border `--color-border`, shadow `--shadow-sm`. Same radius and padding. |
58
+
59
+ Default is `plain`. Tokens.md assigns `--color-surface` to "cards, panels,
60
+ and table rows" and `--color-elevated-surface` to "menus, popovers, and
61
+ dialogs". Use `elevated` only when the Card is competing with other surfaces
62
+ and needs that lift — not on every tile.
63
+
64
+ Do not add outline/ghost Card variants. If the grouping needs no surface,
65
+ it is a section with a heading, not a Card.
66
+
67
+ ## Sizes
68
+
69
+ Size changes padding and gap, not type roles.
70
+
71
+ | Size | Padding / gap | Use |
72
+ | --- | --- | --- |
73
+ | `sm` | `--spacing-md` | Dense dashboards, nested grouping inside a larger region. |
74
+ | `md` (default) | `--spacing-lg` | Standard product Cards. |
75
+ | `lg` | `--spacing-xl` | Rare; a single featured group on an otherwise empty view. |
76
+
77
+ Media that bleeds to the edges still uses the size token for the remaining
78
+ sections, not for the image itself.
79
+
80
+ ## States
81
+
82
+ Card is a container. It does not have hover/active/disabled of its own
83
+ unless the *entire Card* is a single destination or action — which is
84
+ usually the wrong pattern (put a Link or Button inside).
85
+
86
+ | State | Behavior |
87
+ | --- | --- |
88
+ | default | Static grouping. |
89
+ | hover | No chrome change for static Cards. If the whole Card is a Link (rare), use `--color-elevated-surface` on hover and a `--color-focus` ring on focus; the Card *is* the Link, not a Button. |
90
+ | focus | Focus lives on interactive children, not the Card. |
91
+ | active | N/A for static Cards. |
92
+ | disabled | Disable the controls inside; do not grey the whole Card unless every action is unavailable — and then explain why in the Content. |
93
+ | loading | Replace Content with [Skeleton](skeleton.md) shaped like the loaded layout, or set `aria-busy="true"` on the Card. Do not cover a Card with a disconnected [Spinner](spinner.md) if the structure is known. |
94
+ | error | Keep the Card; put an [Alert](alert.md) in Content (or replace Content with the Alert). Do not turn the Card border `--color-danger` as the only error cue. |
95
+
96
+ ## Accessibility
97
+
98
+ - The Card root is a generic grouping (`<section>` or `<article>` when it is
99
+ a self-contained unit; `<div>` when it is only visual). Do not set
100
+ `role="group"` unless the Card is a true composite widget with its own
101
+ name.
102
+ - If the Card has a Title, point the grouping at it:
103
+ `aria-labelledby` on the section.
104
+ - Interactive children keep their own roles (Button, Link, IconButton).
105
+ Do not make the Card clickable *and* nest Buttons — nested interactive
106
+ elements.
107
+ - Keyboard: no Card-level keymap. Tab moves through the children.
108
+
109
+ ### Keyboard
110
+
111
+ | Key | Action |
112
+ | --- | --- |
113
+ | `Tab` / `Shift+Tab` | Move between interactive children. |
114
+
115
+ ## When to use
116
+
117
+ - A bounded cluster of related content that should read as one unit against
118
+ the canvas.
119
+ - A form or setting group that is one task.
120
+ - A summary tile that still contains structure (title, meta, actions) —
121
+ not a single status word.
122
+
123
+ ## When NOT to use
124
+
125
+ - **Status labels** ("Beta", "live"). Use [Badge](badge.md).
126
+ - **Warnings and errors about the page or a section.** Use [Alert](alert.md).
127
+ An Alert may sit *inside* a Card; it is not a Card variant.
128
+ - **Every block on the page.** If removing the surface, border, and radius
129
+ does not hurt understanding, it should not be a Card.
130
+ - **Application chrome.** Header, Sidebar, PageHeader, and tables are their
131
+ own components. A data table does not sit in a Card unless the table is a
132
+ small embedded summary.
133
+ - **Clickable marketing tiles with overlay badges.** Product Cards contain
134
+ controls; they are not posters.
135
+
136
+ ## Radix/shadcn mapping
137
+
138
+ No Radix Card primitive. Structure follows shadcn
139
+ [Card](https://ui.shadcn.com/docs/components/card):
140
+
141
+ | Kiso | shadcn |
142
+ | --- | --- |
143
+ | Card | `Card` |
144
+ | Header | `CardHeader` |
145
+ | Title | `CardTitle` |
146
+ | Description | `CardDescription` |
147
+ | Action | `CardAction` |
148
+ | Content | `CardContent` |
149
+ | Footer | `CardFooter` |
150
+ | `sm` / `md` | `size="sm"` / default; map padding to `--spacing-md` / `--spacing-lg`, not raw values |
151
+ | `plain` / `elevated` | Default shadcn Card uses its `bg-card` token — restyle to `--color-surface` or `--color-elevated-surface` |
152
+
153
+ Ignore shadcn's invitation to hard-code spacing utilities. Card spacing
154
+ consumes `--spacing-md`, `--spacing-lg`, or `--spacing-xl` only.
@@ -0,0 +1,91 @@
1
+ # Checkbox
2
+
3
+ ## Purpose
4
+
5
+ Checkbox represents an independent boolean choice or membership in a list of
6
+ multiple choices. More than one checkbox in a group may be selected.
7
+
8
+ ## Anatomy
9
+
10
+ 1. **Root** — focusable checkbox control and checked-state owner.
11
+ 2. **Indicator** — check mark or indeterminate mark.
12
+ 3. **Label** — names the choice and enlarges the activation target.
13
+ 4. **Description (optional)** — HelperText for consequences or context.
14
+
15
+ ## Variants
16
+
17
+ - **Unchecked / checked** — independent false/true choice.
18
+ - **Indeterminate** — summarizes mixed child selections; it is a visual and
19
+ programmatic third presentation, not a submitted business value by itself.
20
+ - **Checkbox group** — multiple related values within `fieldset` and `legend`.
21
+ - **Required acknowledgment** — use sparingly for a genuinely required consent;
22
+ validation must explain what is needed.
23
+
24
+ Checkbox is for independent or multiple selections. Switch is for one setting
25
+ that takes effect as on/off; Select is for exactly one value from many.
26
+
27
+ ## Sizes
28
+
29
+ - **Small** — dense data and compact lists while retaining an adequate combined
30
+ Label activation target.
31
+ - **Medium** — default.
32
+
33
+ Indicator follows `--type-role-label-font-size`; its label gap is
34
+ `--spacing-sm`. Avoid a
35
+ large decorative variant; hierarchy belongs in the text and layout.
36
+
37
+ ## States
38
+
39
+ | State | Behavior |
40
+ | --- | --- |
41
+ | Default | Unchecked outline uses `--color-border`; checked state uses the semantic primary treatment. |
42
+ | Hover | Root/label pair shows subtle interactive emphasis. |
43
+ | Focus | Visible `--color-focus` ring on the root. |
44
+ | Active | Pressed feedback is brief and does not obscure the checked value. |
45
+ | Disabled | Cannot toggle, uses `--color-disabled`, and the Label reflects disabled state. |
46
+ | Loading | Rare; keep the current value visible and announce pending persistence instead of replacing the indicator. |
47
+ | Error | Group or control is invalid, uses `--color-danger`, and references ValidationMessage. |
48
+
49
+ Loading does not mean unchecked or disabled. For optimistic updates, retain the
50
+ new checked value and expose pending status; on failure, restore or explain the
51
+ actual value with a recovery action.
52
+
53
+ ## Accessibility
54
+
55
+ - Radix supplies `role="checkbox"`, checked/indeterminate state, and hidden
56
+ native form participation. Preserve those semantics.
57
+ - Associate Label with the root ID. A group uses `fieldset`/`legend` or an
58
+ equivalent named group.
59
+ - Use `aria-checked="mixed"` for indeterminate state where not supplied by the
60
+ primitive. Expose required, invalid, disabled, and busy states.
61
+ - Link HelperText and ValidationMessage through `aria-describedby`.
62
+ - Keyboard: `Tab` focuses each enabled checkbox; `Space` toggles it. Do not add
63
+ arrow-key exclusivity—that belongs to radio-group behavior.
64
+
65
+ ## When to use
66
+
67
+ - For independent opt-in/opt-out choices submitted with a form.
68
+ - For selecting zero, one, or many items from a visible list.
69
+ - For a parent “select all” control with a mixed state.
70
+
71
+ ## When NOT to use
72
+
73
+ - Do not use for an immediate single on/off setting; use Switch.
74
+ - Do not use for exactly one mutually exclusive choice from many; use Select
75
+ (or a future RadioGroup).
76
+ - Do not make the check mark the only indication of a consequential choice;
77
+ provide a clear Label and context.
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`, `--radius-sm`, the five property-qualified label
84
+ typography tokens, `--motion-duration-fast`, and `--motion-easing-standard`.
85
+
86
+ ## Radix/shadcn mapping
87
+
88
+ Maps to [Radix Checkbox](https://www.radix-ui.com/primitives/docs/components/checkbox)
89
+ and [shadcn/ui Checkbox](https://ui.shadcn.com/docs/components/checkbox).
90
+ Preserve Radix's controlled/uncontrolled and indeterminate behavior, keyboard
91
+ contract, and form participation.