@teamblind-chorus/ui 1.1.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 +10 -8
- package/agents/components/avatar-rail/avatar-rail.md +2 -4
- package/agents/components/avatar-rail/avatar-rail.spec.json +27 -12
- 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.family.json +3 -1
- package/agents/components/banner/banner.md +66 -15
- package/agents/components/banner/banner.spec.json +37 -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 +25 -8
- package/agents/components/button/fab.md +7 -9
- package/agents/components/button/fab.spec.json +27 -10
- package/agents/components/button/group.spec.json +4 -4
- package/agents/components/button/icon.md +21 -23
- package/agents/components/button/icon.spec.json +29 -12
- package/agents/components/button/standard.md +40 -42
- package/agents/components/button/standard.spec.json +37 -20
- package/agents/components/button/text.md +21 -23
- package/agents/components/button/text.spec.json +30 -13
- package/agents/components/button/toggle.md +7 -9
- package/agents/components/button/toggle.spec.json +27 -10
- 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 +34 -11
- package/agents/components/chip/tag.md +22 -24
- package/agents/components/chip/tag.spec.json +36 -13
- 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.family.json +28 -0
- package/agents/components/empty-state/empty-state.md +69 -0
- package/agents/components/empty-state/empty-state.spec.json +87 -0
- 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 +39 -31
- package/agents/components/form-field/search.md +2 -4
- package/agents/components/form-field/search.spec.json +24 -16
- package/agents/components/form-field/select.md +18 -20
- package/agents/components/form-field/select.spec.json +36 -27
- package/agents/components/form-field/textarea.md +3 -5
- package/agents/components/form-field/textarea.spec.json +37 -29
- 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 +26 -17
- package/agents/components/list/entry.md +59 -81
- package/agents/components/list/entry.spec.json +37 -21
- package/agents/components/list/list.md +2 -2
- package/agents/components/list/radio.md +13 -20
- package/agents/components/list/radio.spec.json +33 -18
- package/agents/components/list/standard.md +88 -64
- package/agents/components/list/standard.spec.json +52 -20
- 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 +25 -16
- 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/page-shell/page-shell.family.json +1 -1
- package/agents/components/page-shell/page-shell.md +33 -0
- package/agents/components/page-shell/page-shell.spec.json +85 -0
- 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 +27 -0
- package/agents/components/spinner/spinner.md +96 -0
- package/agents/components/spinner/spinner.spec.json +82 -0
- 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 +23 -15
- package/agents/components/tab-bar/tab-bar.md +9 -11
- package/agents/components/tab-bar/tab-bar.spec.json +37 -23
- package/agents/components/tabs/rounded.md +6 -8
- package/agents/components/tabs/rounded.spec.json +34 -13
- 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 +31 -14
- 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/manifest.json +8 -6
- package/agents/tokens.usage.json +71 -226
- package/agents/usage.json +12 -0
- package/dist/index.cjs +531 -262
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +57 -13
- package/dist/index.d.ts +57 -13
- package/dist/index.js +530 -263
- package/dist/index.js.map +1 -1
- package/dist/styles.css +560 -379
- 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
|
@@ -27,9 +27,7 @@ import { PlusIcon } from '@teamblind-chorus/ui/icons';
|
|
|
27
27
|
</Button>
|
|
28
28
|
```
|
|
29
29
|
|
|
30
|
-
##
|
|
31
|
-
|
|
32
|
-
### Secondary
|
|
30
|
+
## Secondary
|
|
33
31
|
|
|
34
32
|
Theme-toned FAB that defers to the canvas's surface tones. Reach for it when a brand-red FAB would over-claim the page hierarchy (filtered map, image-rich feed).
|
|
35
33
|
|
|
@@ -48,7 +46,7 @@ import { PlusIcon } from '@teamblind-chorus/ui/icons';
|
|
|
48
46
|
</Button>
|
|
49
47
|
```
|
|
50
48
|
|
|
51
|
-
|
|
49
|
+
## Icon
|
|
52
50
|
|
|
53
51
|
Icon-only — 48 × 48 circle with a 24px glyph. For universally legible actions (`+`, pencil). **Requires `aria-label`.**
|
|
54
52
|
|
|
@@ -66,7 +64,7 @@ import { PlusIcon } from '@teamblind-chorus/ui/icons';
|
|
|
66
64
|
/>
|
|
67
65
|
```
|
|
68
66
|
|
|
69
|
-
|
|
67
|
+
## Text
|
|
70
68
|
|
|
71
69
|
Label-only pill. For multi-word actions or verbs without an obvious glyph (*Save draft*).
|
|
72
70
|
|
|
@@ -80,7 +78,7 @@ import { Button } from '@teamblind-chorus/ui';
|
|
|
80
78
|
</Button>
|
|
81
79
|
```
|
|
82
80
|
|
|
83
|
-
|
|
81
|
+
## Extended
|
|
84
82
|
|
|
85
83
|
Icon + label. Default for primary canvas commits where space allows (desktop canvases, mobile sheets).
|
|
86
84
|
|
|
@@ -99,7 +97,7 @@ import { PlusIcon } from '@teamblind-chorus/ui/icons';
|
|
|
99
97
|
</Button>
|
|
100
98
|
```
|
|
101
99
|
|
|
102
|
-
|
|
100
|
+
## Focus ring
|
|
103
101
|
|
|
104
102
|
Standard ring with the FAB's floating elevation stacked underneath. See [Focus ring composition](../../DESIGN.md#focus-ring-composition).
|
|
105
103
|
|
|
@@ -127,8 +125,8 @@ Two appearances on the standard [Button](./button.md) emphasis ladder — `prima
|
|
|
127
125
|
|
|
128
126
|
| Appearance | Background | Label / icon color | When to reach for it |
|
|
129
127
|
|-------------|----------------------------------|----------------------|---------------------------------------------------------------------------------------------------|
|
|
130
|
-
| `primary` | `sys.color.brand` | `sys.color.
|
|
131
|
-
| `secondary` | `sys.color.surfaceContainerHigh` | `sys.color.
|
|
128
|
+
| `primary` | `sys.color.text.brand` | `sys.color.text.onFill` | Brand-red commit anchoring the canvas's next step (Compose, Add, Create). |
|
|
129
|
+
| `secondary` | `sys.color.surfaceContainerHigh` | `sys.color.text.default` | Theme-toned alternative on dense/chromatic canvases where brand-red would over-claim. |
|
|
132
130
|
|
|
133
131
|
## Sizes
|
|
134
132
|
|
|
@@ -56,12 +56,12 @@
|
|
|
56
56
|
},
|
|
57
57
|
"appearances": {
|
|
58
58
|
"primary": {
|
|
59
|
-
"background": "sys.color.brand",
|
|
60
|
-
"label": "sys.color.
|
|
59
|
+
"background": "sys.color.text.brand",
|
|
60
|
+
"label": "sys.color.text.onFill"
|
|
61
61
|
},
|
|
62
62
|
"secondary": {
|
|
63
|
-
"background": "sys.color.
|
|
64
|
-
"label": "sys.color.
|
|
63
|
+
"background": "sys.color.surface.default",
|
|
64
|
+
"label": "sys.color.text.default"
|
|
65
65
|
}
|
|
66
66
|
},
|
|
67
67
|
"states": {
|
|
@@ -79,27 +79,44 @@
|
|
|
79
79
|
"color": "label",
|
|
80
80
|
"opacity": "sys.state.pressed"
|
|
81
81
|
}
|
|
82
|
+
},
|
|
83
|
+
"focused": {
|
|
84
|
+
"overlay": {
|
|
85
|
+
"color": "label",
|
|
86
|
+
"opacity": "sys.state.focus"
|
|
87
|
+
},
|
|
88
|
+
"focusRing": {
|
|
89
|
+
"composition": "outward",
|
|
90
|
+
"layer": "::after overlay — position:absolute, inset:0, no reflow (DESIGN.md Focus ring composition)",
|
|
91
|
+
"innerCounterRing": {
|
|
92
|
+
"width": "sys.borderWidth.hairline",
|
|
93
|
+
"color": "sys.color.border.focused"
|
|
94
|
+
},
|
|
95
|
+
"outerRing": {
|
|
96
|
+
"width": "sys.borderWidth.thin",
|
|
97
|
+
"color": "sys.color.border.focused"
|
|
98
|
+
}
|
|
99
|
+
},
|
|
100
|
+
"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 FAB is in; never via plain mouse click."
|
|
82
101
|
}
|
|
83
102
|
},
|
|
84
103
|
"focusIndicator": {
|
|
85
104
|
"description": "Keyboard-focus visual — an accessibility indicator, not a lifecycle state. Composes over whichever lifecycle state the FAB is in. The ring stacks above the FAB's floating elevation so the lift survives focus. The `states.focused` block above is kept for JSX runtime consumers; this block is the parallel external-reader contract.",
|
|
86
105
|
"composition": "outward",
|
|
87
|
-
"compositionReason": "Action affordance with breathing room around it; the
|
|
106
|
+
"compositionReason": "Action affordance with breathing room around it; the 1px outward ring is reserved by the surrounding layout.",
|
|
88
107
|
"overlay": {
|
|
89
108
|
"color": "label",
|
|
90
109
|
"opacity": "sys.state.focus"
|
|
91
110
|
},
|
|
92
111
|
"ring": {
|
|
93
|
-
"
|
|
94
|
-
"
|
|
95
|
-
"insetWidth": "sys.borderWidth.hairline",
|
|
96
|
-
"insetColor": "sys.color.focusInset"
|
|
112
|
+
"width": "sys.borderWidth.hairline",
|
|
113
|
+
"color": "sys.color.border.focused"
|
|
97
114
|
},
|
|
98
115
|
"trigger": ":focus-visible (keyboard / programmatic focus, never plain mouse click)"
|
|
99
116
|
},
|
|
100
117
|
"forbidden": [
|
|
101
118
|
"more than one fab per screen — the FAB is the single canonical commit",
|
|
102
|
-
"fab styled with sys.color.primary fill — the FAB carries sys.color.brand by anatomy contract",
|
|
119
|
+
"fab styled with sys.color.background.primary fill — the FAB carries sys.color.text.brand by anatomy contract",
|
|
103
120
|
"destructive flavor on a FAB — destructive commits live inside Dialog / BottomSheet, never a floating commit",
|
|
104
121
|
"fab placed inline in flow — it floats over content, anchored bottom-right by the page shell"
|
|
105
122
|
]
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"name": "ButtonGroup",
|
|
4
4
|
"family": "button",
|
|
5
5
|
"subcomponent": "group",
|
|
6
|
-
"description": "Lays out two or more Buttons as one composition so consumers never hand-roll the wrapper div. A NAMED export (`import { ButtonGroup }`) — NOT a Button variant. Three forms share the family's 8px (`sys.layout.inline.md`) gap: inline horizontal (default), inline vertical (`orientation=\"vertical\"`), and docked (`variant=\"docked\"`) — a footer bar pinned to the bottom of the app. The docked form adds full-bleed `sys.color.surface`, a 16px (`sys.layout.container.md`) inset, an upward `sys.elevation.sheet` shadow (no top stroke), and an optional `label` above the row.",
|
|
6
|
+
"description": "Lays out two or more Buttons as one composition so consumers never hand-roll the wrapper div. A NAMED export (`import { ButtonGroup }`) — NOT a Button variant. Three forms share the family's 8px (`sys.layout.inline.md`) gap: inline horizontal (default), inline vertical (`orientation=\"vertical\"`), and docked (`variant=\"docked\"`) — a footer bar pinned to the bottom of the app. The docked form adds full-bleed `sys.color.surface.default`, a 16px (`sys.layout.container.md`) inset, an upward `sys.elevation.sheet` shadow (no top stroke), and an optional `label` above the row.",
|
|
7
7
|
"element": "div",
|
|
8
8
|
"props": {
|
|
9
9
|
"variant": {
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
"label": {
|
|
27
27
|
"type": "node",
|
|
28
28
|
"optional": true,
|
|
29
|
-
"note": "Optional caption above the row. Rendered in `sys.typo.body.md` (16px) / `sys.color.
|
|
29
|
+
"note": "Optional caption above the row. Rendered in `sys.typo.body.md` (16px) / `sys.color.text.subtle`, centered. An inline <strong> reads as the emphasized value in the full-strength on-surface tone."
|
|
30
30
|
}
|
|
31
31
|
},
|
|
32
32
|
"slots": {
|
|
@@ -39,7 +39,7 @@
|
|
|
39
39
|
},
|
|
40
40
|
"label": {
|
|
41
41
|
"required": false,
|
|
42
|
-
"description": "Optional caption above the row (`label` prop). `sys.typo.body.md` (16px), `sys.color.
|
|
42
|
+
"description": "Optional caption above the row (`label` prop). `sys.typo.body.md` (16px), `sys.color.text.subtle`, centered, 16px (`sys.layout.stack.md`) above the row.",
|
|
43
43
|
"accepts": [
|
|
44
44
|
"text"
|
|
45
45
|
]
|
|
@@ -49,7 +49,7 @@
|
|
|
49
49
|
"gap": "sys.layout.inline.md (8) — between Buttons, both axes",
|
|
50
50
|
"labelStackGap": "sys.layout.stack.md (16) — label → row",
|
|
51
51
|
"docked": {
|
|
52
|
-
"background": "sys.color.surface",
|
|
52
|
+
"background": "sys.color.surface.default",
|
|
53
53
|
"padding": "sys.layout.container.md (16) — all four sides",
|
|
54
54
|
"elevation": "sys.elevation.sheet — upward shadow, the same one Bottom Sheet / Side Sheet cast",
|
|
55
55
|
"border": null,
|
|
@@ -21,9 +21,7 @@ import { SearchIcon } from '@teamblind-chorus/ui/icons';
|
|
|
21
21
|
<Button variant="icon" size="large" icon={<SearchIcon />} aria-label="Search" />
|
|
22
22
|
```
|
|
23
23
|
|
|
24
|
-
##
|
|
25
|
-
|
|
26
|
-
### Inverse
|
|
24
|
+
## Inverse
|
|
27
25
|
|
|
28
26
|
Mirror for inverse hosts (Toast dismiss, coach-mark close). Glyph paints in `inverseOnSurface` against the host's `inverseSurface` fill; state overlays mix from the same token.
|
|
29
27
|
|
|
@@ -36,9 +34,9 @@ import { XIcon } from '@teamblind-chorus/ui/icons';
|
|
|
36
34
|
<Button variant="icon" size="medium" appearance="inverse" icon={<XIcon />} aria-label="Dismiss" />
|
|
37
35
|
```
|
|
38
36
|
|
|
39
|
-
|
|
37
|
+
## Custom palette
|
|
40
38
|
|
|
41
|
-
Outside the named appearances, the glyph inherits `currentColor` — any Chorus icon-paint token works. Reach for a custom colour when the glyph carries semantic weight (favorite star → `sys.color.icon.yellow`, success check → `sys.color.success`, warning bolt, channel-branded glyph in a brand-tinted host). Apply via inline `color` so state overlays still mix from the same token at standard `sys.state.*` opacities — never override `background` or wrap in another element to recolour.
|
|
39
|
+
Outside the named appearances, the glyph inherits `currentColor` — any Chorus icon-paint token works. Reach for a custom colour when the glyph carries semantic weight (favorite star → `sys.color.icon.accent.yellow.default`, success check → `sys.color.text.success`, warning bolt, channel-branded glyph in a brand-tinted host). Apply via inline `color` so state overlays still mix from the same token at standard `sys.state.*` opacities — never override `background` or wrap in another element to recolour.
|
|
42
40
|
|
|
43
41
|
```preview
|
|
44
42
|
button/icon/custom-color
|
|
@@ -47,41 +45,41 @@ import { Button } from '@teamblind-chorus/ui';
|
|
|
47
45
|
import { StarFillIcon } from '@teamblind-chorus/ui/icons';
|
|
48
46
|
|
|
49
47
|
<div style={{ display: 'inline-flex', gap: 'var(--sys-layout-inline-xl)' }}>
|
|
50
|
-
<Button variant="icon" icon={<StarFillIcon />} aria-label="Favorite — inactive" style={{ color: 'var(--sys-color-icon-
|
|
51
|
-
<Button variant="icon" icon={<StarFillIcon />} aria-label="Favorite — active" style={{ color: 'var(--sys-color-icon-yellow)' }} />
|
|
48
|
+
<Button variant="icon" icon={<StarFillIcon />} aria-label="Favorite — inactive" style={{ color: 'var(--sys-color-icon-subtle)' }} />
|
|
49
|
+
<Button variant="icon" icon={<StarFillIcon />} aria-label="Favorite — active" style={{ color: 'var(--sys-color-icon-accent-yellow-default)' }} />
|
|
52
50
|
</div>
|
|
53
51
|
```
|
|
54
52
|
|
|
55
53
|
Pick from `sys.color.*` — semantic roles (`primary` / `success` / `error` / …) for status pairs that also carry a background, and the dedicated `sys.color.icon.*` palette (`muted` / `yellow` / `red` / `blue` / `green` / `purple`) for standalone semantic glyphs. Reaching past sys into `ref.palette.*`, hardcoded hex, and Tailwind colour utilities are all forbidden.
|
|
56
54
|
|
|
57
|
-
|
|
55
|
+
## Focus ring
|
|
58
56
|
|
|
59
|
-
|
|
57
|
+
Standard ring (see [Focus ring composition](../../DESIGN.md#focus-ring-composition)).
|
|
60
58
|
|
|
61
59
|
```preview
|
|
62
|
-
button/icon/
|
|
60
|
+
button/icon/focused
|
|
63
61
|
---
|
|
64
62
|
import { Button } from '@teamblind-chorus/ui';
|
|
65
|
-
import { SearchIcon
|
|
63
|
+
import { SearchIcon } from '@teamblind-chorus/ui/icons';
|
|
66
64
|
|
|
67
|
-
<
|
|
68
|
-
<Button variant="icon" icon={<SearchIcon />} aria-label="Search" />
|
|
69
|
-
<Button variant="icon" icon={<ChatIcon />} aria-label="Messages" />
|
|
70
|
-
<Button variant="icon" icon={<ProfileIcon />} aria-label="Profile" />
|
|
71
|
-
</div>
|
|
65
|
+
<Button variant="icon" icon={<SearchIcon />} aria-label="Search" state="focused" />
|
|
72
66
|
```
|
|
73
67
|
|
|
74
|
-
|
|
68
|
+
## Group
|
|
75
69
|
|
|
76
|
-
|
|
70
|
+
Three Icon Buttons in a row — common shape on the [Navigation bar](../navigation-bar/navigation-bar.md) Main trailing slot. Adjacent buttons sit 16px apart (`sys.layout.inline.xl`); with optical alignment on, that gap *is* the visible glyph-to-glyph distance.
|
|
77
71
|
|
|
78
72
|
```preview
|
|
79
|
-
button/icon/
|
|
73
|
+
button/icon/group
|
|
80
74
|
---
|
|
81
75
|
import { Button } from '@teamblind-chorus/ui';
|
|
82
|
-
import { SearchIcon } from '@teamblind-chorus/ui/icons';
|
|
76
|
+
import { SearchIcon, ChatIcon, ProfileIcon } from '@teamblind-chorus/ui/icons';
|
|
83
77
|
|
|
84
|
-
<
|
|
78
|
+
<div style={{ display: 'inline-flex', gap: 'var(--sys-layout-inline-xl)' }}>
|
|
79
|
+
<Button variant="icon" icon={<SearchIcon />} aria-label="Search" />
|
|
80
|
+
<Button variant="icon" icon={<ChatIcon />} aria-label="Messages" />
|
|
81
|
+
<Button variant="icon" icon={<ProfileIcon />} aria-label="Profile" />
|
|
82
|
+
</div>
|
|
85
83
|
```
|
|
86
84
|
|
|
87
85
|
## Slots
|
|
@@ -105,8 +103,8 @@ Two named appearances; geometry identical, only the glyph colour pair flips.
|
|
|
105
103
|
|
|
106
104
|
| Appearance | Background | Border | Icon color | When to reach for it |
|
|
107
105
|
|-------------|---------------|--------|----------------------------------|----------------------|
|
|
108
|
-
| `default` | `transparent` | none | `sys.color.
|
|
109
|
-
| `inverse` | `transparent` | none | `sys.color.
|
|
106
|
+
| `default` | `transparent` | none | `sys.color.text.default` | Every regular page surface. |
|
|
107
|
+
| `inverse` | `transparent` | none | `sys.color.text.inverse` | For use inside an inverse host (Toast dismiss, coach-mark close). |
|
|
110
108
|
|
|
111
109
|
## Sizes
|
|
112
110
|
|
|
@@ -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
|
}
|
|
@@ -128,6 +128,25 @@
|
|
|
128
128
|
"opacity": "sys.state.pressed"
|
|
129
129
|
}
|
|
130
130
|
},
|
|
131
|
+
"focused": {
|
|
132
|
+
"overlay": {
|
|
133
|
+
"color": "icon",
|
|
134
|
+
"opacity": "sys.state.focus"
|
|
135
|
+
},
|
|
136
|
+
"focusRing": {
|
|
137
|
+
"composition": "outward",
|
|
138
|
+
"layer": "::after overlay — position:absolute, inset:0, no reflow (DESIGN.md Focus ring composition)",
|
|
139
|
+
"innerCounterRing": {
|
|
140
|
+
"width": "sys.borderWidth.hairline",
|
|
141
|
+
"color": "sys.color.border.focused"
|
|
142
|
+
},
|
|
143
|
+
"outerRing": {
|
|
144
|
+
"width": "sys.borderWidth.thin",
|
|
145
|
+
"color": "sys.color.border.focused"
|
|
146
|
+
}
|
|
147
|
+
},
|
|
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."
|
|
149
|
+
},
|
|
131
150
|
"disabled": {
|
|
132
151
|
"overlay": null,
|
|
133
152
|
"containerOpacity": "sys.state.disabled",
|
|
@@ -138,16 +157,14 @@
|
|
|
138
157
|
"focusIndicator": {
|
|
139
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.",
|
|
140
159
|
"composition": "outward",
|
|
141
|
-
"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.",
|
|
142
161
|
"overlay": {
|
|
143
162
|
"color": "icon",
|
|
144
163
|
"opacity": "sys.state.focus"
|
|
145
164
|
},
|
|
146
165
|
"ring": {
|
|
147
|
-
"
|
|
148
|
-
"
|
|
149
|
-
"insetWidth": "sys.borderWidth.hairline",
|
|
150
|
-
"insetColor": "sys.color.focusInset"
|
|
166
|
+
"width": "sys.borderWidth.hairline",
|
|
167
|
+
"color": "sys.color.border.focused"
|
|
151
168
|
},
|
|
152
169
|
"trigger": ":focus-visible (keyboard / programmatic focus, never plain mouse click)"
|
|
153
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
|
}
|
|
@@ -161,6 +161,25 @@
|
|
|
161
161
|
"opacity": "sys.state.pressed"
|
|
162
162
|
}
|
|
163
163
|
},
|
|
164
|
+
"focused": {
|
|
165
|
+
"overlay": {
|
|
166
|
+
"color": "label",
|
|
167
|
+
"opacity": "sys.state.focus"
|
|
168
|
+
},
|
|
169
|
+
"focusRing": {
|
|
170
|
+
"composition": "outward",
|
|
171
|
+
"layer": "::after overlay — position:absolute, inset:0, no reflow (DESIGN.md Focus ring composition)",
|
|
172
|
+
"innerCounterRing": {
|
|
173
|
+
"width": "sys.borderWidth.hairline",
|
|
174
|
+
"color": "sys.color.border.focused"
|
|
175
|
+
},
|
|
176
|
+
"outerRing": {
|
|
177
|
+
"width": "sys.borderWidth.thin",
|
|
178
|
+
"color": "sys.color.border.focused"
|
|
179
|
+
}
|
|
180
|
+
},
|
|
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."
|
|
182
|
+
},
|
|
164
183
|
"disabled": {
|
|
165
184
|
"overlay": null,
|
|
166
185
|
"containerOpacity": "sys.state.disabled",
|
|
@@ -171,16 +190,14 @@
|
|
|
171
190
|
"focusIndicator": {
|
|
172
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.",
|
|
173
192
|
"composition": "outward",
|
|
174
|
-
"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.",
|
|
175
194
|
"overlay": {
|
|
176
195
|
"color": "label",
|
|
177
196
|
"opacity": "sys.state.focus"
|
|
178
197
|
},
|
|
179
198
|
"ring": {
|
|
180
|
-
"
|
|
181
|
-
"
|
|
182
|
-
"insetWidth": "sys.borderWidth.hairline",
|
|
183
|
-
"insetColor": "sys.color.focusInset"
|
|
199
|
+
"width": "sys.borderWidth.hairline",
|
|
200
|
+
"color": "sys.color.border.focused"
|
|
184
201
|
},
|
|
185
202
|
"trigger": ":focus-visible (keyboard / programmatic focus, never plain mouse click)"
|
|
186
203
|
},
|