@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
|
@@ -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
|
}
|
|
@@ -166,6 +166,25 @@
|
|
|
166
166
|
"opacity": "sys.state.pressed"
|
|
167
167
|
}
|
|
168
168
|
},
|
|
169
|
+
"focused": {
|
|
170
|
+
"overlay": {
|
|
171
|
+
"color": "label",
|
|
172
|
+
"opacity": "sys.state.focus"
|
|
173
|
+
},
|
|
174
|
+
"focusRing": {
|
|
175
|
+
"composition": "outward",
|
|
176
|
+
"layer": "::after overlay — position:absolute, inset:0, no reflow (DESIGN.md Focus ring composition)",
|
|
177
|
+
"innerCounterRing": {
|
|
178
|
+
"width": "sys.borderWidth.hairline",
|
|
179
|
+
"color": "sys.color.border.focused"
|
|
180
|
+
},
|
|
181
|
+
"outerRing": {
|
|
182
|
+
"width": "sys.borderWidth.thin",
|
|
183
|
+
"color": "sys.color.border.focused"
|
|
184
|
+
}
|
|
185
|
+
},
|
|
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."
|
|
187
|
+
},
|
|
169
188
|
"disabled": {
|
|
170
189
|
"overlay": null,
|
|
171
190
|
"containerOpacity": "sys.state.disabled",
|
|
@@ -176,16 +195,14 @@
|
|
|
176
195
|
"focusIndicator": {
|
|
177
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.",
|
|
178
197
|
"composition": "outward",
|
|
179
|
-
"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.",
|
|
180
199
|
"overlay": {
|
|
181
200
|
"color": "label",
|
|
182
201
|
"opacity": "sys.state.focus"
|
|
183
202
|
},
|
|
184
203
|
"ring": {
|
|
185
|
-
"
|
|
186
|
-
"
|
|
187
|
-
"insetWidth": "sys.borderWidth.hairline",
|
|
188
|
-
"insetColor": "sys.color.focusInset"
|
|
204
|
+
"width": "sys.borderWidth.hairline",
|
|
205
|
+
"color": "sys.color.border.focused"
|
|
189
206
|
},
|
|
190
207
|
"trigger": ":focus-visible (keyboard / programmatic focus, never plain mouse click)"
|
|
191
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
|
|
|
@@ -63,16 +63,16 @@
|
|
|
63
63
|
},
|
|
64
64
|
"selectionStates": {
|
|
65
65
|
"unselected": {
|
|
66
|
-
"background": "sys.color.primary",
|
|
67
|
-
"label": "sys.color.
|
|
66
|
+
"background": "sys.color.background.primary",
|
|
67
|
+
"label": "sys.color.text.onFill",
|
|
68
68
|
"border": null
|
|
69
69
|
},
|
|
70
70
|
"selected": {
|
|
71
71
|
"background": "transparent",
|
|
72
|
-
"label": "sys.color.
|
|
72
|
+
"label": "sys.color.text.default",
|
|
73
73
|
"border": {
|
|
74
74
|
"width": "sys.borderWidth.hairline",
|
|
75
|
-
"color": "sys.color.
|
|
75
|
+
"color": "sys.color.border.default"
|
|
76
76
|
}
|
|
77
77
|
}
|
|
78
78
|
},
|
|
@@ -92,6 +92,25 @@
|
|
|
92
92
|
"opacity": "sys.state.pressed"
|
|
93
93
|
}
|
|
94
94
|
},
|
|
95
|
+
"focused": {
|
|
96
|
+
"overlay": {
|
|
97
|
+
"color": "label",
|
|
98
|
+
"opacity": "sys.state.focus"
|
|
99
|
+
},
|
|
100
|
+
"focusRing": {
|
|
101
|
+
"composition": "outward",
|
|
102
|
+
"layer": "::after overlay — position:absolute, inset:0, no reflow (DESIGN.md Focus ring composition)",
|
|
103
|
+
"innerCounterRing": {
|
|
104
|
+
"width": "sys.borderWidth.hairline",
|
|
105
|
+
"color": "sys.color.border.focused"
|
|
106
|
+
},
|
|
107
|
+
"outerRing": {
|
|
108
|
+
"width": "sys.borderWidth.thin",
|
|
109
|
+
"color": "sys.color.border.focused"
|
|
110
|
+
}
|
|
111
|
+
},
|
|
112
|
+
"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."
|
|
113
|
+
},
|
|
95
114
|
"disabled": {
|
|
96
115
|
"overlay": null,
|
|
97
116
|
"containerOpacity": "sys.state.disabled",
|
|
@@ -102,21 +121,19 @@
|
|
|
102
121
|
"focusIndicator": {
|
|
103
122
|
"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.",
|
|
104
123
|
"composition": "outward",
|
|
105
|
-
"compositionReason": "Action affordance with breathing room around it; the
|
|
124
|
+
"compositionReason": "Action affordance with breathing room around it; the 1px outward ring is reserved by the surrounding layout.",
|
|
106
125
|
"overlay": {
|
|
107
126
|
"color": "label",
|
|
108
127
|
"opacity": "sys.state.focus"
|
|
109
128
|
},
|
|
110
129
|
"ring": {
|
|
111
|
-
"
|
|
112
|
-
"
|
|
113
|
-
"insetWidth": "sys.borderWidth.hairline",
|
|
114
|
-
"insetColor": "sys.color.focusInset"
|
|
130
|
+
"width": "sys.borderWidth.hairline",
|
|
131
|
+
"color": "sys.color.border.focused"
|
|
115
132
|
},
|
|
116
133
|
"trigger": ":focus-visible (keyboard / programmatic focus, never plain mouse click)"
|
|
117
134
|
},
|
|
118
135
|
"forbidden": [
|
|
119
|
-
"active state painted with sys.color.primary fill — active is transparent + hairline outline (the active state recedes, not asserts)",
|
|
136
|
+
"active state painted with sys.color.background.primary fill — active is transparent + hairline outline (the active state recedes, not asserts)",
|
|
120
137
|
"active state painted with any opaque surface fill (surface, surfaceContainer, surfaceContainerHigh) — the committed form is transparent so the host surface shows through; do not re-bind to a tier'd fill",
|
|
121
138
|
"rest state without an explicit `active={false}` — toggle is a binary contract, never tristate",
|
|
122
139
|
"manual width override that breaks the full-card stretch when used inside ProfileCarousel.followAction"
|
|
@@ -22,9 +22,7 @@ import { Button } from '@teamblind-chorus/ui';
|
|
|
22
22
|
</Button>
|
|
23
23
|
```
|
|
24
24
|
|
|
25
|
-
##
|
|
26
|
-
|
|
27
|
-
### Accent
|
|
25
|
+
## Accent
|
|
28
26
|
|
|
29
27
|
Brand-blue fill, `onPrimary` label — the single-commit form. Used when the Toolbar Button IS the surface's commit affordance.
|
|
30
28
|
|
|
@@ -38,7 +36,7 @@ import { Button } from '@teamblind-chorus/ui';
|
|
|
38
36
|
</Button>
|
|
39
37
|
```
|
|
40
38
|
|
|
41
|
-
|
|
39
|
+
## Inverse
|
|
42
40
|
|
|
43
41
|
Mirror for inverse hosts (snackbars, coach-mark surfaces). Geometry identical; colour pair flips.
|
|
44
42
|
|
|
@@ -52,7 +50,7 @@ import { Button } from '@teamblind-chorus/ui';
|
|
|
52
50
|
</Button>
|
|
53
51
|
```
|
|
54
52
|
|
|
55
|
-
|
|
53
|
+
## Leading icon
|
|
56
54
|
|
|
57
55
|
Context glyph before the label — tag for *Filters*, calendar for *Pick date*.
|
|
58
56
|
|
|
@@ -70,7 +68,7 @@ import { PlusIcon } from '@teamblind-chorus/ui/icons';
|
|
|
70
68
|
</Button>
|
|
71
69
|
```
|
|
72
70
|
|
|
73
|
-
|
|
71
|
+
## Trailing icon
|
|
74
72
|
|
|
75
73
|
Directional/destination glyph — chevron-down to open a menu, "×" to clear. Unlike standard [Button](./button.md), Toolbar Button carries trailing icons because its role is often *trigger* rather than commit.
|
|
76
74
|
|
|
@@ -88,7 +86,7 @@ import { CheckedIcon } from '@teamblind-chorus/ui/icons';
|
|
|
88
86
|
</Button>
|
|
89
87
|
```
|
|
90
88
|
|
|
91
|
-
|
|
89
|
+
## Icon only
|
|
92
90
|
|
|
93
91
|
Glyph-only 32×32 square. Requires `aria-label`. When the label slot is absent, inline padding drops to `sys.layout.container.xs` (8) so the glyph centers.
|
|
94
92
|
|
|
@@ -105,7 +103,22 @@ import { PlusIcon } from '@teamblind-chorus/ui/icons';
|
|
|
105
103
|
/>
|
|
106
104
|
```
|
|
107
105
|
|
|
108
|
-
|
|
106
|
+
## Focus ring
|
|
107
|
+
|
|
108
|
+
Hairline stroke is kept underneath the standard ring.
|
|
109
|
+
|
|
110
|
+
```preview
|
|
111
|
+
button/toolbar/focused
|
|
112
|
+
---
|
|
113
|
+
import { Button } from '@teamblind-chorus/ui';
|
|
114
|
+
import { PlusIcon } from '@teamblind-chorus/ui/icons';
|
|
115
|
+
|
|
116
|
+
<Button variant="toolbar" state="focused" leadingIcon={<PlusIcon />}>
|
|
117
|
+
Add row
|
|
118
|
+
</Button>
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
## Group
|
|
109
122
|
|
|
110
123
|
Adjacent Toolbar Buttons share `4px` gap (`sys.layout.inline.sm`) with Filter chips. Mix freely.
|
|
111
124
|
|
|
@@ -128,21 +141,6 @@ import { PlusIcon, CheckedIcon } from '@teamblind-chorus/ui/icons';
|
|
|
128
141
|
</div>
|
|
129
142
|
```
|
|
130
143
|
|
|
131
|
-
### Focus indicator
|
|
132
|
-
|
|
133
|
-
Hairline stroke is kept underneath the standard ring.
|
|
134
|
-
|
|
135
|
-
```preview
|
|
136
|
-
button/toolbar/focused
|
|
137
|
-
---
|
|
138
|
-
import { Button } from '@teamblind-chorus/ui';
|
|
139
|
-
import { PlusIcon } from '@teamblind-chorus/ui/icons';
|
|
140
|
-
|
|
141
|
-
<Button variant="toolbar" state="focused" leadingIcon={<PlusIcon />}>
|
|
142
|
-
Add row
|
|
143
|
-
</Button>
|
|
144
|
-
```
|
|
145
|
-
|
|
146
144
|
## Slots
|
|
147
145
|
|
|
148
146
|
- **label** — accessible name. Required, single line.
|
|
@@ -155,9 +153,9 @@ Only the container ↔ label colour pair flips; geometry identical. For inline d
|
|
|
155
153
|
|
|
156
154
|
| Appearance | Background | Border | Label / icon | When to reach for it |
|
|
157
155
|
|-------------|-------------------------------------|--------------------------------------------------|------------------------------------|----------------------|
|
|
158
|
-
| `default` | `sys.color.surfaceContainerHigh` | 1px `sys.color.
|
|
159
|
-
| `accent` | `sys.color.primary` | none | `sys.color.
|
|
160
|
-
| `inverse` | `sys.color.
|
|
156
|
+
| `default` | `sys.color.surfaceContainerHigh` | 1px `sys.color.border.default` | `sys.color.text.default` | Quiet inline action — toolbar opener, "Edit" beside a row title. |
|
|
157
|
+
| `accent` | `sys.color.background.primary` | none | `sys.color.text.onFill` | The single commit — a [Page](../navigation-bar/sub.md) bar's "Save". Never two in a row. |
|
|
158
|
+
| `inverse` | `sys.color.background.inverse` | none | `sys.color.text.inverse` | Inside an inverse host (Toast, coach-mark). |
|
|
161
159
|
|
|
162
160
|
## Sizes
|
|
163
161
|
|
|
@@ -61,23 +61,23 @@
|
|
|
61
61
|
},
|
|
62
62
|
"appearances": {
|
|
63
63
|
"default": {
|
|
64
|
-
"background": "sys.color.
|
|
65
|
-
"label": "sys.color.
|
|
64
|
+
"background": "sys.color.surface.default",
|
|
65
|
+
"label": "sys.color.text.default",
|
|
66
66
|
"border": {
|
|
67
67
|
"width": "sys.borderWidth.hairline",
|
|
68
|
-
"color": "sys.color.
|
|
68
|
+
"color": "sys.color.border.default"
|
|
69
69
|
},
|
|
70
70
|
"note": "The canonical Toolbar Button chrome (mirrors Filter chip's unselected state). The chip-shape signals an inline action that lives within a row of peers (a toolbar, a card footer, a Page bar where the affordance is one of several actions the user might pick)."
|
|
71
71
|
},
|
|
72
72
|
"accent": {
|
|
73
|
-
"background": "sys.color.primary",
|
|
74
|
-
"label": "sys.color.
|
|
73
|
+
"background": "sys.color.background.primary",
|
|
74
|
+
"label": "sys.color.text.onFill",
|
|
75
75
|
"border": null,
|
|
76
76
|
"note": "Brand-blue commit chip — reach for it when the Toolbar Button IS the surface's commit affordance (a Page bar's 'Save', a sheet's 'Confirm', a flow step's 'Done'). The accent fill claims commit-rank attention while keeping the 32px Toolbar Button footprint, so the bar reads at one density even though the action is the screen's most important. Pair sparingly — never two accent Toolbar Buttons in the same row."
|
|
77
77
|
},
|
|
78
78
|
"inverse": {
|
|
79
|
-
"background": "sys.color.
|
|
80
|
-
"label": "sys.color.
|
|
79
|
+
"background": "sys.color.background.inverse",
|
|
80
|
+
"label": "sys.color.text.inverse",
|
|
81
81
|
"border": null,
|
|
82
82
|
"note": "Inverse-toned chip for use inside an inverse host (snackbars, coach-mark surfaces, dark coach overlays). Same chrome geometry as `default`; the colour pair flips to the inverse cluster so the chip reads against the host's `inverseSurface` fill without a per-host tweak."
|
|
83
83
|
}
|
|
@@ -88,16 +88,14 @@
|
|
|
88
88
|
"focusIndicator": {
|
|
89
89
|
"description": "Keyboard-focus visual — an accessibility indicator, not a lifecycle state. Composes over whichever lifecycle state the button is in. Visual ring is delegated to Filter chip's composition; this block restates the contract for external readers.",
|
|
90
90
|
"composition": "outward",
|
|
91
|
-
"compositionReason": "Action affordance with breathing room around it; the
|
|
91
|
+
"compositionReason": "Action affordance with breathing room around it; the 1px outward ring is reserved by the surrounding toolbar row.",
|
|
92
92
|
"overlay": {
|
|
93
93
|
"color": "label",
|
|
94
94
|
"opacity": "sys.state.focus"
|
|
95
95
|
},
|
|
96
96
|
"ring": {
|
|
97
|
-
"
|
|
98
|
-
"
|
|
99
|
-
"insetWidth": "sys.borderWidth.hairline",
|
|
100
|
-
"insetColor": "sys.color.focusInset"
|
|
97
|
+
"width": "sys.borderWidth.hairline",
|
|
98
|
+
"color": "sys.color.border.focused"
|
|
101
99
|
},
|
|
102
100
|
"trigger": ":focus-visible (keyboard / programmatic focus, never plain mouse click)"
|
|
103
101
|
},
|
|
@@ -19,7 +19,7 @@ Both subs share the same pager geometry — a `ref.space.500` (40px) trailing pe
|
|
|
19
19
|
|
|
20
20
|
Every Carousel paints a [Header](../header/header.md) (`size="large"`) at the top:
|
|
21
21
|
|
|
22
|
-
- **Label** *(required)* — `sys.typo.heading.md` / Semibold / `sys.color.
|
|
22
|
+
- **Label** *(required)* — `sys.typo.heading.md` / Semibold / `sys.color.text.default`. Leading position.
|
|
23
23
|
- **headerAction** *(optional)* — trailing [Text Button](../button/text.md) (`size={'xsmall'}`, `appearance={'accent'}`) per the link-affordance rule. Extends the header when there's an index page to route to.
|
|
24
24
|
|
|
25
25
|
Carousel forwards `label` and `headerAction` to [Header](../header/header.md) verbatim; the header anatomy lives in Header's spec. Other hosts (in-sheet sub-sections, bounded cards, [SuggestionList](../suggestion-list/suggestion-list.md)) reach for `<Header />` directly — Carousel is the labelled-region host, Header the leading-row primitive it composes.
|
|
@@ -55,9 +55,7 @@ import { Carousel, PostCarousel } from '@teamblind-chorus/ui';
|
|
|
55
55
|
</Carousel>
|
|
56
56
|
```
|
|
57
57
|
|
|
58
|
-
##
|
|
59
|
-
|
|
60
|
-
### With header action
|
|
58
|
+
## Header action
|
|
61
59
|
|
|
62
60
|
Extend the header with a trailing `accent` Text Button when there's an index page to route to. Lifts the `headerAction` prop on the `<Carousel>` wrapper.
|
|
63
61
|
|
|
@@ -69,17 +67,15 @@ import { Carousel, PostCarousel } from '@teamblind-chorus/ui';
|
|
|
69
67
|
<Carousel label="Trending right now" headerAction={{ label: 'See all', href: '#' }}>
|
|
70
68
|
<PostCarousel
|
|
71
69
|
items={[
|
|
72
|
-
{ avatar: { src: '/placeholder.png', alt: 'Channel' }, channel: 'Engineering',
|
|
73
|
-
{ avatar: { src: '/placeholder.png', alt: 'Channel' }, channel: 'Compensation', verified: true, followAction: true, title: 'Equity refresh negotiations — what actually moves', body: 'A read on the conversations that get an actual refresh on the calendar versus the ones that get a polite no.', mention: '@career', views: '9K' },
|
|
74
|
-
{ avatar: { src: '/placeholder.png', alt: 'Channel' }, channel: 'Plant People', verified: false, followAction: true, title: 'Monstera dropping aerial roots — repot or train?', body: 'Two-year-old monstera, roots crawling out of the drainage holes. Light and watering are dialed in.', mention: '@plant-parents', views: '3K' },
|
|
70
|
+
{ avatar: { src: '/placeholder.png', alt: 'Channel' }, channel: 'Engineering', verified: true, followAction: true, title: 'The migration that finally landed after three quarters', body: 'Internal postmortem turned editorial — the scaffolding that held the rewrite together when the timeline did not.', mention: '@infra-talk', views: '14K' },
|
|
75
71
|
]}
|
|
76
72
|
/>
|
|
77
73
|
</Carousel>
|
|
78
74
|
```
|
|
79
75
|
|
|
80
|
-
|
|
76
|
+
## Editorial card
|
|
81
77
|
|
|
82
|
-
|
|
78
|
+
The card drops `verified` and `followAction` — its header collapses to avatar + channel name. Reach for it on editorial collections where the card is informational only (round-ups, archives, *what we're reading*); the surface should not invite a per-card commit.
|
|
83
79
|
|
|
84
80
|
```preview
|
|
85
81
|
carousel/post-editorial
|
|
@@ -89,9 +85,7 @@ import { Carousel, PostCarousel } from '@teamblind-chorus/ui';
|
|
|
89
85
|
<Carousel label="Editor picks">
|
|
90
86
|
<PostCarousel
|
|
91
87
|
items={[
|
|
92
|
-
{ avatar: { src: '/placeholder.png', alt: 'Channel' }, channel: 'Career',
|
|
93
|
-
{ avatar: { src: '/placeholder.png', alt: 'Channel' }, channel: 'Compensation', title: 'Equity refresh negotiations — what actually moves', body: 'A read on the conversations that get an actual refresh on the calendar versus the ones that get a polite no.', views: '9K' },
|
|
94
|
-
{ avatar: { src: '/placeholder.png', alt: 'Channel' }, channel: 'Engineering', title: 'The migration that finally landed after three quarters', body: 'Internal postmortem turned editorial — the scaffolding that held the rewrite together when the timeline did not.', views: '14K' },
|
|
88
|
+
{ avatar: { src: '/placeholder.png', alt: 'Channel' }, channel: 'Career', title: 'The quiet math of staying versus leaving', body: 'Salary checks, offer evaluations, and the long thread that runs longer than any single conversation can.', views: '18K' },
|
|
95
89
|
]}
|
|
96
90
|
/>
|
|
97
91
|
</Carousel>
|
|
@@ -103,12 +97,12 @@ import { Carousel, PostCarousel } from '@teamblind-chorus/ui';
|
|
|
103
97
|
- **pager** — horizontal scroll-snap track. `scroll-snap-type: x mandatory`; native scrollbar hidden.
|
|
104
98
|
- **card** — one compact post card per page; outline-only surface.
|
|
105
99
|
- **avatar** — [Thumbnail](../thumbnail/thumbnail.md) `size={40}`, every prop (`src`, `alt`, `updateDot`, `logoBadge`) forwarded verbatim.
|
|
106
|
-
- **verified** *(optional)* — `VerifiedFillIcon` (`sys.icon.md`, `sys.color.primary`) to the LEFT of the channel name so the trust signal reads first.
|
|
107
|
-
- **channel** — channel / author name. `sys.typo.label.md` / Semibold / `sys.color.
|
|
108
|
-
- **followAction** *(optional)* — trailing [Text Button](../button/text.md) (`xsmall`). `accent` inactive (link-affordance) → `default` active (recedes). Same trailing-Text-Button shape as [List/entry](../list/entry.md)'s [attribution-row case](../list/entry.md#
|
|
109
|
-
- **title** — `sys.typo.label.md` / Semibold / `sys.color.
|
|
110
|
-
- **body** — `sys.typo.body.sm` / `sys.color.
|
|
111
|
-
- **mention** *(optional)* — `sys.typo.body.sm` / `sys.color.primary` (not italic).
|
|
100
|
+
- **verified** *(optional)* — `VerifiedFillIcon` (`sys.icon.md`, `sys.color.background.primary`) to the LEFT of the channel name so the trust signal reads first.
|
|
101
|
+
- **channel** — channel / author name. `sys.typo.label.md` / Semibold / `sys.color.text.default`.
|
|
102
|
+
- **followAction** *(optional)* — trailing [Text Button](../button/text.md) (`xsmall`). `accent` inactive (link-affordance) → `default` active (recedes). Same trailing-Text-Button shape as [List/entry](../list/entry.md)'s [attribution-row case](../list/entry.md#trailing-text-button) — see the Behavior note below.
|
|
103
|
+
- **title** — `sys.typo.label.md` / Semibold / `sys.color.text.default`. Single line, truncates.
|
|
104
|
+
- **body** — `sys.typo.body.sm` / `sys.color.text.subtle`. Three-line clamp.
|
|
105
|
+
- **mention** *(optional)* — `sys.typo.body.sm` / `sys.color.background.primary` (not italic).
|
|
112
106
|
- **footer** — leading 'See more' [Text Button](../button/text.md) (`xsmall` / `secondary`) + trailing view count (`EyeIcon` + count `<span>`).
|
|
113
107
|
- **pagination** — [Pagination](../pagination/pagination.md) component, one dot per card (`count` = card count, `activeIndex` from scroll position). Decorative — dot tokens and the `aria-hidden` contract live on its spec; the carousel centers the intrinsic-width row (`align-self: center`).
|
|
114
108
|
|
|
@@ -118,11 +112,11 @@ import { Carousel, PostCarousel } from '@teamblind-chorus/ui';
|
|
|
118
112
|
|-------------------|----------------|
|
|
119
113
|
| container | No fill / padding — surrounding [Carousel](../carousel/carousel.md) provides chrome; `sys.layout.stack.md` pager→dots gap |
|
|
120
114
|
| pager | `gap: sys.layout.inline.md`, `padding-left: sys.layout.container.md` + `scroll-padding-left: sys.layout.container.md` (the 16 rail; full-bleed host → trailing edge reached intrinsically, no negative margin), `scroll-snap-type: x mandatory`, `scrollbar-width: none` |
|
|
121
|
-
| card | `flex: 0 0 calc(100% - sys.layout.inline.md - ref.space.500)`; `sys.color.surface` fill, `sys.radius.md`, `sys.borderWidth.hairline sys.color.
|
|
115
|
+
| card | `flex: 0 0 calc(100% - sys.layout.inline.md - ref.space.500)`; `sys.color.surface` fill, `sys.radius.md`, `sys.borderWidth.hairline sys.color.border.default` outline (inset box-shadow), `sys.layout.container.md` padding, `sys.layout.stack.sm` between blocks, `scroll-snap-align: start` |
|
|
122
116
|
| header | Row: avatar + verified mark + name + spacer + follow action; `align-items: center`; `sys.layout.inline.md` gap |
|
|
123
|
-
| avatar / verified | [Thumbnail](../thumbnail/thumbnail.md) `size={40}` delegated verbatim · `VerifiedFillIcon` at `sys.icon.md` / `sys.color.primary` (resolves to `ref.palette.blue.500`), leading of the channel name |
|
|
124
|
-
| channel / title | `sys.typo.label.md`, `sys.color.
|
|
125
|
-
| body / mention | `sys.typo.body.sm` / `sys.color.
|
|
117
|
+
| avatar / verified | [Thumbnail](../thumbnail/thumbnail.md) `size={40}` delegated verbatim · `VerifiedFillIcon` at `sys.icon.md` / `sys.color.background.primary` (resolves to `ref.palette.blue.500`), leading of the channel name |
|
|
118
|
+
| channel / title | `sys.typo.label.md`, `sys.color.text.default`, single-line truncate |
|
|
119
|
+
| body / mention | `sys.typo.body.sm` / `sys.color.text.subtle` (three-line clamp) · mention in `sys.color.background.primary` |
|
|
126
120
|
| followAction | [Text Button](../button/text.md) `size={'xsmall'}`, `appearance={'accent'}` inactive → `appearance={'default'}` active; state tokens delegate to Text Button |
|
|
127
121
|
| footer | Row: leading 'See more' Text Button (`xsmall` / `secondary`) + trailing view count `<span>`; `justify-content: space-between` |
|
|
128
122
|
| pagination dot | [Pagination](../pagination/pagination.md) component delegated verbatim — 6 × 6 dots, active/inactive colors, and row gap bind on its spec |
|