@teamblind-chorus/ui 1.2.0 → 2.0.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/README.md +3 -3
- package/agents/AGENTS.md +6 -6
- package/agents/DESIGN.md +245 -244
- package/agents/LOVABLE.md +40 -11
- package/agents/catalog.md +4 -4
- package/agents/components/avatar-rail/avatar-rail.md +2 -4
- package/agents/components/avatar-rail/avatar-rail.spec.json +10 -14
- package/agents/components/badge/role.md +7 -9
- package/agents/components/badge/role.spec.json +6 -6
- package/agents/components/badge/update.md +6 -8
- package/agents/components/badge/update.spec.json +5 -5
- package/agents/components/banner/banner.md +16 -18
- package/agents/components/banner/banner.spec.json +14 -14
- package/agents/components/bottom-sheet/bottom-sheet.md +4 -6
- package/agents/components/bottom-sheet/bottom-sheet.spec.json +5 -5
- package/agents/components/bubble/bubble.md +8 -10
- package/agents/components/bubble/bubble.spec.json +11 -11
- package/agents/components/button/button.md +1 -1
- package/agents/components/button/check.md +9 -11
- package/agents/components/button/check.spec.json +8 -10
- package/agents/components/button/fab.md +7 -9
- package/agents/components/button/fab.spec.json +10 -12
- package/agents/components/button/group.spec.json +4 -4
- package/agents/components/button/icon.md +21 -23
- package/agents/components/button/icon.spec.json +12 -14
- package/agents/components/button/standard.md +40 -42
- package/agents/components/button/standard.spec.json +20 -22
- package/agents/components/button/text.md +21 -23
- package/agents/components/button/text.spec.json +13 -15
- package/agents/components/button/toggle.md +7 -9
- package/agents/components/button/toggle.spec.json +10 -12
- package/agents/components/button/toolbar.md +24 -26
- package/agents/components/button/toolbar.spec.json +10 -12
- package/agents/components/carousel/carousel.md +1 -1
- package/agents/components/carousel/post.md +15 -21
- package/agents/components/carousel/post.spec.json +17 -17
- package/agents/components/carousel/profile.md +9 -45
- package/agents/components/carousel/profile.spec.json +17 -17
- package/agents/components/chip/chip.md +1 -1
- package/agents/components/chip/filter.md +22 -24
- package/agents/components/chip/filter.spec.json +17 -13
- package/agents/components/chip/tag.md +22 -24
- package/agents/components/chip/tag.spec.json +19 -15
- package/agents/components/dialog/dialog.md +1 -3
- package/agents/components/dialog/dialog.spec.json +3 -3
- package/agents/components/directory-list/directory-list.md +1 -3
- package/agents/components/directory-list/directory-list.spec.json +2 -2
- package/agents/components/divider/divider.family.json +1 -1
- package/agents/components/divider/divider.md +12 -14
- package/agents/components/divider/divider.spec.json +8 -8
- package/agents/components/empty-state/empty-state.md +9 -9
- package/agents/components/empty-state/empty-state.spec.json +14 -14
- package/agents/components/feed/ad.md +2 -4
- package/agents/components/feed/ad.spec.json +10 -10
- package/agents/components/feed/post.md +41 -43
- package/agents/components/feed/post.spec.json +35 -39
- package/agents/components/form-field/form-field.md +1 -1
- package/agents/components/form-field/input.md +32 -34
- package/agents/components/form-field/input.spec.json +34 -33
- package/agents/components/form-field/search.md +2 -4
- package/agents/components/form-field/search.spec.json +19 -18
- package/agents/components/form-field/select.md +18 -20
- package/agents/components/form-field/select.spec.json +30 -29
- package/agents/components/form-field/textarea.md +3 -5
- package/agents/components/form-field/textarea.spec.json +32 -31
- package/agents/components/header/main.md +4 -6
- package/agents/components/header/main.spec.json +3 -3
- package/agents/components/header/sub.md +6 -8
- package/agents/components/header/sub.spec.json +3 -3
- package/agents/components/list/accordion.md +34 -45
- package/agents/components/list/accordion.spec.json +20 -20
- package/agents/components/list/entry.md +59 -81
- package/agents/components/list/entry.spec.json +20 -23
- package/agents/components/list/list.md +2 -2
- package/agents/components/list/radio.md +13 -20
- package/agents/components/list/radio.spec.json +16 -20
- package/agents/components/list/standard.md +50 -72
- package/agents/components/list/standard.spec.json +18 -21
- package/agents/components/metadata/compact.md +4 -6
- package/agents/components/metadata/compact.spec.json +6 -6
- package/agents/components/metadata/metadata.md +1 -1
- package/agents/components/metadata/standard.md +12 -14
- package/agents/components/metadata/standard.spec.json +10 -10
- package/agents/components/nav-card/nav-card.md +25 -27
- package/agents/components/nav-card/nav-card.spec.json +19 -19
- package/agents/components/nav-list/nav-list.md +2 -8
- package/agents/components/nav-list/nav-list.spec.json +3 -3
- package/agents/components/navigation-bar/main.md +9 -11
- package/agents/components/navigation-bar/main.spec.json +6 -6
- package/agents/components/navigation-bar/search.md +6 -8
- package/agents/components/navigation-bar/search.spec.json +9 -9
- package/agents/components/navigation-bar/sub.md +9 -11
- package/agents/components/navigation-bar/sub.spec.json +7 -7
- package/agents/components/pagination/pagination.family.json +1 -1
- package/agents/components/pagination/pagination.md +3 -3
- package/agents/components/pagination/pagination.spec.json +5 -5
- package/agents/components/profile-header/profile-header.md +9 -11
- package/agents/components/profile-header/profile-header.spec.json +9 -9
- package/agents/components/progress/progress.family.json +1 -1
- package/agents/components/progress/progress.md +5 -5
- package/agents/components/progress/progress.spec.json +8 -8
- package/agents/components/side-sheet/side-sheet.md +11 -13
- package/agents/components/side-sheet/side-sheet.spec.json +3 -3
- package/agents/components/skeleton/skeleton.md +7 -9
- package/agents/components/skeleton/skeleton.spec.json +5 -5
- package/agents/components/spinner/spinner.family.json +1 -1
- package/agents/components/spinner/spinner.md +8 -10
- package/agents/components/spinner/spinner.spec.json +9 -9
- package/agents/components/status-tag/status-tag.md +7 -9
- package/agents/components/status-tag/status-tag.spec.json +5 -5
- package/agents/components/suggestion-list/suggestion-list.md +3 -7
- package/agents/components/suggestion-list/suggestion-list.spec.json +8 -12
- package/agents/components/switch/switch.md +12 -14
- package/agents/components/switch/switch.spec.json +17 -18
- package/agents/components/tab-bar/tab-bar.md +9 -11
- package/agents/components/tab-bar/tab-bar.spec.json +25 -27
- package/agents/components/tabs/rounded.md +6 -8
- package/agents/components/tabs/rounded.spec.json +17 -15
- package/agents/components/tabs/segmented.md +4 -6
- package/agents/components/tabs/segmented.spec.json +4 -8
- package/agents/components/tabs/underline.md +9 -11
- package/agents/components/tabs/underline.spec.json +14 -16
- package/agents/components/thumbnail/thumbnail.md +5 -7
- package/agents/components/thumbnail/thumbnail.spec.json +8 -8
- package/agents/components/toast/toast.md +5 -7
- package/agents/components/toast/toast.spec.json +3 -3
- package/agents/components/tooltip/tooltip.md +6 -8
- package/agents/components/tooltip/tooltip.spec.json +4 -4
- package/agents/tokens.usage.json +71 -226
- package/dist/index.cjs +212 -223
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +16 -16
- package/dist/index.d.ts +16 -16
- package/dist/index.js +212 -223
- package/dist/index.js.map +1 -1
- package/dist/styles.css +386 -387
- package/eslint/rules.js +7 -7
- package/package.json +2 -3
- package/agents/anti-patterns.md +0 -533
- package/agents/compose.md +0 -240
- package/agents/images.md +0 -66
|
@@ -75,24 +75,24 @@
|
|
|
75
75
|
"default": {
|
|
76
76
|
"background": "transparent",
|
|
77
77
|
"border": null,
|
|
78
|
-
"icon": "sys.color.
|
|
78
|
+
"icon": "sys.color.icon.default",
|
|
79
79
|
"note": "Transparent capsule with the glyph in `onSurface`. The canonical Icon Button chrome, used on every regular page surface."
|
|
80
80
|
},
|
|
81
81
|
"inverse": {
|
|
82
82
|
"background": "transparent",
|
|
83
83
|
"border": null,
|
|
84
|
-
"icon": "sys.color.
|
|
84
|
+
"icon": "sys.color.text.inverse",
|
|
85
85
|
"note": "Mirror of `default` for use inside an inverse host (Toast, coach-mark, snackbar). Glyph paints in `inverseOnSurface` so it reads against the host's `inverseSurface` fill; state overlays mix from the same token so the recipe carries over without per-host tuning."
|
|
86
86
|
}
|
|
87
87
|
},
|
|
88
88
|
"customGlyphColor": {
|
|
89
89
|
"allowed": true,
|
|
90
|
-
"description": "Outside the named `default` / `inverse` appearances, the glyph colour is open — the icon inherits `currentColor`, so any Chorus icon-paint token can be applied via inline `color` (e.g. `style={{ color: 'var(--sys-color-icon-yellow)' }}` for an active favorite star, `var(--sys-color-success)` for a confirm check, `var(--sys-color-icon-
|
|
90
|
+
"description": "Outside the named `default` / `inverse` appearances, the glyph colour is open — the icon inherits `currentColor`, so any Chorus icon-paint token can be applied via inline `color` (e.g. `style={{ color: 'var(--sys-color-icon-accent-yellow-default)' }}` for an active favorite star, `var(--sys-color-icon-success)` for a confirm check, `var(--sys-color-icon-subtle)` for the unpressed partner). State overlays still mix from the same token at the standard `hovered`/`pressed`/`focused` state opacities, so hover / pressed / focus carry over without per-host tuning.",
|
|
91
91
|
"constraints": [
|
|
92
|
-
"Pick a Chorus colour token. Two valid sources: (a) a status-pair role (`sys.color.primary`, `sys.color.success`, `sys.color.
|
|
92
|
+
"Pick a Chorus colour token. Two valid sources: (a) a status-pair role (`sys.color.background.primary`, `sys.color.text.success`, `sys.color.text.danger`, etc.) when the glyph travels with its own background; (b) the dedicated icon palette (`sys.color.icon.subtle`, `sys.color.icon.accent.yellow.default`, `sys.color.icon.accent.red.default`, `sys.color.icon.accent.blue.default`, `sys.color.icon.accent.green.default`, `sys.color.icon.accent.purple.default`) for standalone semantic glyphs. Reaching past sys into the raw palette (`ref.palette.yellow.500` etc.), raw hex, and Tailwind colour utilities are all forbidden.",
|
|
93
93
|
"Override the icon's `color` only — never `background`, never wrap in another element to restyle.",
|
|
94
94
|
"Reach for a custom colour when the glyph itself carries semantic weight (favorite, success, warning, brand-tinted host). Otherwise stick to `default` / `inverse`.",
|
|
95
|
-
"The `destructive` flavor (`sys.color.
|
|
95
|
+
"The `destructive` flavor (`sys.color.text.danger`) is a named convenience for the same pattern."
|
|
96
96
|
]
|
|
97
97
|
},
|
|
98
98
|
"flavors": {
|
|
@@ -102,12 +102,12 @@
|
|
|
102
102
|
"default": {
|
|
103
103
|
"background": "transparent",
|
|
104
104
|
"border": null,
|
|
105
|
-
"icon": "sys.color.
|
|
105
|
+
"icon": "sys.color.icon.danger"
|
|
106
106
|
},
|
|
107
107
|
"inverse": {
|
|
108
108
|
"background": "transparent",
|
|
109
109
|
"border": null,
|
|
110
|
-
"icon": "sys.color.
|
|
110
|
+
"icon": "sys.color.icon.danger"
|
|
111
111
|
}
|
|
112
112
|
}
|
|
113
113
|
}
|
|
@@ -138,11 +138,11 @@
|
|
|
138
138
|
"layer": "::after overlay — position:absolute, inset:0, no reflow (DESIGN.md Focus ring composition)",
|
|
139
139
|
"innerCounterRing": {
|
|
140
140
|
"width": "sys.borderWidth.hairline",
|
|
141
|
-
"color": "sys.color.
|
|
141
|
+
"color": "sys.color.border.focused"
|
|
142
142
|
},
|
|
143
143
|
"outerRing": {
|
|
144
144
|
"width": "sys.borderWidth.thin",
|
|
145
|
-
"color": "sys.color.
|
|
145
|
+
"color": "sys.color.border.focused"
|
|
146
146
|
}
|
|
147
147
|
},
|
|
148
148
|
"note": "Keyboard-focus (:focus-visible) visual. Mirrors the `focusIndicator` block (the external-reader contract); kept here so spec-only renderers see focus in the states map. Composes over the lifecycle state the button is in; never via plain mouse click."
|
|
@@ -157,16 +157,14 @@
|
|
|
157
157
|
"focusIndicator": {
|
|
158
158
|
"description": "Keyboard-focus visual — an accessibility indicator, not a lifecycle state. Composes over whichever lifecycle state the button is in. The `states.focused` block above is kept for JSX runtime consumers; this block is the parallel external-reader contract.",
|
|
159
159
|
"composition": "outward",
|
|
160
|
-
"compositionReason": "Action affordance with breathing room around it; the
|
|
160
|
+
"compositionReason": "Action affordance with breathing room around it; the 1px outward ring is reserved by the surrounding layout.",
|
|
161
161
|
"overlay": {
|
|
162
162
|
"color": "icon",
|
|
163
163
|
"opacity": "sys.state.focus"
|
|
164
164
|
},
|
|
165
165
|
"ring": {
|
|
166
|
-
"
|
|
167
|
-
"
|
|
168
|
-
"insetWidth": "sys.borderWidth.hairline",
|
|
169
|
-
"insetColor": "sys.color.focusInset"
|
|
166
|
+
"width": "sys.borderWidth.hairline",
|
|
167
|
+
"color": "sys.color.border.focused"
|
|
170
168
|
},
|
|
171
169
|
"trigger": ":focus-visible (keyboard / programmatic focus, never plain mouse click)"
|
|
172
170
|
},
|
|
@@ -22,9 +22,7 @@ import { Button } from '@teamblind-chorus/ui';
|
|
|
22
22
|
</Button>
|
|
23
23
|
```
|
|
24
24
|
|
|
25
|
-
##
|
|
26
|
-
|
|
27
|
-
### Secondary
|
|
25
|
+
## Secondary
|
|
28
26
|
|
|
29
27
|
Lower-emphasis tier paired against `primary` — opposing actions (Cancel beside Save) or quieter alternatives. Safe to repeat on a single view.
|
|
30
28
|
|
|
@@ -38,7 +36,7 @@ import { Button } from '@teamblind-chorus/ui';
|
|
|
38
36
|
</Button>
|
|
39
37
|
```
|
|
40
38
|
|
|
41
|
-
|
|
39
|
+
## Outlined
|
|
42
40
|
|
|
43
41
|
Bordered blue-on-transparent — supplementary option beside `primary` (*See more*, *Learn more*, *Skip for now*). For opposing paths use `secondary`.
|
|
44
42
|
|
|
@@ -52,9 +50,9 @@ import { Button } from '@teamblind-chorus/ui';
|
|
|
52
50
|
</Button>
|
|
53
51
|
```
|
|
54
52
|
|
|
55
|
-
|
|
53
|
+
## Tertiary
|
|
56
54
|
|
|
57
|
-
Neutral grey ghost — transparent at rest, label in `sys.color.
|
|
55
|
+
Neutral grey ghost — transparent at rest, label in `sys.color.text.subtle`. Reads as a button only on hover.
|
|
58
56
|
|
|
59
57
|
```preview
|
|
60
58
|
button/standard/tertiary
|
|
@@ -66,7 +64,7 @@ import { Button } from '@teamblind-chorus/ui';
|
|
|
66
64
|
</Button>
|
|
67
65
|
```
|
|
68
66
|
|
|
69
|
-
|
|
67
|
+
## Leading icon
|
|
70
68
|
|
|
71
69
|
Optional context glyph before the label. Inherits label color via `currentColor` (`sys.icon.lg` on `large`, `sys.icon.md` on `medium`/`small`).
|
|
72
70
|
|
|
@@ -85,7 +83,7 @@ import { PlusIcon } from '@teamblind-chorus/ui/icons';
|
|
|
85
83
|
</Button>
|
|
86
84
|
```
|
|
87
85
|
|
|
88
|
-
|
|
86
|
+
## Full width
|
|
89
87
|
|
|
90
88
|
Stretched to fill the column (`width: 100%`). Default mobile shape for hero surfaces, empty states, onboarding, login. On wider surfaces, fall back to content-sized.
|
|
91
89
|
|
|
@@ -103,33 +101,7 @@ import { Button } from '@teamblind-chorus/ui';
|
|
|
103
101
|
</Button>
|
|
104
102
|
```
|
|
105
103
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
Compose adjacent Buttons with **`ButtonGroup`** instead of a hand-rolled wrapper — it owns the family's **8px** gap (`sys.layout.inline.md`). Horizontal (default): outlined left, primary right. Vertical (`orientation="vertical"`): primary top, secondary below.
|
|
109
|
-
|
|
110
|
-
```preview
|
|
111
|
-
button/standard/group
|
|
112
|
-
---
|
|
113
|
-
import { Button, ButtonGroup } from '@teamblind-chorus/ui';
|
|
114
|
-
|
|
115
|
-
<ButtonGroup aria-label="Group example">
|
|
116
|
-
<Button appearance="outlined" size="large">See more</Button>
|
|
117
|
-
<Button appearance="primary" size="large">Confirm</Button>
|
|
118
|
-
</ButtonGroup>
|
|
119
|
-
```
|
|
120
|
-
|
|
121
|
-
```preview
|
|
122
|
-
button/standard/group-vertical
|
|
123
|
-
---
|
|
124
|
-
import { Button, ButtonGroup } from '@teamblind-chorus/ui';
|
|
125
|
-
|
|
126
|
-
<ButtonGroup orientation="vertical" aria-label="Group example">
|
|
127
|
-
<Button appearance="primary" size="large" fullWidth>Save</Button>
|
|
128
|
-
<Button appearance="secondary" size="large" fullWidth>Cancel</Button>
|
|
129
|
-
</ButtonGroup>
|
|
130
|
-
```
|
|
131
|
-
|
|
132
|
-
### Docked action bar
|
|
104
|
+
## Docked action bar
|
|
133
105
|
|
|
134
106
|
`ButtonGroup` with **`variant="docked"`** — two **Large** Buttons combined into a footer bar pinned to the bottom of the app, the screen-level commit row for a flow (a picker, a form, a detail view). Full-bleed `sys.color.surface` with a **16px** inset (`sys.layout.container.md`); the two Buttons split the row equally at the family's 8px gap (`sys.layout.inline.md`). No top stroke — instead an upward `sys.elevation.sheet` shadow (the same one [Bottom Sheet](../bottom-sheet/bottom-sheet.md) / [Side Sheet](../side-sheet/side-sheet.md) cast) lifts it off the scrolling body, so content passing behind reads as a separate region. Supplementary **outlined** left, **primary** commit right. Renders in flow — like the other bars it must **not** self-pin (`position: sticky`/`fixed`); [Page Shell](../page-shell/page-shell.md) owns the pinning.
|
|
135
107
|
|
|
@@ -144,7 +116,7 @@ import { Button, ButtonGroup } from '@teamblind-chorus/ui';
|
|
|
144
116
|
</ButtonGroup>
|
|
145
117
|
```
|
|
146
118
|
|
|
147
|
-
An **optional text label** sits above the row — pass it via the `label` prop. It's a caption echoing what the Buttons act on (the current selection, a running total), rendered in `sys.typo.body.md` (**16px**) / `sys.color.
|
|
119
|
+
An **optional text label** sits above the row — pass it via the `label` prop. It's a caption echoing what the Buttons act on (the current selection, a running total), rendered in `sys.typo.body.md` (**16px**) / `sys.color.text.subtle`, centered, separated from the row by `sys.layout.stack.md` (16px); an inline `<strong>` reads as the emphasized value in the full-strength on-surface tone.
|
|
148
120
|
|
|
149
121
|
```preview
|
|
150
122
|
button/standard/docked-bar-labeled
|
|
@@ -161,7 +133,7 @@ import { Button, ButtonGroup } from '@teamblind-chorus/ui';
|
|
|
161
133
|
</ButtonGroup>
|
|
162
134
|
```
|
|
163
135
|
|
|
164
|
-
|
|
136
|
+
## Truncation
|
|
165
137
|
|
|
166
138
|
When the column is narrower than the label, it clips with an ellipsis — Buttons are single-line by contract.
|
|
167
139
|
|
|
@@ -180,7 +152,7 @@ import { Button } from '@teamblind-chorus/ui';
|
|
|
180
152
|
</Button>
|
|
181
153
|
```
|
|
182
154
|
|
|
183
|
-
|
|
155
|
+
## Focus ring
|
|
184
156
|
|
|
185
157
|
Standard keyboard-focus ring (see [Focus ring composition](../../DESIGN.md#focus-ring-composition)).
|
|
186
158
|
|
|
@@ -194,6 +166,32 @@ import { Button } from '@teamblind-chorus/ui';
|
|
|
194
166
|
</Button>
|
|
195
167
|
```
|
|
196
168
|
|
|
169
|
+
## Group
|
|
170
|
+
|
|
171
|
+
Compose adjacent Buttons with **`ButtonGroup`** instead of a hand-rolled wrapper — it owns the family's **8px** gap (`sys.layout.inline.md`). Horizontal (default): outlined left, primary right. Vertical (`orientation="vertical"`): primary top, secondary below.
|
|
172
|
+
|
|
173
|
+
```preview
|
|
174
|
+
button/standard/group
|
|
175
|
+
---
|
|
176
|
+
import { Button, ButtonGroup } from '@teamblind-chorus/ui';
|
|
177
|
+
|
|
178
|
+
<ButtonGroup aria-label="Group example">
|
|
179
|
+
<Button appearance="outlined" size="large">See more</Button>
|
|
180
|
+
<Button appearance="primary" size="large">Confirm</Button>
|
|
181
|
+
</ButtonGroup>
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
```preview
|
|
185
|
+
button/standard/group-vertical
|
|
186
|
+
---
|
|
187
|
+
import { Button, ButtonGroup } from '@teamblind-chorus/ui';
|
|
188
|
+
|
|
189
|
+
<ButtonGroup orientation="vertical" aria-label="Group example">
|
|
190
|
+
<Button appearance="primary" size="large" fullWidth>Save</Button>
|
|
191
|
+
<Button appearance="secondary" size="large" fullWidth>Cancel</Button>
|
|
192
|
+
</ButtonGroup>
|
|
193
|
+
```
|
|
194
|
+
|
|
197
195
|
## Slots
|
|
198
196
|
|
|
199
197
|
- **label** — accessible name. Required, single line; long labels truncate.
|
|
@@ -207,10 +205,10 @@ A **destructive** flavor swaps `primary` → `error` across any appearance; rese
|
|
|
207
205
|
|
|
208
206
|
| Appearance | Background | Border (1px) | Label color | Notes |
|
|
209
207
|
|-------------|---------------|--------------------------|-----------------------------------|-----------------------------------------------------------------------|
|
|
210
|
-
| `primary` | `sys.color.primary` | — | `sys.color.
|
|
211
|
-
| `secondary` | `sys.color.
|
|
212
|
-
| `outlined` | `transparent` | `sys.color.primary` (`sys.borderWidth.hairline`) | `sys.color.primary` | Supplementary option beside `primary`. |
|
|
213
|
-
| `tertiary` | `transparent` | — | `sys.color.
|
|
208
|
+
| `primary` | `sys.color.background.primary` | — | `sys.color.text.onFill` | Single highest-emphasis action; one per view. |
|
|
209
|
+
| `secondary` | `sys.color.background.neutral` | — | `sys.color.text.default` | Lower-emphasis tier; opposing-action and quieter-alternative roles. |
|
|
210
|
+
| `outlined` | `transparent` | `sys.color.background.primary` (`sys.borderWidth.hairline`) | `sys.color.background.primary` | Supplementary option beside `primary`. |
|
|
211
|
+
| `tertiary` | `transparent` | — | `sys.color.text.subtle` | Lowest-emphasis neutral ghost. |
|
|
214
212
|
|
|
215
213
|
## Sizes
|
|
216
214
|
|
|
@@ -92,55 +92,55 @@
|
|
|
92
92
|
},
|
|
93
93
|
"appearances": {
|
|
94
94
|
"primary": {
|
|
95
|
-
"background": "sys.color.primary",
|
|
95
|
+
"background": "sys.color.background.primary",
|
|
96
96
|
"border": null,
|
|
97
|
-
"label": "sys.color.
|
|
97
|
+
"label": "sys.color.text.onFill"
|
|
98
98
|
},
|
|
99
99
|
"secondary": {
|
|
100
|
-
"background": "sys.color.
|
|
100
|
+
"background": "sys.color.background.neutral",
|
|
101
101
|
"border": null,
|
|
102
|
-
"label": "sys.color.
|
|
102
|
+
"label": "sys.color.text.default"
|
|
103
103
|
},
|
|
104
104
|
"outlined": {
|
|
105
105
|
"background": "transparent",
|
|
106
106
|
"border": {
|
|
107
107
|
"width": "sys.borderWidth.hairline",
|
|
108
|
-
"color": "sys.color.
|
|
108
|
+
"color": "sys.color.text.link"
|
|
109
109
|
},
|
|
110
|
-
"label": "sys.color.
|
|
110
|
+
"label": "sys.color.text.link"
|
|
111
111
|
},
|
|
112
112
|
"tertiary": {
|
|
113
113
|
"background": "transparent",
|
|
114
114
|
"border": null,
|
|
115
|
-
"label": "sys.color.
|
|
115
|
+
"label": "sys.color.text.subtle"
|
|
116
116
|
}
|
|
117
117
|
},
|
|
118
118
|
"flavors": {
|
|
119
119
|
"destructive": {
|
|
120
|
-
"description": "
|
|
120
|
+
"description": "Error-family appearances for irreversible commits (Delete, Remove, Discard). Destructive is a TONAL form, not a solid red fill or an outline — a soft danger-toned fill with a danger label and NO border, matching Blind's tonal button system (a tonal red sits beside a tonal neutral). It reads as destructive without a loud red block dominating the (monochrome) UI. A solid red fill is reserved for a future high-stakes escalation only.",
|
|
121
121
|
"appearances": {
|
|
122
122
|
"primary": {
|
|
123
|
-
"background": "sys.color.
|
|
123
|
+
"background": "sys.color.background.danger",
|
|
124
124
|
"border": null,
|
|
125
|
-
"label": "sys.color.
|
|
125
|
+
"label": "sys.color.text.danger"
|
|
126
126
|
},
|
|
127
127
|
"secondary": {
|
|
128
|
-
"background": "sys.color.
|
|
128
|
+
"background": "sys.color.background.danger",
|
|
129
129
|
"border": null,
|
|
130
|
-
"label": "sys.color.
|
|
130
|
+
"label": "sys.color.text.danger"
|
|
131
131
|
},
|
|
132
132
|
"outlined": {
|
|
133
133
|
"background": "transparent",
|
|
134
134
|
"border": {
|
|
135
135
|
"width": "sys.borderWidth.hairline",
|
|
136
|
-
"color": "sys.color.
|
|
136
|
+
"color": "sys.color.text.danger"
|
|
137
137
|
},
|
|
138
|
-
"label": "sys.color.
|
|
138
|
+
"label": "sys.color.text.danger"
|
|
139
139
|
},
|
|
140
140
|
"tertiary": {
|
|
141
141
|
"background": "transparent",
|
|
142
142
|
"border": null,
|
|
143
|
-
"label": "sys.color.
|
|
143
|
+
"label": "sys.color.text.danger"
|
|
144
144
|
}
|
|
145
145
|
}
|
|
146
146
|
}
|
|
@@ -171,11 +171,11 @@
|
|
|
171
171
|
"layer": "::after overlay — position:absolute, inset:0, no reflow (DESIGN.md Focus ring composition)",
|
|
172
172
|
"innerCounterRing": {
|
|
173
173
|
"width": "sys.borderWidth.hairline",
|
|
174
|
-
"color": "sys.color.
|
|
174
|
+
"color": "sys.color.border.focused"
|
|
175
175
|
},
|
|
176
176
|
"outerRing": {
|
|
177
177
|
"width": "sys.borderWidth.thin",
|
|
178
|
-
"color": "sys.color.
|
|
178
|
+
"color": "sys.color.border.focused"
|
|
179
179
|
}
|
|
180
180
|
},
|
|
181
181
|
"note": "Keyboard-focus (:focus-visible) visual. Mirrors the `focusIndicator` block (the external-reader contract); kept here so spec-only renderers see focus in the states map. Composes over the lifecycle state the button is in; never via plain mouse click."
|
|
@@ -190,16 +190,14 @@
|
|
|
190
190
|
"focusIndicator": {
|
|
191
191
|
"description": "Keyboard-focus visual — an accessibility indicator, not a lifecycle state. Composes over whichever lifecycle state the button is in. The `states.focused` block above is kept for JSX runtime consumers; this block is the parallel external-reader contract.",
|
|
192
192
|
"composition": "outward",
|
|
193
|
-
"compositionReason": "Action affordance with breathing room around it; the
|
|
193
|
+
"compositionReason": "Action affordance with breathing room around it; the 1px outward ring is reserved by the surrounding layout.",
|
|
194
194
|
"overlay": {
|
|
195
195
|
"color": "label",
|
|
196
196
|
"opacity": "sys.state.focus"
|
|
197
197
|
},
|
|
198
198
|
"ring": {
|
|
199
|
-
"
|
|
200
|
-
"
|
|
201
|
-
"insetWidth": "sys.borderWidth.hairline",
|
|
202
|
-
"insetColor": "sys.color.focusInset"
|
|
199
|
+
"width": "sys.borderWidth.hairline",
|
|
200
|
+
"color": "sys.color.border.focused"
|
|
203
201
|
},
|
|
204
202
|
"trigger": ":focus-visible (keyboard / programmatic focus, never plain mouse click)"
|
|
205
203
|
},
|
|
@@ -20,9 +20,7 @@ import { Button } from '@teamblind-chorus/ui';
|
|
|
20
20
|
<Button variant="text">Not now</Button>
|
|
21
21
|
```
|
|
22
22
|
|
|
23
|
-
##
|
|
24
|
-
|
|
25
|
-
### Accent
|
|
23
|
+
## Accent
|
|
26
24
|
|
|
27
25
|
Brand-blue label (`primary`) — the inline commit. Reach for `accent` when the button reads as a navigational link (*See all*, *Follow*, *View details*).
|
|
28
26
|
|
|
@@ -34,7 +32,7 @@ import { Button } from '@teamblind-chorus/ui';
|
|
|
34
32
|
<Button variant="text" appearance="accent">Skip</Button>
|
|
35
33
|
```
|
|
36
34
|
|
|
37
|
-
|
|
35
|
+
## On primary
|
|
38
36
|
|
|
39
37
|
Always-white label on top of a `primary`-filled host (Tooltip `default`). Theme-stable.
|
|
40
38
|
|
|
@@ -46,7 +44,7 @@ import { Button } from '@teamblind-chorus/ui';
|
|
|
46
44
|
<Button variant="text" appearance="onPrimary">Got it</Button>
|
|
47
45
|
```
|
|
48
46
|
|
|
49
|
-
|
|
47
|
+
## Inverse
|
|
50
48
|
|
|
51
49
|
For inverse hosts (Toast, coach-mark, snackbar). Label paints `inverseOnSurface`; tokens flip with theme.
|
|
52
50
|
|
|
@@ -58,7 +56,7 @@ import { Button } from '@teamblind-chorus/ui';
|
|
|
58
56
|
<Button variant="text" appearance="inverse">Undo</Button>
|
|
59
57
|
```
|
|
60
58
|
|
|
61
|
-
|
|
59
|
+
## Leading icon
|
|
62
60
|
|
|
63
61
|
16px (`sys.icon.md`) glyph before the label at 4px gap — fixed across rungs.
|
|
64
62
|
|
|
@@ -70,7 +68,7 @@ import { Button, ChevronLeftIcon } from '@teamblind-chorus/ui';
|
|
|
70
68
|
<Button variant="text" leadingIcon={<ChevronLeftIcon />}>Back</Button>
|
|
71
69
|
```
|
|
72
70
|
|
|
73
|
-
|
|
71
|
+
## Trailing icon
|
|
74
72
|
|
|
75
73
|
Destination glyph after the label — chevron-right *Continue*, external-link *Open in new tab*.
|
|
76
74
|
|
|
@@ -82,7 +80,7 @@ import { Button, ChevronRightIcon } from '@teamblind-chorus/ui';
|
|
|
82
80
|
<Button variant="text" trailingIcon={<ChevronRightIcon />}>Continue</Button>
|
|
83
81
|
```
|
|
84
82
|
|
|
85
|
-
|
|
83
|
+
## Dropdown
|
|
86
84
|
|
|
87
85
|
Disclosure trigger: the label reads as the **current value**, the trailing chevron flips with state — `ChevronDownIcon` at rest, `ChevronUpIcon` while the menu is open. Pair `aria-haspopup` + `aria-expanded` on the trigger and portal the menu so it escapes any clipping ancestor; never freeze the chevron when the menu is open.
|
|
88
86
|
|
|
@@ -112,31 +110,31 @@ function Example() {
|
|
|
112
110
|
}
|
|
113
111
|
```
|
|
114
112
|
|
|
115
|
-
|
|
113
|
+
## Focus ring
|
|
116
114
|
|
|
117
|
-
|
|
115
|
+
Standard ring.
|
|
118
116
|
|
|
119
117
|
```preview
|
|
120
|
-
button/text/
|
|
118
|
+
button/text/focused
|
|
121
119
|
---
|
|
122
120
|
import { Button } from '@teamblind-chorus/ui';
|
|
123
121
|
|
|
124
|
-
<
|
|
125
|
-
<Button variant="text">Cancel</Button>
|
|
126
|
-
<Button variant="text" appearance="accent">Save</Button>
|
|
127
|
-
</div>
|
|
122
|
+
<Button variant="text" state="focused">Skip</Button>
|
|
128
123
|
```
|
|
129
124
|
|
|
130
|
-
|
|
125
|
+
## Group
|
|
131
126
|
|
|
132
|
-
|
|
127
|
+
Optical alignment means chrome-to-chrome gap **is** the visible label-to-label distance. Row gap: `medium`/`small` → 16px (`sys.layout.inline.xl`); `xsmall` → 12px (`sys.layout.inline.lg`).
|
|
133
128
|
|
|
134
129
|
```preview
|
|
135
|
-
button/text/
|
|
130
|
+
button/text/group
|
|
136
131
|
---
|
|
137
132
|
import { Button } from '@teamblind-chorus/ui';
|
|
138
133
|
|
|
139
|
-
<
|
|
134
|
+
<div style={{ display: 'inline-flex', gap: 'var(--sys-layout-inline-md)' }}>
|
|
135
|
+
<Button variant="text">Cancel</Button>
|
|
136
|
+
<Button variant="text" appearance="accent">Save</Button>
|
|
137
|
+
</div>
|
|
140
138
|
```
|
|
141
139
|
|
|
142
140
|
## Slots
|
|
@@ -151,10 +149,10 @@ A **destructive** flavor swaps the label to `error` across every appearance.
|
|
|
151
149
|
|
|
152
150
|
| Appearance | Background (rest) | Label color | When to reach for it |
|
|
153
151
|
|-------------|-------------------|-----------------------------------|--------------------------------------------------------------------------------------|
|
|
154
|
-
| `default` | `transparent` | `sys.color.
|
|
155
|
-
| `accent` | `transparent` | `sys.color.primary` | Brand-blue inline commit — "Skip", "See all". One per row. |
|
|
156
|
-
| `onPrimary` | `transparent` | `sys.color.
|
|
157
|
-
| `inverse` | `transparent` | `sys.color.
|
|
152
|
+
| `default` | `transparent` | `sys.color.text.subtle` | Base inline action — "Not now", secondary inline trail commits. |
|
|
153
|
+
| `accent` | `transparent` | `sys.color.background.primary` | Brand-blue inline commit — "Skip", "See all". One per row. |
|
|
154
|
+
| `onPrimary` | `transparent` | `sys.color.text.onFill` | On a `primary`-filled host (Tooltip `default`). Theme-stable. |
|
|
155
|
+
| `inverse` | `transparent` | `sys.color.text.inverse` | Inside an inverse host (Toast, coach-mark). |
|
|
158
156
|
|
|
159
157
|
## Sizes
|
|
160
158
|
|
|
@@ -100,26 +100,26 @@
|
|
|
100
100
|
"default": {
|
|
101
101
|
"background": "transparent",
|
|
102
102
|
"border": null,
|
|
103
|
-
"label": "sys.color.
|
|
103
|
+
"label": "sys.color.text.subtle",
|
|
104
104
|
"note": "The base neutral inline action — the canonical Text Button. Quiet enough to live next to typographic content without claiming commit-rank attention."
|
|
105
105
|
},
|
|
106
106
|
"accent": {
|
|
107
107
|
"background": "transparent",
|
|
108
108
|
"border": null,
|
|
109
|
-
"label": "sys.color.
|
|
109
|
+
"label": "sys.color.text.link",
|
|
110
110
|
"note": "Brand-blue label for the inline commit affordance. Use sparingly — never two `accent` Text Buttons in the same row.",
|
|
111
111
|
"linkAffordanceRecommendation": "Prefer `accent` whenever the Text Button reads as a **link affordance** — a section header's trailing 'See all' / 'See more', a card-header 'Follow', an inline 'View details' next to a body paragraph. Link-like affordances should carry chromatic emphasis so the navigational intent is unambiguous; `default` (onSurfaceVariant) is for quieter inline commits that should recede into the body copy. Override to `default` only when a parent surface (e.g. a busy chrome bar) already carries enough chromatic weight that an `accent` label would compete."
|
|
112
112
|
},
|
|
113
113
|
"onPrimary": {
|
|
114
114
|
"background": "transparent",
|
|
115
115
|
"border": null,
|
|
116
|
-
"label": "sys.color.
|
|
116
|
+
"label": "sys.color.text.onFill",
|
|
117
117
|
"note": "Always-white label for use on top of a `primary`-filled host (e.g. the Tooltip `default` appearance). Both `primary` and `onPrimary` are theme-stable (blue / white in light and dark mode), so the label reads as white against the brand-blue fill in either theme — unlike `inverse`, which flips with the theme."
|
|
118
118
|
},
|
|
119
119
|
"inverse": {
|
|
120
120
|
"background": "transparent",
|
|
121
121
|
"border": null,
|
|
122
|
-
"label": "sys.color.
|
|
122
|
+
"label": "sys.color.text.inverse",
|
|
123
123
|
"note": "Mirror for use inside an inverse host (Toast, coach-mark, snackbar). Label paints in `inverseOnSurface` so it reads against the host's `inverseSurface` fill; state overlays mix from the same token so the recipe carries over without per-host tuning. The `inverseOnSurface` token FLIPS with the theme (white in light mode, dark in dark mode) — that's correct against the also-flipping `inverseSurface` host. When the host fill does NOT flip (e.g. a `primary`-filled Tooltip in `default` appearance), reach for `onPrimary` instead so the label stays white in both themes."
|
|
124
124
|
}
|
|
125
125
|
},
|
|
@@ -130,22 +130,22 @@
|
|
|
130
130
|
"default": {
|
|
131
131
|
"background": "transparent",
|
|
132
132
|
"border": null,
|
|
133
|
-
"label": "sys.color.
|
|
133
|
+
"label": "sys.color.text.danger"
|
|
134
134
|
},
|
|
135
135
|
"accent": {
|
|
136
136
|
"background": "transparent",
|
|
137
137
|
"border": null,
|
|
138
|
-
"label": "sys.color.
|
|
138
|
+
"label": "sys.color.text.danger"
|
|
139
139
|
},
|
|
140
140
|
"onPrimary": {
|
|
141
141
|
"background": "transparent",
|
|
142
142
|
"border": null,
|
|
143
|
-
"label": "sys.color.
|
|
143
|
+
"label": "sys.color.text.danger"
|
|
144
144
|
},
|
|
145
145
|
"inverse": {
|
|
146
146
|
"background": "transparent",
|
|
147
147
|
"border": null,
|
|
148
|
-
"label": "sys.color.
|
|
148
|
+
"label": "sys.color.text.danger"
|
|
149
149
|
}
|
|
150
150
|
}
|
|
151
151
|
}
|
|
@@ -176,11 +176,11 @@
|
|
|
176
176
|
"layer": "::after overlay — position:absolute, inset:0, no reflow (DESIGN.md Focus ring composition)",
|
|
177
177
|
"innerCounterRing": {
|
|
178
178
|
"width": "sys.borderWidth.hairline",
|
|
179
|
-
"color": "sys.color.
|
|
179
|
+
"color": "sys.color.border.focused"
|
|
180
180
|
},
|
|
181
181
|
"outerRing": {
|
|
182
182
|
"width": "sys.borderWidth.thin",
|
|
183
|
-
"color": "sys.color.
|
|
183
|
+
"color": "sys.color.border.focused"
|
|
184
184
|
}
|
|
185
185
|
},
|
|
186
186
|
"note": "Keyboard-focus (:focus-visible) visual. Mirrors the `focusIndicator` block (the external-reader contract); kept here so spec-only renderers see focus in the states map. Composes over the lifecycle state the button is in; never via plain mouse click."
|
|
@@ -195,16 +195,14 @@
|
|
|
195
195
|
"focusIndicator": {
|
|
196
196
|
"description": "Keyboard-focus visual — an accessibility indicator, not a lifecycle state. Composes over whichever lifecycle state the button is in. The `states.focused` block above is kept for JSX runtime consumers; this block is the parallel external-reader contract.",
|
|
197
197
|
"composition": "outward",
|
|
198
|
-
"compositionReason": "Action affordance with breathing room around it; the
|
|
198
|
+
"compositionReason": "Action affordance with breathing room around it; the 1px outward ring is reserved by the surrounding layout.",
|
|
199
199
|
"overlay": {
|
|
200
200
|
"color": "label",
|
|
201
201
|
"opacity": "sys.state.focus"
|
|
202
202
|
},
|
|
203
203
|
"ring": {
|
|
204
|
-
"
|
|
205
|
-
"
|
|
206
|
-
"insetWidth": "sys.borderWidth.hairline",
|
|
207
|
-
"insetColor": "sys.color.focusInset"
|
|
204
|
+
"width": "sys.borderWidth.hairline",
|
|
205
|
+
"color": "sys.color.border.focused"
|
|
208
206
|
},
|
|
209
207
|
"trigger": ":focus-visible (keyboard / programmatic focus, never plain mouse click)"
|
|
210
208
|
},
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
> 🇰🇷 한국어: [`i18n/ko/schema/components/button/toggle.md`](../../../i18n/ko/schema/components/button/toggle.md)
|
|
4
4
|
|
|
5
|
-
Commit-and-record action — a Toolbar-footprint button with two states. **Inactive** invites the commit (`primary` fill); **active** records it (`transparent` fill + hairline `
|
|
5
|
+
Commit-and-record action — a Toolbar-footprint button with two states. **Inactive** invites the commit (`primary` fill); **active** records it (`transparent` fill + hairline `border.default` outline).
|
|
6
6
|
|
|
7
7
|
**Reach for this when** you need a reversible commit that persists across views — *Follow / Following*, *Subscribe / Subscribed*, *Join / Joined*. **Skip when** the action is one-shot ([Standard Button](./standard.md)), the row is a dense toolbar ([Toolbar Button](./toolbar.md)), or the toggle belongs to a filter set ([Filter Chip](../chip/filter.md)).
|
|
8
8
|
|
|
@@ -24,7 +24,7 @@ import { Button } from '@teamblind-chorus/ui';
|
|
|
24
24
|
|
|
25
25
|
## Active
|
|
26
26
|
|
|
27
|
-
The committed form — `transparent` fill with hairline `
|
|
27
|
+
The committed form — `transparent` fill with hairline `border.default` stroke. The transparent fill lets the button sit on any host surface tier (page `surface`, card `surfaceContainer`, raised `surfaceContainerHigh`) without re-painting a background that would clash with the host. Use the same element across both states and toggle the `active` flag; the consumer swaps the label text. Reports state via `aria-pressed`.
|
|
28
28
|
|
|
29
29
|
```preview
|
|
30
30
|
button/toggle/active
|
|
@@ -36,9 +36,7 @@ import { Button } from '@teamblind-chorus/ui';
|
|
|
36
36
|
</Button>
|
|
37
37
|
```
|
|
38
38
|
|
|
39
|
-
##
|
|
40
|
-
|
|
41
|
-
### With icon
|
|
39
|
+
## Leading icon
|
|
42
40
|
|
|
43
41
|
A check glyph on commit reinforces the active read. Inactive form stays glyph-less.
|
|
44
42
|
|
|
@@ -53,7 +51,7 @@ import { CheckedIcon } from '@teamblind-chorus/ui/icons';
|
|
|
53
51
|
</Button>
|
|
54
52
|
```
|
|
55
53
|
|
|
56
|
-
|
|
54
|
+
## Focus ring
|
|
57
55
|
|
|
58
56
|
Both forms take the same standard ring; below shows inactive. See [Focus ring composition](../../DESIGN.md#focus-ring-composition).
|
|
59
57
|
|
|
@@ -71,7 +69,7 @@ import { Button } from '@teamblind-chorus/ui';
|
|
|
71
69
|
|
|
72
70
|
- **label** — accessible name. Required, single line. Consumer swaps the verb between states ("Follow" → "Following"); no auto-rewrite.
|
|
73
71
|
- **leadingIcon** (optional) — context glyph before the label. Inherits colour via `currentColor` per the [family rule](./button.md#icon-colour-inheritance-family-wide).
|
|
74
|
-
- **trailingIcon** (optional) — directional/destination glyph after the label. Same contract as [Toolbar Button](./toolbar.md#
|
|
72
|
+
- **trailingIcon** (optional) — directional/destination glyph after the label. Same contract as [Toolbar Button](./toolbar.md#trailing-icon).
|
|
75
73
|
|
|
76
74
|
## Sizes
|
|
77
75
|
|
|
@@ -93,8 +91,8 @@ A single visual variant — inactive/active is expressed as a state on the same
|
|
|
93
91
|
|
|
94
92
|
| State | Background | Border (always 1px `sys.borderWidth.hairline`) | Label / icon color | Notes |
|
|
95
93
|
|--------------|-------------------------------------|---------------------------------------------------------|-----------------------------------|----------------------------------------------------------------------|
|
|
96
|
-
| inactive | `sys.color.primary` | `transparent` | `sys.color.
|
|
97
|
-
| active | `transparent` | `sys.color.
|
|
94
|
+
| inactive | `sys.color.background.primary` | `transparent` | `sys.color.text.onFill` | Brand-loud fill inviting commit. Border `transparent` but 1px width held so footprint never changes between states. |
|
|
95
|
+
| active | `transparent` | `sys.color.border.default` | `sys.color.text.default` | Committed form — hairline-outlined ghost over whatever host surface the button sits on. Transparent fill records state without claiming attention or clashing with the host tier. |
|
|
98
96
|
|
|
99
97
|
## States
|
|
100
98
|
|