@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
|
@@ -33,9 +33,7 @@ const [open, setOpen] = useState(false);
|
|
|
33
33
|
</>
|
|
34
34
|
```
|
|
35
35
|
|
|
36
|
-
##
|
|
37
|
-
|
|
38
|
-
### Overflow
|
|
36
|
+
## Overflow
|
|
39
37
|
|
|
40
38
|
When content exceeds the card's `max-height`, the content slot scrolls internally — handle and actions stay pinned, footer gains its `is-elevated` upward shadow.
|
|
41
39
|
|
|
@@ -62,7 +60,7 @@ const [open, setOpen] = useState(false);
|
|
|
62
60
|
</>
|
|
63
61
|
```
|
|
64
62
|
|
|
65
|
-
|
|
63
|
+
## Keyboard
|
|
66
64
|
|
|
67
65
|
When the sheet hosts an input that summons the virtual keyboard, the card lifts above the keyboard's top edge so the actions footer stays reachable. Handle and footer pinned; content scrolls to keep the focused input in view.
|
|
68
66
|
|
|
@@ -88,7 +86,7 @@ const [open, setOpen] = useState(false);
|
|
|
88
86
|
</>
|
|
89
87
|
```
|
|
90
88
|
|
|
91
|
-
|
|
89
|
+
## Nested step
|
|
92
90
|
|
|
93
91
|
The sheet can host a **drill-in step** without spawning a second modal. The consumer swaps title, content, and primary action between renders; passing `onBack` paints a leading back chevron — an Icon Button rendering `ChevronLeftIcon` at `sys.icon.lg` (24), with `sys.layout.inline.md` (8) between glyph and title. Card chrome, scrim, drag handle, and actions footer stay identical across steps.
|
|
94
92
|
|
|
@@ -151,7 +149,7 @@ const [value, setValue] = useState('');
|
|
|
151
149
|
| drag handle | 48 × 4px pill, `onSurfaceVariant @ 40%`, `sys.radius.full`, 8px vertical gutter |
|
|
152
150
|
| content | Flex column, 16px padding, 16px between children, vertical scroll on overflow |
|
|
153
151
|
| title | `sys.typo.heading.lg` (24 / Semibold), `onSurface` |
|
|
154
|
-
| back chevron | [Icon Button](../button/icon.md) → `ChevronLeftIcon` at `sys.icon.lg` (24), `sys.color.
|
|
152
|
+
| back chevron | [Icon Button](../button/icon.md) → `ChevronLeftIcon` at `sys.icon.lg` (24), `sys.color.text.default`. Glyph aligns to the title's leading edge via Icon Button optical-alignment. `sys.layout.inline.md` (8) glyph → title gap. |
|
|
155
153
|
| body | `sys.typo.body.md` (16 / Regular), `onSurfaceVariant` |
|
|
156
154
|
| actions | Flex column, 8px between buttons, 16px padding on all four sides |
|
|
157
155
|
| primary CTA | [Button](../button/button.md) `appearance="primary"`, `size="large"`, `fullWidth` |
|
|
@@ -104,14 +104,14 @@
|
|
|
104
104
|
},
|
|
105
105
|
"sizing": {
|
|
106
106
|
"scrimTint": "ref.palette.black.600",
|
|
107
|
-
"containerFill": "sys.color.
|
|
107
|
+
"containerFill": "sys.color.surface.default",
|
|
108
108
|
"containerRadiusTop": "sys.radius.xl",
|
|
109
109
|
"containerRadiusBottom": "0",
|
|
110
110
|
"elevation": "sys.elevation.sheet",
|
|
111
111
|
"maxWidth": "480px",
|
|
112
112
|
"maxHeight": "90vh",
|
|
113
113
|
"dragHandleSize": "48 × 4px",
|
|
114
|
-
"dragHandleFill": "sys.color.
|
|
114
|
+
"dragHandleFill": "sys.color.text.subtle @ 40%",
|
|
115
115
|
"dragHandleRadius": "sys.radius.full",
|
|
116
116
|
"dragHandleGutter": "sys.layout.container.xs",
|
|
117
117
|
"contentPadding": "sys.layout.container.md",
|
|
@@ -119,13 +119,13 @@
|
|
|
119
119
|
"actionsStackGap": "sys.layout.stack.xs",
|
|
120
120
|
"actionsPadding": "sys.layout.container.md",
|
|
121
121
|
"titleTypo": "sys.typo.heading.lg",
|
|
122
|
-
"titleColor": "sys.color.
|
|
122
|
+
"titleColor": "sys.color.text.default",
|
|
123
123
|
"backIcon": "ChevronLeftIcon",
|
|
124
124
|
"backIconSize": "sys.icon.lg",
|
|
125
|
-
"backIconColor": "sys.color.
|
|
125
|
+
"backIconColor": "sys.color.icon.default",
|
|
126
126
|
"backToTitleGap": "sys.layout.inline.md",
|
|
127
127
|
"bodyTypo": "sys.typo.body.md",
|
|
128
|
-
"bodyColor": "sys.color.
|
|
128
|
+
"bodyColor": "sys.color.text.subtle"
|
|
129
129
|
},
|
|
130
130
|
"states": {
|
|
131
131
|
"open": {
|
|
@@ -8,7 +8,7 @@ A small persistent annotation pill with a tail pointing at an anchor — a chat-
|
|
|
8
8
|
|
|
9
9
|
**Layout inset.** `inline` — the bubble ships no positioning. The host anchors it to the target element by *visual alignment* (CSS anchor positioning, or a positioned wrapper around the anchor): the tail's TIP sits flush on the anchor's content edge (padding excluded) — the bubble body is set back from that edge by the tail's own protrusion (`ref.space.50 / √2`) so the tail meets the anchor cleanly rather than overlapping it — and the bubble centres on the anchor's visual centre so the tail sits at the anchor's bottom-centre. The bubble caps its own `max-width` so it always keeps an 8-token margin from every viewport edge; position-clamping is the host's job. Only when centring would push the bubble past that safe margin does the host shift it inward and flip `tailAlign` so the tail still points at the anchor — `start` for left-edge anchors, `end` for right-edge anchors, `center` whenever the anchor has room on both sides (the default).
|
|
10
10
|
|
|
11
|
-
**Colour tuning.** Default fill `sys.color.primary` / label `sys.color.
|
|
11
|
+
**Colour tuning.** Default fill `sys.color.background.primary` / label `sys.color.text.onFill` — both theme-stable. Operations re-tint per campaign by setting `--bubble-fill` and `--bubble-ink` on inline style; the tail's `background: inherit` follows automatically.
|
|
12
12
|
|
|
13
13
|
## Default
|
|
14
14
|
|
|
@@ -20,9 +20,7 @@ import { Bubble } from '@teamblind-chorus/ui';
|
|
|
20
20
|
<Bubble>5 new messages + gift</Bubble>
|
|
21
21
|
```
|
|
22
22
|
|
|
23
|
-
##
|
|
24
|
-
|
|
25
|
-
### Anchored to a top-bar icon
|
|
23
|
+
## Anchored icon
|
|
26
24
|
|
|
27
25
|
A [Navigation bar (home)](../navigation-bar/main.md) with three trailing actions, bubble anchored to the chat glyph itself. The glyph carries `anchor-name: --chat-icon`; the bubble pins to the glyph's bottom — padding excluded — set back by the tail's own protrusion (`top: calc(anchor(bottom) + var(--bubble-tail-protrusion))`, the system token = `ref.space.50 / √2`) so the tail's top vertex lands *flush* on the glyph's bottom edge rather than poking into it, and centres on the glyph's visual centre (`left: anchor(center)` + `translateX(-50%)`). The tail tip thus sits on the chat icon's bottom-centre regardless of where the bar reflows — no hardcoded pixel offsets, `tailAlign="center"`.
|
|
28
26
|
|
|
@@ -70,7 +68,7 @@ import { SearchIcon, ChatIcon, ProfileIcon } from '@teamblind-chorus/ui/icons';
|
|
|
70
68
|
</div>
|
|
71
69
|
```
|
|
72
70
|
|
|
73
|
-
|
|
71
|
+
## Tail alignment
|
|
74
72
|
|
|
75
73
|
Three tail positions stacked so the offset reads at a glance — pick by where the anchor sits.
|
|
76
74
|
|
|
@@ -86,7 +84,7 @@ import { Bubble } from '@teamblind-chorus/ui';
|
|
|
86
84
|
</div>
|
|
87
85
|
```
|
|
88
86
|
|
|
89
|
-
|
|
87
|
+
## Operations re-tint
|
|
90
88
|
|
|
91
89
|
Brand red instead of primary blue — the tail inherits the fill, so a single declaration covers both surfaces.
|
|
92
90
|
|
|
@@ -95,12 +93,12 @@ bubble/recoloured
|
|
|
95
93
|
---
|
|
96
94
|
import { Bubble } from '@teamblind-chorus/ui';
|
|
97
95
|
|
|
98
|
-
<Bubble style={{ '--bubble-fill': 'var(--sys-color-brand)', '--bubble-ink': 'var(--sys-color-
|
|
96
|
+
<Bubble style={{ '--bubble-fill': 'var(--sys-color-text-brand)', '--bubble-ink': 'var(--sys-color-text-onFill)' }}>
|
|
99
97
|
Free daily tarot
|
|
100
98
|
</Bubble>
|
|
101
99
|
```
|
|
102
100
|
|
|
103
|
-
|
|
101
|
+
## Long copy
|
|
104
102
|
|
|
105
103
|
Copy that exceeds the host width truncates with an ellipsis. If the message can't fit on one line, it belongs in a [Banner](../banner/banner.md) instead.
|
|
106
104
|
|
|
@@ -124,8 +122,8 @@ import { Bubble } from '@teamblind-chorus/ui';
|
|
|
124
122
|
|
|
125
123
|
| Slot | Token bindings |
|
|
126
124
|
|-----------|----------------|
|
|
127
|
-
| container | Fill `--bubble-fill` (default `sys.color.primary`), ink `--bubble-ink` (default `sys.color.
|
|
128
|
-
| body | `sys.typo.
|
|
125
|
+
| container | Fill `--bubble-fill` (default `sys.color.background.primary`), ink `--bubble-ink` (default `sys.color.text.onFill`), `sys.layout.container.2xs` padding-block, `ref.space.75` padding-inline, `sys.radius.full`, viewport-safe `max-width` cap |
|
|
126
|
+
| body | `sys.typo.label.xs` |
|
|
129
127
|
| tail | `ref.space.50` square, rotated 45° |
|
|
130
128
|
|
|
131
129
|
## Behavior
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"$schema": "../../spec.schema.json",
|
|
3
3
|
"name": "Bubble",
|
|
4
4
|
"family": "bubble",
|
|
5
|
-
"description": "Always-on annotation bubble — a pill-shaped label with a small tail that points at the anchor UI element. Ships with a default brand-blue fill (`sys.color.primary` / `sys.color.
|
|
5
|
+
"description": "Always-on annotation bubble — a pill-shaped label with a small tail that points at the anchor UI element. Ships with a default brand-blue fill (`sys.color.background.primary` / `sys.color.text.onFill` — both theme-stable so the bubble reads identically in light and dark mode) and exposes two CSS custom properties (`--bubble-fill`, `--bubble-ink`) so operations can re-tint per campaign without forking the component. Distinct from Tooltip on three axes: (1) persistent rather than transient — Bubble stays in view as part of the UI's resting state; (2) lower visual priority — no elevation shadow, smaller padding, single-line truncation; (3) does NOT occlude its neighbours — the host positions it inline next to the anchor, never as a portal-mounted overlay.",
|
|
6
6
|
"element": "div",
|
|
7
7
|
"props": {
|
|
8
8
|
"children": {
|
|
@@ -36,12 +36,12 @@
|
|
|
36
36
|
"slots": {
|
|
37
37
|
"container": {
|
|
38
38
|
"required": true,
|
|
39
|
-
"description": "The pill body. `position: relative` so the tail can pin to its edge; `display: inline-flex` so the bubble shrink-wraps its body. Carries the fill (`--bubble-fill` defaulting to `sys.color.primary`), ink (`--bubble-ink` defaulting to `sys.color.
|
|
39
|
+
"description": "The pill body. `position: relative` so the tail can pin to its edge; `display: inline-flex` so the bubble shrink-wraps its body. Carries the fill (`--bubble-fill` defaulting to `sys.color.background.primary`), ink (`--bubble-ink` defaulting to `sys.color.text.onFill`), 4 / 6 padding, and pill radius. `role='note'` so the annotation reads as supplementary rather than as a main UI control.",
|
|
40
40
|
"intrinsic": true
|
|
41
41
|
},
|
|
42
42
|
"body": {
|
|
43
43
|
"required": true,
|
|
44
|
-
"description": "Bubble copy in `sys.typo.
|
|
44
|
+
"description": "Bubble copy in `sys.typo.label.xs` (10 / Semibold). Single line — `white-space: nowrap` + `text-overflow: ellipsis` truncate overflow rather than wrap. Inherits the container's ink colour.",
|
|
45
45
|
"accepts": ["text"]
|
|
46
46
|
},
|
|
47
47
|
"tail": {
|
|
@@ -51,9 +51,9 @@
|
|
|
51
51
|
}
|
|
52
52
|
},
|
|
53
53
|
"sizing": {
|
|
54
|
-
"background": "sys.color.primary",
|
|
55
|
-
"foreground": "sys.color.
|
|
56
|
-
"labelTypo": "sys.typo.
|
|
54
|
+
"background": "sys.color.background.primary",
|
|
55
|
+
"foreground": "sys.color.text.onFill",
|
|
56
|
+
"labelTypo": "sys.typo.label.xs",
|
|
57
57
|
"paddingBlock": "sys.layout.container.2xs",
|
|
58
58
|
"paddingInline": "ref.space.75",
|
|
59
59
|
"radius": "sys.radius.full",
|
|
@@ -62,9 +62,9 @@
|
|
|
62
62
|
"viewportSafeArea": "sys.layout.container.xs"
|
|
63
63
|
},
|
|
64
64
|
"appearance": {
|
|
65
|
-
"background": "sys.color.primary",
|
|
66
|
-
"foreground": "sys.color.
|
|
67
|
-
"note": "Single canonical appearance — Bubble has no `default` / `accent` / `destructive` axis. Default fill is `sys.color.primary` and label is `sys.color.
|
|
65
|
+
"background": "sys.color.background.primary",
|
|
66
|
+
"foreground": "sys.color.text.onFill",
|
|
67
|
+
"note": "Single canonical appearance — Bubble has no `default` / `accent` / `destructive` axis. Default fill is `sys.color.background.primary` and label is `sys.color.text.onFill`; both are theme-stable so the bubble reads identically in light and dark mode. Operations re-tint by setting `--bubble-fill` and `--bubble-ink` on the bubble's inline style or a wrapping class — the tail inherits the fill via `background: inherit`, so a colour swap on the container covers the whole bubble in one declaration. Re-tints should keep the contrast above WCAG AA against `--bubble-ink` (4.5:1); the system does not enforce this at runtime."
|
|
68
68
|
},
|
|
69
69
|
"behavior": {
|
|
70
70
|
"ariaRole": "Container carries `role='note'` so screen readers announce the bubble as a supplementary note rather than as a main UI control. The decorative tail carries `aria-hidden='true'`.",
|
|
@@ -74,12 +74,12 @@
|
|
|
74
74
|
"zeroGapToAnchor": "**The tail's TIP sits flush on the anchor's CONTENT edge (padding excluded) — no overlap, no gap.** This is the canonical home of the set-back math. Align to the anchor's content box, not its padding box: the tail-bearing edge (top for `tailSide='top'`, bottom for `tailSide='bottom'`) is set BACK from the anchor by the tail's own protrusion so the tail's outer vertex lands exactly on the anchor's content edge. The tail is a `ref.space.50` (4) square rotated 45°, so its vertex pokes `ref.space.50 / √2` ≈ 2.83 past the bubble edge; the system exposes this at :root as `--bubble-tail-protrusion`, and the host adds exactly that set-back (e.g. `top: calc(anchor(bottom) + var(--bubble-tail-protrusion))`). Overlapping the anchor reads as a collision; a gap larger than the protrusion leaves the tail unattached. For more breathing room, widen the *anchor's* padding — never enlarge the set-back. This √2 set-back is the single derived constant in the contract; the horizontal axis is pure visual alignment.",
|
|
75
75
|
"tailAlignSelection": "`tailAlign='center'` is the default and preferred case — the bubble centres on the anchor so the tail sits dead-centre and its tip falls on the anchor's centreX by construction (see tailTipCentredOnAnchor). It flips off-centre only as an edge fallback: when the anchor sits so near a viewport edge that centring would push the bubble past the 8-token safe margin, the bubble shifts AWAY from the edge and `tailAlign` flips to the SAME side as the anchor — `end` at the right edge (bubble extends leftward), `start` at the left edge (bubble extends rightward). In every case the bubble's nearest edge stays ≥8 from the viewport edge and the tip stays on the anchor's centreline; visibility is the contract, tail position is the lever.",
|
|
76
76
|
"positioning": "Bubble is presentational — the *host* owns positioning, and the canonical pattern binds bubble to anchor by VISUAL ALIGNMENT (CSS anchor positioning, or a positioned wrapper around the anchor) rather than DOM proximity, so the tail tracks the anchor through reflows. Four coupled decisions, each detailed in its own rule: (a) `tailSide` — `top` when the bubble sits below the anchor, `bottom` when above; (b) the vertical set-back so the tail tip is flush on the anchor's content edge — see zeroGapToAnchor; (c) horizontal centring so the tip lands on the anchor's centreX — see tailTipCentredOnAnchor; (d) `tailAlign`, `center` by default and flipped only at a viewport edge — see tailAlignSelection. Get any one wrong and the tail points at empty space.",
|
|
77
|
-
"colorTuning": "Two CSS custom properties — `--bubble-fill` (background) and `--bubble-ink` (label) — are the supported runtime override surface. Default to `sys.color.primary` / `sys.color.
|
|
77
|
+
"colorTuning": "Two CSS custom properties — `--bubble-fill` (background) and `--bubble-ink` (label) — are the supported runtime override surface. Default to `sys.color.background.primary` / `sys.color.text.onFill`. Operations campaigns set them on inline style; static themes set them on a wrapper class. The tail's `background: inherit` follows automatically."
|
|
78
78
|
},
|
|
79
79
|
"forbidden": [
|
|
80
80
|
"bubble used as a transient hover/focus tooltip — that role is the `tooltip` family; bubble must remain in view at the UI's resting state",
|
|
81
81
|
"bubble portal-mounted at document root with a fixed z-index that overlays surrounding chrome — bubble must NEVER occlude neighbour elements; that contract is what separates it from Tooltip",
|
|
82
|
-
"bubble painted with an elevation shadow (`sys.elevation.
|
|
82
|
+
"bubble painted with an elevation shadow (`sys.elevation.floating` / `overlay` / `sheet`) — the lift signal is what makes Tooltip 'jump'; Bubble is intentionally flat",
|
|
83
83
|
"bubble copy that wraps to multiple lines — overflow truncates with an ellipsis rather than wrapping; if the message can't fit on one line, it belongs in a Banner or a Tooltip with action",
|
|
84
84
|
"bubble retinted with hardcoded hex / rgba on `background` or `color` instead of `--bubble-fill` / `--bubble-ink` — campaign tunes must route through the documented custom-property surface so the tail colour tracks the body",
|
|
85
85
|
"bubble used to convey *required* meaning (error blocking a submit, account-locked notice) — that role is Banner / Dialog; bubble is a soft annotation only",
|
|
@@ -20,7 +20,7 @@ Transparent-rest forms ([Icon Button](./icon.md), [Text Button](./text.md), [Ter
|
|
|
20
20
|
|
|
21
21
|
### Focus ring
|
|
22
22
|
|
|
23
|
-
All Button family components draw the same outward
|
|
23
|
+
All Button family components draw the same outward single ring as a `position: absolute` pseudo-element — never a layout-affecting border. Suppressed while `disabled`. Trigger: `:focus-visible`. See [DESIGN.md → Focus ring composition](../../DESIGN.md#focus-ring-composition).
|
|
24
24
|
|
|
25
25
|
## Sub-components
|
|
26
26
|
|
|
@@ -35,11 +35,9 @@ function Demo() {
|
|
|
35
35
|
<Demo />
|
|
36
36
|
```
|
|
37
37
|
|
|
38
|
-
##
|
|
38
|
+
## Accent
|
|
39
39
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
Brand-blue label — `sys.color.primary`. Use sparingly; never two accent Check Buttons in the same row.
|
|
40
|
+
Brand-blue label — `sys.color.background.primary`. Use sparingly; never two accent Check Buttons in the same row.
|
|
43
41
|
|
|
44
42
|
```preview
|
|
45
43
|
button/check/accent
|
|
@@ -51,9 +49,9 @@ import { Button } from '@teamblind-chorus/ui';
|
|
|
51
49
|
</Button>
|
|
52
50
|
```
|
|
53
51
|
|
|
54
|
-
|
|
52
|
+
## Inverse
|
|
55
53
|
|
|
56
|
-
Mirror for inverse hosts (Toast, coach-mark, snackbar). Label paints `sys.color.
|
|
54
|
+
Mirror for inverse hosts (Toast, coach-mark, snackbar). Label paints `sys.color.text.inverse` against the host's `inverseSurface` fill.
|
|
57
55
|
|
|
58
56
|
```preview
|
|
59
57
|
button/check/inverse
|
|
@@ -65,7 +63,7 @@ import { Button } from '@teamblind-chorus/ui';
|
|
|
65
63
|
</Button>
|
|
66
64
|
```
|
|
67
65
|
|
|
68
|
-
|
|
66
|
+
## Checked
|
|
69
67
|
|
|
70
68
|
Same row with `checked={true}` — checkbox glyph flips to the filled square. State overlays follow the label color.
|
|
71
69
|
|
|
@@ -79,7 +77,7 @@ import { Button } from '@teamblind-chorus/ui';
|
|
|
79
77
|
</Button>
|
|
80
78
|
```
|
|
81
79
|
|
|
82
|
-
|
|
80
|
+
## Middle icon
|
|
83
81
|
|
|
84
82
|
Optional 16px icon between checkbox and label. Use sparingly — most rows don't need it. Canonical case: an item-use row where the middle glyph names the item being consumed.
|
|
85
83
|
|
|
@@ -100,9 +98,9 @@ Three appearances. `default` is the base neutral toggle; `accent` paints the lab
|
|
|
100
98
|
|
|
101
99
|
| Appearance | Background (rest) | Label color | When to reach for it |
|
|
102
100
|
|-----------|-------------------|------------------------------|-----------------------------------------------------------------------|
|
|
103
|
-
| `default` | `transparent` | `sys.color.
|
|
104
|
-
| `accent` | `transparent` | `sys.color.primary` | One option per row that needs commit-rank emphasis. |
|
|
105
|
-
| `inverse` | `transparent` | `sys.color.
|
|
101
|
+
| `default` | `transparent` | `sys.color.text.subtle` | The base neutral toggle — option rows next to a primary commit. |
|
|
102
|
+
| `accent` | `transparent` | `sys.color.background.primary` | One option per row that needs commit-rank emphasis. |
|
|
103
|
+
| `inverse` | `transparent` | `sys.color.text.inverse` | Inside an inverse host (Toast, coach-mark, snackbar). |
|
|
106
104
|
|
|
107
105
|
## Slots
|
|
108
106
|
|
|
@@ -92,19 +92,19 @@
|
|
|
92
92
|
"default": {
|
|
93
93
|
"background": "transparent",
|
|
94
94
|
"border": null,
|
|
95
|
-
"label": "sys.color.
|
|
95
|
+
"label": "sys.color.text.subtle",
|
|
96
96
|
"note": "The base neutral inline toggle — the canonical Check Button. Quiet enough to live next to typographic content without claiming commit-rank attention."
|
|
97
97
|
},
|
|
98
98
|
"accent": {
|
|
99
99
|
"background": "transparent",
|
|
100
100
|
"border": null,
|
|
101
|
-
"label": "sys.color.
|
|
101
|
+
"label": "sys.color.text.link",
|
|
102
102
|
"note": "Brand-blue label for the inline toggle. Use sparingly — never two `accent` Check Buttons in the same row."
|
|
103
103
|
},
|
|
104
104
|
"inverse": {
|
|
105
105
|
"background": "transparent",
|
|
106
106
|
"border": null,
|
|
107
|
-
"label": "sys.color.
|
|
107
|
+
"label": "sys.color.text.inverse",
|
|
108
108
|
"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."
|
|
109
109
|
}
|
|
110
110
|
},
|
|
@@ -134,11 +134,11 @@
|
|
|
134
134
|
"layer": "::after overlay — position:absolute, inset:0, no reflow (DESIGN.md Focus ring composition)",
|
|
135
135
|
"innerCounterRing": {
|
|
136
136
|
"width": "sys.borderWidth.hairline",
|
|
137
|
-
"color": "sys.color.
|
|
137
|
+
"color": "sys.color.border.focused"
|
|
138
138
|
},
|
|
139
139
|
"outerRing": {
|
|
140
140
|
"width": "sys.borderWidth.thin",
|
|
141
|
-
"color": "sys.color.
|
|
141
|
+
"color": "sys.color.border.focused"
|
|
142
142
|
}
|
|
143
143
|
},
|
|
144
144
|
"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."
|
|
@@ -153,16 +153,14 @@
|
|
|
153
153
|
"focusIndicator": {
|
|
154
154
|
"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.",
|
|
155
155
|
"composition": "outward",
|
|
156
|
-
"compositionReason": "Action affordance with breathing room around it; the
|
|
156
|
+
"compositionReason": "Action affordance with breathing room around it; the 1px outward ring is reserved by the surrounding layout.",
|
|
157
157
|
"overlay": {
|
|
158
158
|
"color": "label",
|
|
159
159
|
"opacity": "sys.state.focus"
|
|
160
160
|
},
|
|
161
161
|
"ring": {
|
|
162
|
-
"
|
|
163
|
-
"
|
|
164
|
-
"insetWidth": "sys.borderWidth.hairline",
|
|
165
|
-
"insetColor": "sys.color.focusInset"
|
|
162
|
+
"width": "sys.borderWidth.hairline",
|
|
163
|
+
"color": "sys.color.border.focused"
|
|
166
164
|
},
|
|
167
165
|
"trigger": ":focus-visible (keyboard / programmatic focus, never plain mouse click)"
|
|
168
166
|
},
|
|
@@ -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": {
|
|
@@ -90,11 +90,11 @@
|
|
|
90
90
|
"layer": "::after overlay — position:absolute, inset:0, no reflow (DESIGN.md Focus ring composition)",
|
|
91
91
|
"innerCounterRing": {
|
|
92
92
|
"width": "sys.borderWidth.hairline",
|
|
93
|
-
"color": "sys.color.
|
|
93
|
+
"color": "sys.color.border.focused"
|
|
94
94
|
},
|
|
95
95
|
"outerRing": {
|
|
96
96
|
"width": "sys.borderWidth.thin",
|
|
97
|
-
"color": "sys.color.
|
|
97
|
+
"color": "sys.color.border.focused"
|
|
98
98
|
}
|
|
99
99
|
},
|
|
100
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."
|
|
@@ -103,22 +103,20 @@
|
|
|
103
103
|
"focusIndicator": {
|
|
104
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.",
|
|
105
105
|
"composition": "outward",
|
|
106
|
-
"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.",
|
|
107
107
|
"overlay": {
|
|
108
108
|
"color": "label",
|
|
109
109
|
"opacity": "sys.state.focus"
|
|
110
110
|
},
|
|
111
111
|
"ring": {
|
|
112
|
-
"
|
|
113
|
-
"
|
|
114
|
-
"insetWidth": "sys.borderWidth.hairline",
|
|
115
|
-
"insetColor": "sys.color.focusInset"
|
|
112
|
+
"width": "sys.borderWidth.hairline",
|
|
113
|
+
"color": "sys.color.border.focused"
|
|
116
114
|
},
|
|
117
115
|
"trigger": ":focus-visible (keyboard / programmatic focus, never plain mouse click)"
|
|
118
116
|
},
|
|
119
117
|
"forbidden": [
|
|
120
118
|
"more than one fab per screen — the FAB is the single canonical commit",
|
|
121
|
-
"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",
|
|
122
120
|
"destructive flavor on a FAB — destructive commits live inside Dialog / BottomSheet, never a floating commit",
|
|
123
121
|
"fab placed inline in flow — it floats over content, anchored bottom-right by the page shell"
|
|
124
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
|
|