@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
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
"slots": {
|
|
31
31
|
"container": {
|
|
32
32
|
"required": true,
|
|
33
|
-
"description": "Outer scroll surface. Vertical stack with a transparent fill (inherits parent container tone); rows separated by a 1px
|
|
33
|
+
"description": "Outer scroll surface. Vertical stack with a transparent fill (inherits parent container tone); rows separated by a 1px border.default divider, not a gap."
|
|
34
34
|
},
|
|
35
35
|
"row": {
|
|
36
36
|
"required": true,
|
|
@@ -114,17 +114,17 @@
|
|
|
114
114
|
"trailingActionGap": "sys.layout.inline.md",
|
|
115
115
|
"trailingActionGapNote": "Fixed 8px between the text group and a trailing nav chevron (the major-category `nav: true` case) — the family-wide trailing gap.",
|
|
116
116
|
"dividerWidth": "sys.borderWidth.hairline",
|
|
117
|
-
"dividerColor": "sys.color.
|
|
117
|
+
"dividerColor": "sys.color.border.default",
|
|
118
118
|
"labelTypo": "sys.typo.body.md",
|
|
119
|
-
"labelColor": "sys.color.
|
|
119
|
+
"labelColor": "sys.color.text.default",
|
|
120
120
|
"supportingTypo": "sys.typo.body.sm",
|
|
121
|
-
"supportingColor": "sys.color.
|
|
121
|
+
"supportingColor": "sys.color.text.subtle",
|
|
122
122
|
"supportingOffset": "0",
|
|
123
123
|
"leadingRadioSize": "24 × 24",
|
|
124
|
-
"leadingRadioColorRest": "sys.color.
|
|
125
|
-
"leadingRadioColorSelected": "sys.color.primary",
|
|
124
|
+
"leadingRadioColorRest": "sys.color.border.boldest",
|
|
125
|
+
"leadingRadioColorSelected": "sys.color.background.primary",
|
|
126
126
|
"navChevronSize": "16 × 16",
|
|
127
|
-
"navChevronColor": "sys.color.
|
|
127
|
+
"navChevronColor": "sys.color.text.subtle"
|
|
128
128
|
},
|
|
129
129
|
"states": {
|
|
130
130
|
"default": {
|
|
@@ -152,11 +152,11 @@
|
|
|
152
152
|
"layer": "::after/::before overlay — position:absolute, inset:0, inset box-shadow, no reflow (DESIGN.md Focus ring composition)",
|
|
153
153
|
"innerCounterRing": {
|
|
154
154
|
"width": "sys.borderWidth.hairline",
|
|
155
|
-
"color": "sys.color.
|
|
155
|
+
"color": "sys.color.border.focused"
|
|
156
156
|
},
|
|
157
157
|
"outerRing": {
|
|
158
158
|
"width": "sys.borderWidth.thin",
|
|
159
|
-
"color": "sys.color.
|
|
159
|
+
"color": "sys.color.border.focused"
|
|
160
160
|
}
|
|
161
161
|
},
|
|
162
162
|
"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 row is in; never via plain mouse click."
|
|
@@ -165,27 +165,23 @@
|
|
|
165
165
|
"leading": "Filled primary indicator; row foreground stays at onSurface. No fill change on the row itself."
|
|
166
166
|
},
|
|
167
167
|
"disabled": {
|
|
168
|
-
"
|
|
169
|
-
"
|
|
168
|
+
"text": "sys.color.text.disabled",
|
|
169
|
+
"icon": "sys.color.icon.disabled",
|
|
170
170
|
"pointerEvents": "none",
|
|
171
|
-
"note": "
|
|
171
|
+
"note": "Explicit disabled (no opacity): row text to text.disabled, radio indicator + icons to icon.disabled. Divider/focus overlay unaffected."
|
|
172
172
|
}
|
|
173
173
|
},
|
|
174
174
|
"focusIndicator": {
|
|
175
175
|
"description": "Keyboard-focus visual — an accessibility indicator, not a lifecycle state. The ring sits on the row, not on the leading indicator — the row is the keyboard target.",
|
|
176
176
|
"composition": "inward",
|
|
177
|
-
"compositionReason": "Rows tile the column flush with only a hairline `
|
|
177
|
+
"compositionReason": "Rows tile the column flush with only a hairline `border.default` divider between them; an outward ring would overlap the divider and the neighbouring row.",
|
|
178
178
|
"overlay": {
|
|
179
179
|
"color": "label",
|
|
180
180
|
"opacity": "sys.state.focus"
|
|
181
181
|
},
|
|
182
182
|
"ring": {
|
|
183
|
-
"
|
|
184
|
-
"
|
|
185
|
-
"outerLayerPosition": "depth 0..2px from the row edge (the outer stroke)",
|
|
186
|
-
"insetWidth": "sys.borderWidth.hairline",
|
|
187
|
-
"insetColor": "sys.color.focusInset",
|
|
188
|
-
"insetLayerPosition": "depth 2..3px from the row edge (the counter-ring just inside the outer stroke)",
|
|
183
|
+
"width": "sys.borderWidth.hairline",
|
|
184
|
+
"color": "sys.color.border.focused",
|
|
189
185
|
"implementation": "inset box-shadow on the row's `::before` overlay (the `::after` carries the inter-row divider). Constrained strictly inside the row's footprint and never exceeds it."
|
|
190
186
|
},
|
|
191
187
|
"trigger": ":focus-visible (keyboard / programmatic focus, never plain mouse click)"
|
|
@@ -200,6 +196,6 @@
|
|
|
200
196
|
"forbidden": [
|
|
201
197
|
"radio glyph as a separate hit area — the entire row is the click target",
|
|
202
198
|
"multi-select painted as radio — radio variant is single-select; use checkbox or chip/filter for multi-select",
|
|
203
|
-
"selected state painted with sys.color.
|
|
199
|
+
"selected state painted with sys.color.background.selected fill — radio selected paints the inner dot, not the row fill"
|
|
204
200
|
]
|
|
205
201
|
}
|
|
@@ -10,7 +10,7 @@ A row opts into an inline `count` Badge to the right of the label (4px / `sys.la
|
|
|
10
10
|
|
|
11
11
|
## Default
|
|
12
12
|
|
|
13
|
-
A
|
|
13
|
+
A single text row — the atomic List component. A row is a `value` + `label`, with an optional `supportingText` second line. The whole row is the click target.
|
|
14
14
|
|
|
15
15
|
```preview
|
|
16
16
|
list/standard
|
|
@@ -19,18 +19,12 @@ import { List } from '@teamblind-chorus/ui';
|
|
|
19
19
|
|
|
20
20
|
<List
|
|
21
21
|
items={[
|
|
22
|
-
{ value: 'profile',
|
|
23
|
-
{ value: 'notif', label: 'Notifications', supportingText: 'Email, push, in-app' },
|
|
24
|
-
{ value: 'privacy', label: 'Privacy', supportingText: 'Who can see your activity' },
|
|
25
|
-
{ value: 'language', label: 'Language' },
|
|
26
|
-
{ value: 'about', label: 'About' },
|
|
22
|
+
{ value: 'profile', label: 'Profile', supportingText: 'Display name, avatar, bio' },
|
|
27
23
|
]}
|
|
28
24
|
/>
|
|
29
25
|
```
|
|
30
26
|
|
|
31
|
-
##
|
|
32
|
-
|
|
33
|
-
### With trailing action
|
|
27
|
+
## Trailing action
|
|
34
28
|
|
|
35
29
|
A Text Button in the row's `trailingIcon` slot turns a display row into row + action — the row label stays informational (no `onClick` on the row), the trailing button is the only commit target. Reach for it on settings rows that pair a value with a small "change / edit / view" action.
|
|
36
30
|
|
|
@@ -52,21 +46,11 @@ import { Button, List } from '@teamblind-chorus/ui';
|
|
|
52
46
|
</Button>
|
|
53
47
|
),
|
|
54
48
|
},
|
|
55
|
-
{
|
|
56
|
-
value: 'sms',
|
|
57
|
-
label: 'SMS',
|
|
58
|
-
supportingText: '+1 (415) ***-2487',
|
|
59
|
-
trailingIcon: (
|
|
60
|
-
<Button variant="text" size="small" appearance="accent" onClick={() => {}}>
|
|
61
|
-
Edit
|
|
62
|
-
</Button>
|
|
63
|
-
),
|
|
64
|
-
},
|
|
65
49
|
]}
|
|
66
50
|
/>
|
|
67
51
|
```
|
|
68
52
|
|
|
69
|
-
|
|
53
|
+
## Inline count
|
|
70
54
|
|
|
71
55
|
Pass a `count` on a row and a Badge renders to the right of the label on the same line, separated by `4px` (`sys.layout.inline.sm`) — the unread / status-count case. The label shrinks first so a long label truncates against the count, which stays pinned at its intrinsic width. Unlike `trailingIcon`, the count tiles tight to the label inside the text group, so it **composes with the drill-in chevron**: a `nav: true` row carries the count by the label *and* keeps its trailing chevron — one row with both an icon and a badge.
|
|
72
56
|
|
|
@@ -77,16 +61,12 @@ import { List, Badge } from '@teamblind-chorus/ui';
|
|
|
77
61
|
|
|
78
62
|
<List
|
|
79
63
|
items={[
|
|
80
|
-
{ value: '
|
|
81
|
-
{ value: 'channels', label: 'My channels', supportingText: '12 joined · 3 muted', nav: true },
|
|
82
|
-
{ value: 'notif', label: 'Notifications', count: <Badge>3</Badge>, nav: true },
|
|
83
|
-
{ value: 'privacy', label: 'Privacy', nav: true },
|
|
84
|
-
{ value: 'account', label: 'Account', nav: true },
|
|
64
|
+
{ value: 'notif', label: 'Notifications', count: <Badge>3</Badge>, nav: true },
|
|
85
65
|
]}
|
|
86
66
|
/>
|
|
87
67
|
```
|
|
88
68
|
|
|
89
|
-
|
|
69
|
+
## Drill-in
|
|
90
70
|
|
|
91
71
|
Set `nav: true` on a row to auto-render a trailing right-pointing chevron — the drill-in affordance signalling the row routes to another surface. The whole row is the click target; the chevron is decorative. This is the canonical settings / menu navigation shape (it replaces the former `nav` variant). A per-item `trailingIcon` overrides the chevron for that row.
|
|
92
72
|
|
|
@@ -97,16 +77,12 @@ import { List } from '@teamblind-chorus/ui';
|
|
|
97
77
|
|
|
98
78
|
<List
|
|
99
79
|
items={[
|
|
100
|
-
{ value: 'profile',
|
|
101
|
-
{ value: 'channels', label: 'My channels', supportingText: '12 joined · 3 muted', nav: true },
|
|
102
|
-
{ value: 'notif', label: 'Notifications', nav: true },
|
|
103
|
-
{ value: 'privacy', label: 'Privacy', nav: true },
|
|
104
|
-
{ value: 'account', label: 'Account', nav: true },
|
|
80
|
+
{ value: 'profile', label: 'Profile', supportingText: 'Display name, avatar, bio', nav: true },
|
|
105
81
|
]}
|
|
106
82
|
/>
|
|
107
83
|
```
|
|
108
84
|
|
|
109
|
-
|
|
85
|
+
## Leading icon
|
|
110
86
|
|
|
111
87
|
Pass an `icon` on a row and a 24px (`sys.icon.lg`) glyph renders at the leading edge in `onSurfaceVariant`, `8px` (`sys.layout.inline.md`) from the text group — the category-mark shape for settings / menu rows, lighter than a 40px leading image (and mutually exclusive with `thumbnail`). The glyph is decorative (the label carries the meaning) and the slot enforces the 24 rung regardless of the glyph's own `size`. Every other slot stays optional, so the icon composes with `supportingText`, an inline `count`, and the drill-in chevron.
|
|
112
88
|
|
|
@@ -114,19 +90,16 @@ Pass an `icon` on a row and a 24px (`sys.icon.lg`) glyph renders at the leading
|
|
|
114
90
|
list/standard-icon-leading
|
|
115
91
|
---
|
|
116
92
|
import { List, Badge } from '@teamblind-chorus/ui';
|
|
117
|
-
import {
|
|
93
|
+
import { BellIcon } from '@teamblind-chorus/ui/icons';
|
|
118
94
|
|
|
119
95
|
<List
|
|
120
96
|
items={[
|
|
121
|
-
{ value: '
|
|
122
|
-
{ value: 'notif', label: 'Notifications', icon: <BellIcon />, count: <Badge>3</Badge>, nav: true },
|
|
123
|
-
{ value: 'saved', label: 'Saved', icon: <BookmarkIcon />, nav: true },
|
|
124
|
-
{ value: 'activity', label: 'Activity', icon: <PulseIcon />, nav: true },
|
|
97
|
+
{ value: 'notif', label: 'Notifications', icon: <BellIcon />, count: <Badge>3</Badge>, nav: true },
|
|
125
98
|
]}
|
|
126
99
|
/>
|
|
127
100
|
```
|
|
128
101
|
|
|
129
|
-
|
|
102
|
+
## Leading image
|
|
130
103
|
|
|
131
104
|
Pass a `thumbnail` on a row and a 40px [Thumbnail](../thumbnail/thumbnail.md) renders at the leading edge, vertically centred against the label column. `thumbnail` props (`src`, `alt`, `updateDot`, `logoBadge`) forward verbatim. The gap to the text group steps up to `12px` (`sys.layout.inline.lg`) so the avatar and the label column read as two distinct blocks. This is the channel / source / author row shape — same click semantics as a text row, no selection model.
|
|
132
105
|
|
|
@@ -137,14 +110,12 @@ import { List } from '@teamblind-chorus/ui';
|
|
|
137
110
|
|
|
138
111
|
<List
|
|
139
112
|
items={[
|
|
140
|
-
{ value: '
|
|
141
|
-
{ value: 'frontend', label: 'Frontend Friday', supportingText: 'Updated 1d ago', thumbnail: { alt: 'Frontend Friday' } },
|
|
142
|
-
{ value: 'changelog', label: 'Changelog', supportingText: 'Updated 3d ago', thumbnail: { alt: 'Changelog' } },
|
|
113
|
+
{ value: 'sourdough', label: 'Sourdough Bakers', supportingText: '3 new posts today', thumbnail: { alt: 'Sourdough Bakers' } },
|
|
143
114
|
]}
|
|
144
115
|
/>
|
|
145
116
|
```
|
|
146
117
|
|
|
147
|
-
|
|
118
|
+
## Leading image + trailing action
|
|
148
119
|
|
|
149
120
|
A Text Button in the row's `trailingIcon` slot — the canonical "directory row + small commit" composition. Reach for it on follow / join / invite rows where the leading Thumbnail anchors the entity and the trailing button is the only commit. Row body stays informational.
|
|
150
121
|
|
|
@@ -167,22 +138,11 @@ import { Button, List } from '@teamblind-chorus/ui';
|
|
|
167
138
|
</Button>
|
|
168
139
|
),
|
|
169
140
|
},
|
|
170
|
-
{
|
|
171
|
-
value: 'frontend',
|
|
172
|
-
label: 'Frontend',
|
|
173
|
-
supportingText: '892 colleagues following',
|
|
174
|
-
thumbnail: { alt: 'Frontend' },
|
|
175
|
-
trailingIcon: (
|
|
176
|
-
<Button variant="text" size="small" appearance="accent" onClick={() => {}}>
|
|
177
|
-
Follow
|
|
178
|
-
</Button>
|
|
179
|
-
),
|
|
180
|
-
},
|
|
181
141
|
]}
|
|
182
142
|
/>
|
|
183
143
|
```
|
|
184
144
|
|
|
185
|
-
|
|
145
|
+
## Leading image + drill-in
|
|
186
146
|
|
|
187
147
|
Set `nav: true` on a leading-image row for an avatar-anchored row that routes to another surface (channel → channel detail, person → profile). The whole row is the click target; the chevron is decorative. A per-item `trailingIcon` overrides the chevron.
|
|
188
148
|
|
|
@@ -193,14 +153,12 @@ import { List } from '@teamblind-chorus/ui';
|
|
|
193
153
|
|
|
194
154
|
<List
|
|
195
155
|
items={[
|
|
196
|
-
{ value: 'design-weekly', label: 'Design Weekly',
|
|
197
|
-
{ value: 'frontend', label: 'Frontend Friday', supportingText: '1.1k members', thumbnail: { alt: 'Frontend Friday' }, nav: true },
|
|
198
|
-
{ value: 'changelog', label: 'Changelog', supportingText: '840 members', thumbnail: { alt: 'Changelog' }, nav: true },
|
|
156
|
+
{ value: 'design-weekly', label: 'Design Weekly', supportingText: '2.3k members', thumbnail: { alt: 'Design Weekly' }, nav: true },
|
|
199
157
|
]}
|
|
200
158
|
/>
|
|
201
159
|
```
|
|
202
160
|
|
|
203
|
-
|
|
161
|
+
## Leading image, no divider
|
|
204
162
|
|
|
205
163
|
`divider: false` on a row suppresses its bottom hairline rule — useful when a visual group ends mid-stack and the divider would visually fence off the next group from its label. The row's footprint and inline padding stay unchanged.
|
|
206
164
|
|
|
@@ -218,17 +176,17 @@ import { List } from '@teamblind-chorus/ui';
|
|
|
218
176
|
/>
|
|
219
177
|
```
|
|
220
178
|
|
|
221
|
-
|
|
179
|
+
## Embedded Banner
|
|
222
180
|
|
|
223
181
|
Pass a `banner` on a row and a [Banner](../banner/banner.md) renders **below** the row's text group, `8px` (`sys.layout.stack.xs`) down, spanning the row's full content width (aligned to the same 16px inline inset as the label above it). The row flips from a single line to a vertical stack — text group on top, Banner underneath. Reach for it when a row needs an in-row call-out tied to *that row's* subject (a follow-up prompt, a capability nudge, a single-line CTA), rather than a separate full-width Banner detached from the row.
|
|
224
182
|
|
|
225
|
-
The Banner keeps its full prop surface — here `appearance="accent"` + `neutralBody` for the quiet-tint shape, a blue `CheckCircleFillIcon` leading the single-line body, and a `trailingAction` Text Button with a trailing chevron. The Banner is a nested-action region: its button never commits the row, and the row's hover / pressed overlay is suppressed over it. The
|
|
183
|
+
The Banner keeps its full prop surface — here `appearance="accent"` + `neutralBody` for the quiet-tint shape, a blue `CheckCircleFillIcon` leading the single-line body, and a `trailingAction` Text Button with a trailing chevron. The Banner is a nested-action region: its button never commits the row, and the row's hover / pressed overlay is suppressed over it. The row leads with a fill-type category glyph (`BriefcaseFillIcon`) that the leading slot tones to `onSurfaceVariant`.
|
|
226
184
|
|
|
227
185
|
```preview
|
|
228
186
|
list/standard-embedded-banner
|
|
229
187
|
---
|
|
230
188
|
import { Banner, Button, List } from '@teamblind-chorus/ui';
|
|
231
|
-
import { BriefcaseFillIcon, CheckCircleFillIcon, ChevronRightIcon
|
|
189
|
+
import { BriefcaseFillIcon, CheckCircleFillIcon, ChevronRightIcon } from '@teamblind-chorus/ui/icons';
|
|
232
190
|
|
|
233
191
|
<List
|
|
234
192
|
aria-label="Career"
|
|
@@ -242,7 +200,7 @@ import { BriefcaseFillIcon, CheckCircleFillIcon, ChevronRightIcon, FlagFillIcon
|
|
|
242
200
|
<Banner
|
|
243
201
|
appearance="accent"
|
|
244
202
|
neutralBody
|
|
245
|
-
icon={<CheckCircleFillIcon size={16} style={{ color: 'var(--sys-color-primary)' }} />}
|
|
203
|
+
icon={<CheckCircleFillIcon size={16} style={{ color: 'var(--sys-color-background-primary)' }} />}
|
|
246
204
|
trailingAction={(
|
|
247
205
|
<Button variant="text" appearance="accent" size="small" trailingIcon={<ChevronRightIcon />}>
|
|
248
206
|
Expert Q&A
|
|
@@ -253,11 +211,35 @@ import { BriefcaseFillIcon, CheckCircleFillIcon, ChevronRightIcon, FlagFillIcon
|
|
|
253
211
|
</Banner>
|
|
254
212
|
),
|
|
255
213
|
},
|
|
214
|
+
]}
|
|
215
|
+
/>
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
## Group
|
|
219
|
+
|
|
220
|
+
Several rows bundled into one `<List>`. Every row carries a leading icon and reaches for a different per-row variant — a drill-in row, a row with an inline `count` + drill-in chevron, a row with a trailing action — so the group reads as the realistic composition the single-variant sections below each isolate one facet of. Rows tile with a hairline divider between them; the last row drops its divider automatically (no `divider: false` needed).
|
|
221
|
+
|
|
222
|
+
```preview
|
|
223
|
+
list/standard-group
|
|
224
|
+
---
|
|
225
|
+
import { Badge, Button, List } from '@teamblind-chorus/ui';
|
|
226
|
+
import { BellIcon, InvitationIcon, ProfileIcon } from '@teamblind-chorus/ui/icons';
|
|
227
|
+
|
|
228
|
+
<List
|
|
229
|
+
aria-label="Account"
|
|
230
|
+
items={[
|
|
231
|
+
{ value: 'profile', label: 'Profile', supportingText: 'Display name, avatar, bio', icon: <ProfileIcon />, nav: true },
|
|
232
|
+
{ value: 'notif', label: 'Notifications', icon: <BellIcon />, count: <Badge>3</Badge>, nav: true },
|
|
256
233
|
{
|
|
257
|
-
value: '
|
|
258
|
-
label: '
|
|
259
|
-
|
|
260
|
-
|
|
234
|
+
value: 'email',
|
|
235
|
+
label: 'Email',
|
|
236
|
+
supportingText: 'work@example.com',
|
|
237
|
+
icon: <InvitationIcon />,
|
|
238
|
+
trailingIcon: (
|
|
239
|
+
<Button variant="text" size="small" appearance="accent" onClick={() => {}}>
|
|
240
|
+
Edit
|
|
241
|
+
</Button>
|
|
242
|
+
),
|
|
261
243
|
},
|
|
262
244
|
]}
|
|
263
245
|
/>
|
|
@@ -284,7 +266,7 @@ No `selected` state — selection belongs to the [Radio sub](./radio.md).
|
|
|
284
266
|
|
|
285
267
|
## Focus indicator
|
|
286
268
|
|
|
287
|
-
Inward
|
|
269
|
+
Inward single ring inside the row's bounds — see the family-wide [Focus indicator](./list.md#cross-sub-contract). The preview pins **My channels** to its focused state via `forcedState: 'focused'` for static inspection.
|
|
288
270
|
|
|
289
271
|
```preview
|
|
290
272
|
list/focus-indicator
|
|
@@ -293,11 +275,7 @@ import { List } from '@teamblind-chorus/ui';
|
|
|
293
275
|
|
|
294
276
|
<List
|
|
295
277
|
items={[
|
|
296
|
-
{ value: '
|
|
297
|
-
{ value: 'channels', label: 'My channels', supportingText: '12 joined · 3 muted', nav: true, forcedState: 'focused' },
|
|
298
|
-
{ value: 'notif', label: 'Notifications', nav: true },
|
|
299
|
-
{ value: 'privacy', label: 'Privacy', nav: true },
|
|
300
|
-
{ value: 'account', label: 'Account', nav: true },
|
|
278
|
+
{ value: 'channels', label: 'My channels', supportingText: '12 joined · 3 muted', nav: true, forcedState: 'focused' },
|
|
301
279
|
]}
|
|
302
280
|
/>
|
|
303
281
|
```
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
"slots": {
|
|
21
21
|
"container": {
|
|
22
22
|
"required": true,
|
|
23
|
-
"description": "Outer scroll surface. Vertical stack with a transparent fill (inherits parent container tone); rows separated by a 1px
|
|
23
|
+
"description": "Outer scroll surface. Vertical stack with a transparent fill (inherits parent container tone); rows separated by a 1px border.default divider, not a gap."
|
|
24
24
|
},
|
|
25
25
|
"row": {
|
|
26
26
|
"required": true,
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
},
|
|
29
29
|
"leading": {
|
|
30
30
|
"required": false,
|
|
31
|
-
"fallbackOnMissingSrc": "sys.color.
|
|
31
|
+
"fallbackOnMissingSrc": "sys.color.surface.sunken",
|
|
32
32
|
"description": "The leading slot — omitted by default (no reserved leading whitespace), hosting one of two mutually exclusive types. **Image type** (`thumbnail`): a [Thumbnail](../thumbnail/thumbnail.md) at the 40 rung, vertically centred against the label column, with a 12px (`sys.layout.inline.lg`) gap to the text group; `thumbnail` props (`src`, `alt`, `updateDot`, `logoBadge`) forward verbatim, and `fallbackOnMissingSrc` is the dim-tone fill it paints when `src` is empty / fails to load (at scaffold time, agents fill `src` with `/placeholder.png` rather than relying on the fallback). **Icon type** (`icon`): a 24px (`sys.icon.lg`) decorative glyph in `onSurfaceVariant`, vertically centred, with an 8px (`sys.layout.inline.md`) gap to the text group — the category-mark shape for settings / menu rows. The slot enforces the 24 rung regardless of the glyph's own `size`. A row passes `thumbnail` or `icon`, never both.",
|
|
33
33
|
"accepts": [
|
|
34
34
|
"thumbnail",
|
|
@@ -117,7 +117,7 @@
|
|
|
117
117
|
"divider": {
|
|
118
118
|
"type": "boolean",
|
|
119
119
|
"default": true,
|
|
120
|
-
"description": "Per-row bottom-divider opt-out. Pass `divider: false` to suppress the hairline `
|
|
120
|
+
"description": "Per-row bottom-divider opt-out. Pass `divider: false` to suppress the hairline `border.default` rule beneath the row; the row's footprint and inline padding stay unchanged. Reach for it when a visual group ends mid-stack and the divider would visually fence off the next group from its label. The last row already omits its divider via `:not(:last-child)`."
|
|
121
121
|
},
|
|
122
122
|
"nav": {
|
|
123
123
|
"type": "boolean",
|
|
@@ -164,18 +164,18 @@
|
|
|
164
164
|
"labelToCountGap": "sys.layout.inline.sm",
|
|
165
165
|
"labelToCountGapNote": "4px (`sys.layout.inline.sm`) between the label and an inline `count` badge — they tile flush on the primary line as one label+count block, narrower than the 8px trailing gap. Mirrors list/entry's identity-group spacing.",
|
|
166
166
|
"dividerWidth": "sys.borderWidth.hairline",
|
|
167
|
-
"dividerColor": "sys.color.
|
|
167
|
+
"dividerColor": "sys.color.border.default",
|
|
168
168
|
"dividerPerRowOptOut": "Pass `divider: false` on a row to suppress its bottom divider.",
|
|
169
169
|
"leadingThumbnailSize": 40,
|
|
170
170
|
"labelTypo": "sys.typo.body.md",
|
|
171
|
-
"labelColor": "sys.color.
|
|
171
|
+
"labelColor": "sys.color.text.default",
|
|
172
172
|
"supportingTypo": "sys.typo.body.sm",
|
|
173
|
-
"supportingColor": "sys.color.
|
|
173
|
+
"supportingColor": "sys.color.text.subtle",
|
|
174
174
|
"supportingOffset": "0",
|
|
175
175
|
"trailingIconSize": "16 × 16",
|
|
176
|
-
"trailingIconColor": "sys.color.
|
|
176
|
+
"trailingIconColor": "sys.color.text.subtle",
|
|
177
177
|
"navChevronSize": "16 × 16",
|
|
178
|
-
"navChevronColor": "sys.color.
|
|
178
|
+
"navChevronColor": "sys.color.text.subtle"
|
|
179
179
|
},
|
|
180
180
|
"states": {
|
|
181
181
|
"default": {
|
|
@@ -203,37 +203,34 @@
|
|
|
203
203
|
"layer": "::after/::before overlay — position:absolute, inset:0, inset box-shadow, no reflow (DESIGN.md Focus ring composition)",
|
|
204
204
|
"innerCounterRing": {
|
|
205
205
|
"width": "sys.borderWidth.hairline",
|
|
206
|
-
"color": "sys.color.
|
|
206
|
+
"color": "sys.color.border.focused"
|
|
207
207
|
},
|
|
208
208
|
"outerRing": {
|
|
209
209
|
"width": "sys.borderWidth.thin",
|
|
210
|
-
"color": "sys.color.
|
|
210
|
+
"color": "sys.color.border.focused"
|
|
211
211
|
}
|
|
212
212
|
},
|
|
213
213
|
"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 row is in; never via plain mouse click."
|
|
214
214
|
},
|
|
215
215
|
"nestedActionScope": "The hover / pressed overlay is suppressed while the pointer sits on an independent trailing action (a `trailingIcon` button — favorite / mute / Follow). The small control owns the state; the large row does NOT also read as hovered / pressed. The decorative nav chevron is exempt — it is the row's own drill-in affordance, so hovering it still lights the whole row. The visual-state boundary matches the event boundary (the trailing action already stops propagation).",
|
|
216
216
|
"disabled": {
|
|
217
|
-
"
|
|
218
|
-
"
|
|
219
|
-
"pointerEvents": "none"
|
|
217
|
+
"text": "sys.color.text.disabled",
|
|
218
|
+
"icon": "sys.color.icon.disabled",
|
|
219
|
+
"pointerEvents": "none",
|
|
220
|
+
"note": "Explicit disabled (no opacity): row text to text.disabled, icons to icon.disabled. The inter-row divider and focus overlay are unaffected (they were never part of the content tone)."
|
|
220
221
|
}
|
|
221
222
|
},
|
|
222
223
|
"focusIndicator": {
|
|
223
224
|
"description": "Keyboard-focus visual — an accessibility indicator, not a lifecycle state. Composes over whichever lifecycle state the row is in.",
|
|
224
225
|
"composition": "inward",
|
|
225
|
-
"compositionReason": "Rows tile the column flush with only a hairline `
|
|
226
|
+
"compositionReason": "Rows tile the column flush with only a hairline `border.default` divider between them; an outward ring would overlap the divider and the neighbouring row.",
|
|
226
227
|
"overlay": {
|
|
227
228
|
"color": "label",
|
|
228
229
|
"opacity": "sys.state.focus"
|
|
229
230
|
},
|
|
230
231
|
"ring": {
|
|
231
|
-
"
|
|
232
|
-
"
|
|
233
|
-
"outerLayerPosition": "depth 0..2px from the row edge (the outer stroke)",
|
|
234
|
-
"insetWidth": "sys.borderWidth.hairline",
|
|
235
|
-
"insetColor": "sys.color.focusInset",
|
|
236
|
-
"insetLayerPosition": "depth 2..3px from the row edge (the counter-ring just inside the outer stroke)",
|
|
232
|
+
"width": "sys.borderWidth.hairline",
|
|
233
|
+
"color": "sys.color.border.focused",
|
|
237
234
|
"implementation": "inset box-shadow on the row's `::before` overlay (the `::after` carries the inter-row divider). Constrained strictly inside the row's footprint and never exceeds it."
|
|
238
235
|
},
|
|
239
236
|
"trigger": ":focus-visible (keyboard / programmatic focus, never plain mouse click)"
|
|
@@ -250,7 +247,7 @@
|
|
|
250
247
|
"nav chevron as a separate hit target — the drill-in chevron is decorative; the whole row is the click target",
|
|
251
248
|
"leading thumbnail at a size other than the row's intrinsic 40 rung",
|
|
252
249
|
"compact directory rows (selectable 32/48/56 avatar + stacked secondary line + optional toggle) built as a leading-image Standard row — that directory anatomy is [list/entry](./entry.md); Standard's image type is single-density at the 40 rung. (Standard does carry an inline `count` badge next to the label for unread / status counts, but not the avatar-rung directory shape.)",
|
|
253
|
-
"raw `border:` on the row — list seam is the family's bottom divider via
|
|
250
|
+
"raw `border:` on the row — list seam is the family's bottom divider via border.default",
|
|
254
251
|
"embedded `banner` painted with a per-child `margin-block` / `padding-block` wrapper to fake the 8px gap — the text-group↔Banner gap is the row stack's `gap: sys.layout.stack.xs`, and the Banner's horizontal inset is the row's own 16px padding; Banner ships no outer margin",
|
|
255
252
|
"embedded `banner` as a separate `<List>` row beneath the header — the call-out belongs to its row's subject, so it nests inside that row via `item.banner`, not as a sibling row that the divider would fence off"
|
|
256
253
|
]
|
|
@@ -25,9 +25,7 @@ import { Metadata } from '@teamblind-chorus/ui';
|
|
|
25
25
|
/>
|
|
26
26
|
```
|
|
27
27
|
|
|
28
|
-
##
|
|
29
|
-
|
|
30
|
-
### With role badge
|
|
28
|
+
## Role badge
|
|
31
29
|
|
|
32
30
|
An object identity item carries `badge` — a single presentational mark rendered after the nickname's link, outside the `<a>`, at the middots' 4px gap. At most one badge rides the nickname, never a stack.
|
|
33
31
|
|
|
@@ -59,9 +57,9 @@ import { Metadata, Badge } from '@teamblind-chorus/ui';
|
|
|
59
57
|
| Slot | Token bindings |
|
|
60
58
|
|---------------|----------------|
|
|
61
59
|
| avatar | [Thumbnail](../thumbnail/thumbnail.md) `size={32}` |
|
|
62
|
-
| meta | `sys.typo.label.sm` / `sys.color.
|
|
63
|
-
| timestamp | `sys.typo.label.sm` / `sys.color.
|
|
64
|
-
| dot separator | `·` glyph, `color: sys.color.
|
|
60
|
+
| meta | `sys.typo.label.sm` / `sys.color.text.subtle`, links inherit; underline on hover |
|
|
61
|
+
| timestamp | `sys.typo.label.sm` / `sys.color.border.boldest` |
|
|
62
|
+
| dot separator | `·` glyph, `color: sys.color.border.boldest`, **`line-height: 1`** so its line-box equals its font-size — never inflates the text line |
|
|
65
63
|
|
|
66
64
|
## States
|
|
67
65
|
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"name": "Metadata",
|
|
4
4
|
"family": "metadata",
|
|
5
5
|
"subcomponent": "compact",
|
|
6
|
-
"description": "One-line channel-detail attribution — the slimmed [Standard](standard.md) head that keeps the leading 32-rung [Thumbnail](../thumbnail/thumbnail.md) but drops the primary name line, follow toggle, and subtitle (the info that is unnecessary once the channel context is already established). A leading avatar plus the identity meta-link row, with the posting time relocated to the line's trailing edge: avatar · company name · nickname (bare, no @ prefix, optional single role badge) · timestamp. The identity items keep the standard meta-row grammar (independent `<a>` links, middot separators, badge outside the link); the timestamp is plain text in `sys.color.
|
|
6
|
+
"description": "One-line channel-detail attribution — the slimmed [Standard](standard.md) head that keeps the leading 32-rung [Thumbnail](../thumbnail/thumbnail.md) but drops the primary name line, follow toggle, and subtitle (the info that is unnecessary once the channel context is already established). A leading avatar plus the identity meta-link row, with the posting time relocated to the line's trailing edge: avatar · company name · nickname (bare, no @ prefix, optional single role badge) · timestamp. The identity items keep the standard meta-row grammar (independent `<a>` links, middot separators, badge outside the link); the timestamp is plain text in `sys.color.border.boldest` so the line reads identity-first, time-last. `layoutInset: inline` — atom-shaped, pays no padding of its own; the host row owns the gutter / divider / click target.",
|
|
7
7
|
"element": "div",
|
|
8
8
|
"props": {
|
|
9
9
|
"variant": {
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
"timestamp": {
|
|
24
24
|
"type": "string",
|
|
25
25
|
"required": true,
|
|
26
|
-
"description": "Posting time at the line's trailing edge — plain text (never a link), in `label.sm` / `sys.color.
|
|
26
|
+
"description": "Posting time at the line's trailing edge — plain text (never a link), in `label.sm` / `sys.color.border.boldest` so it recedes behind the identity links. NOT preceded by a middot: the time is separated from the identity cluster by an `inline.md` (8) gap so it reads as a distinct trailing element rather than another identity item (mirrors the Standard head's name↔time treatment). Required: the timestamp is what distinguishes a compact attribution from a bare identity row."
|
|
27
27
|
}
|
|
28
28
|
},
|
|
29
29
|
"slots": {
|
|
@@ -47,7 +47,7 @@
|
|
|
47
47
|
},
|
|
48
48
|
"timestamp": {
|
|
49
49
|
"required": true,
|
|
50
|
-
"description": "Trailing plain-text posting time after the identity cluster — no leading middot, separated by an `inline.md` (8) gap. `sys.typo.label.sm` / `sys.color.
|
|
50
|
+
"description": "Trailing plain-text posting time after the identity cluster — no leading middot, separated by an `inline.md` (8) gap. `sys.typo.label.sm` / `sys.color.border.boldest` — one tonal step further than the identity links.",
|
|
51
51
|
"accepts": [
|
|
52
52
|
"text"
|
|
53
53
|
]
|
|
@@ -55,10 +55,10 @@
|
|
|
55
55
|
},
|
|
56
56
|
"sizing": {
|
|
57
57
|
"metaTypo": "sys.typo.label.sm",
|
|
58
|
-
"metaColor": "sys.color.
|
|
58
|
+
"metaColor": "sys.color.text.subtle",
|
|
59
59
|
"timestampTypo": "sys.typo.label.sm",
|
|
60
|
-
"timestampColor": "sys.color.
|
|
61
|
-
"dotColor": "sys.color.
|
|
60
|
+
"timestampColor": "sys.color.border.boldest",
|
|
61
|
+
"dotColor": "sys.color.border.boldest",
|
|
62
62
|
"dotLineHeight": "1",
|
|
63
63
|
"dotLineHeightNote": "Same family-wide rule as the standard sub: the middot separator inherits the surrounding text's font-size but uses `line-height: 1` so its line-box never exceeds the glyph's font-size — the dot never inflates the single text line.",
|
|
64
64
|
"metaSeparatorInset": "sys.layout.inline.sm"
|
|
@@ -12,7 +12,7 @@ Three rules hold across every family member.
|
|
|
12
12
|
|
|
13
13
|
### Dot height
|
|
14
14
|
|
|
15
|
-
Every middot separator inherits the surrounding text's font-size but uses `line-height: 1`, so its line-box never exceeds the glyph's font-size — the U+00B7 glyph's natural vertical extent never inflates the host text line. The dot paints in `sys.color.
|
|
15
|
+
Every middot separator inherits the surrounding text's font-size but uses `line-height: 1`, so its line-box never exceeds the glyph's font-size — the U+00B7 glyph's natural vertical extent never inflates the host text line. The dot paints in `sys.color.border.boldest` and is always inline, never a block element.
|
|
16
16
|
|
|
17
17
|
### Bare nickname, single badge
|
|
18
18
|
|
|
@@ -26,11 +26,9 @@ import { Metadata } from '@teamblind-chorus/ui';
|
|
|
26
26
|
/>
|
|
27
27
|
```
|
|
28
28
|
|
|
29
|
-
##
|
|
29
|
+
## Follow action
|
|
30
30
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
The inline follow toggle. A middot separates it from the timestamp; the toggle paints in `sys.color.primary` at rest and `sys.color.onSurfaceVariant` when active. The dot's line-box stays inside its font-size so the row's text-line stays tight.
|
|
31
|
+
The inline follow toggle. A middot separates it from the timestamp; the toggle paints in `sys.color.background.primary` at rest and `sys.color.text.subtle` when active. The dot's line-box stays inside its font-size so the row's text-line stays tight.
|
|
34
32
|
|
|
35
33
|
```preview
|
|
36
34
|
metadata/standard/follow
|
|
@@ -47,7 +45,7 @@ import { Metadata } from '@teamblind-chorus/ui';
|
|
|
47
45
|
/>
|
|
48
46
|
```
|
|
49
47
|
|
|
50
|
-
|
|
48
|
+
## Sponsored
|
|
51
49
|
|
|
52
50
|
Feed Ad shape — `subtitle="Sponsored"` paints a plain caption-tone line under the brand name (no link affordance, no meta row). Pair with `trailing` to host the dismiss × button.
|
|
53
51
|
|
|
@@ -69,7 +67,7 @@ import { XIcon } from '@teamblind-chorus/ui/icons';
|
|
|
69
67
|
/>
|
|
70
68
|
```
|
|
71
69
|
|
|
72
|
-
|
|
70
|
+
## Role badge
|
|
73
71
|
|
|
74
72
|
A meta item in object form carries `badge` — a single presentational mark rendered after the item's link, outside the `<a>`, at the middots' 4px gap. Canonical fill: a role [Badge](../badge/badge.md) on the trailing nickname. At most one badge rides the nickname, never a stack.
|
|
75
73
|
|
|
@@ -96,8 +94,8 @@ import { Metadata, Badge } from '@teamblind-chorus/ui';
|
|
|
96
94
|
- **container** — outer flex row. `align-items: center`, `sys.layout.inline.md` (8px) gap between avatar, text column, and trailing slot.
|
|
97
95
|
- **avatar** *(optional)* — leading [Thumbnail](../thumbnail/thumbnail.md) at `size={32}`. Forwards every Thumbnail prop verbatim.
|
|
98
96
|
- **text** — two-line text column. Primary line on top, optional secondary line below. `flex: 1 1 auto`, `min-width: 0` so both lines truncate.
|
|
99
|
-
- **name** — entity name. `<a>` when `nameHref` is set, `<span>` otherwise. `sys.typo.label.sm` / `sys.color.
|
|
100
|
-
- **timestamp** *(optional)* — inline timestamp after the name. `sys.typo.label.sm` / `sys.color.
|
|
97
|
+
- **name** — entity name. `<a>` when `nameHref` is set, `<span>` otherwise. `sys.typo.label.sm` / `sys.color.text.default`. Single line; truncates.
|
|
98
|
+
- **timestamp** *(optional)* — inline timestamp after the name. `sys.typo.label.sm` / `sys.color.border.boldest`.
|
|
101
99
|
- **followAction** *(optional)* — bare-text follow toggle at the primary line's trailing edge. Preceded by a middot.
|
|
102
100
|
- **subtitle** *(optional, ad)* — plain caption-tone secondary line. Mutually exclusive with `meta`.
|
|
103
101
|
- **meta** *(optional, post)* — secondary line meta-link row. Each item is its own `<a>`; siblings separate by middot. The last item is canonically the user's nickname, displayed bare (no @ prefix). An object item may carry `badge` — a single presentational mark node rendered after the link, outside the `<a>` (canonical fill: [badge/role](../badge/badge.md) on the nickname — at most one badge rides the nickname, never a stack).
|
|
@@ -109,12 +107,12 @@ import { Metadata, Badge } from '@teamblind-chorus/ui';
|
|
|
109
107
|
|---------------|----------------|
|
|
110
108
|
| container | Flex row, `sys.layout.inline.md` (8) gap, `align-items: center` |
|
|
111
109
|
| avatar | [Thumbnail](../thumbnail/thumbnail.md) `size={32}` |
|
|
112
|
-
| name | `sys.typo.label.sm` / Semibold / `sys.color.
|
|
113
|
-
| timestamp | `sys.typo.label.sm` / `sys.color.
|
|
114
|
-
| dot separator | `·` glyph, `color: sys.color.
|
|
115
|
-
| follow | `sys.typo.label.sm` / Semibold / `sys.color.primary` (active → `sys.color.
|
|
116
|
-
| subtitle | `sys.typo.label.sm` / `sys.color.
|
|
117
|
-
| meta | `sys.typo.label.sm` / `sys.color.
|
|
110
|
+
| name | `sys.typo.label.sm` / Semibold / `sys.color.text.default`, single-line ellipsis |
|
|
111
|
+
| timestamp | `sys.typo.label.sm` / `sys.color.border.boldest` |
|
|
112
|
+
| dot separator | `·` glyph, `color: sys.color.border.boldest`, **`line-height: 1`** so its line-box equals its font-size — never inflates the text line |
|
|
113
|
+
| follow | `sys.typo.label.sm` / Semibold / `sys.color.background.primary` (active → `sys.color.text.subtle`) |
|
|
114
|
+
| subtitle | `sys.typo.label.sm` / `sys.color.text.subtle` |
|
|
115
|
+
| meta | `sys.typo.label.sm` / `sys.color.text.subtle`, links inherit; underline on hover |
|
|
118
116
|
|
|
119
117
|
## States
|
|
120
118
|
|