@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
|
@@ -10,7 +10,7 @@ Input-shaped picker — same box, label, helper, and error re-tone as [Input](./
|
|
|
10
10
|
|
|
11
11
|
## Default
|
|
12
12
|
|
|
13
|
-
Neutral at-rest field — transparent fill, hairline `
|
|
13
|
+
Neutral at-rest field — transparent fill, hairline `border.default` stroke, placeholder in faint `outline` colour. The trailing 16px chevron signals that the field opens a sheet rather than a caret.
|
|
14
14
|
|
|
15
15
|
```preview
|
|
16
16
|
form-field/select/default
|
|
@@ -20,9 +20,7 @@ import { FormField } from '@teamblind-chorus/ui';
|
|
|
20
20
|
<FormField variant="select" placeholder="Select an option" onOpen={() => {}} />
|
|
21
21
|
```
|
|
22
22
|
|
|
23
|
-
##
|
|
24
|
-
|
|
25
|
-
### With leading icon
|
|
23
|
+
## Leading icon
|
|
26
24
|
|
|
27
25
|
Optional `leadingIcon` (16px / `sys.icon.md`) pins inner-left — same affordance as Search bar's glyph. Available on `input` and `select`.
|
|
28
26
|
|
|
@@ -40,22 +38,7 @@ import { GlobeIcon } from '@teamblind-chorus/ui/icons';
|
|
|
40
38
|
/>
|
|
41
39
|
```
|
|
42
40
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
Pair a Select with an Input on one row via `<FormFieldGroup direction="horizontal">`. The group owns one shared label above and helper below; children render as bare boxes joined at `sys.layout.inline.md` gap. Typical pattern: leading Select (country dial code, currency, unit) + trailing real Input.
|
|
46
|
-
|
|
47
|
-
```preview
|
|
48
|
-
form-field/select/group
|
|
49
|
-
---
|
|
50
|
-
import { FormField, FormFieldGroup } from '@teamblind-chorus/ui';
|
|
51
|
-
|
|
52
|
-
<FormFieldGroup direction="horizontal" label="Phone number" helper="We'll text a one-time code">
|
|
53
|
-
<FormField variant="select" value="+82" onOpen={() => {}} style={{ flex: '0 0 96px' }} />
|
|
54
|
-
<FormField variant="input" placeholder="010-0000-0000" />
|
|
55
|
-
</FormFieldGroup>
|
|
56
|
-
```
|
|
57
|
-
|
|
58
|
-
### Focus indicator
|
|
41
|
+
## Focused state
|
|
59
42
|
|
|
60
43
|
Same as [Input → Focus indicator](./input.md#focus-indicator) — layered on top of the `active` border re-tone.
|
|
61
44
|
|
|
@@ -72,6 +55,21 @@ import { FormField } from '@teamblind-chorus/ui';
|
|
|
72
55
|
/>
|
|
73
56
|
```
|
|
74
57
|
|
|
58
|
+
## Group
|
|
59
|
+
|
|
60
|
+
Pair a Select with an Input on one row via `<FormFieldGroup direction="horizontal">`. The group owns one shared label above and helper below; children render as bare boxes joined at `sys.layout.inline.md` gap. Typical pattern: leading Select (country dial code, currency, unit) + trailing real Input.
|
|
61
|
+
|
|
62
|
+
```preview
|
|
63
|
+
form-field/select/group
|
|
64
|
+
---
|
|
65
|
+
import { FormField, FormFieldGroup } from '@teamblind-chorus/ui';
|
|
66
|
+
|
|
67
|
+
<FormFieldGroup direction="horizontal" label="Phone number" helper="We'll text a one-time code">
|
|
68
|
+
<FormField variant="select" value="+82" onOpen={() => {}} style={{ flex: '0 0 96px' }} />
|
|
69
|
+
<FormField variant="input" placeholder="010-0000-0000" />
|
|
70
|
+
</FormFieldGroup>
|
|
71
|
+
```
|
|
72
|
+
|
|
75
73
|
## Appearance
|
|
76
74
|
|
|
77
75
|
Same two-appearance axis as [Input → Appearance](./input.md#appearance) — `default` and `error`. The error re-tone covers box, chevron, label and helper rung; placeholder text steps to `onErrorContainer`.
|
|
@@ -42,7 +42,7 @@
|
|
|
42
42
|
"leadingIcon": {
|
|
43
43
|
"type": "node",
|
|
44
44
|
"optional": true,
|
|
45
|
-
"description": "Optional 16px (`sys.icon.md`) decorative glyph pinned at the inner-left edge of the field. Tracks the field's active text colour (`sys.color.
|
|
45
|
+
"description": "Optional 16px (`sys.icon.md`) decorative glyph pinned at the inner-left edge of the field. Tracks the field's active text colour (`sys.color.text.default` on the default appearance, `sys.color.text.danger` on `error`) so the glyph reads as part of the typed content."
|
|
46
46
|
},
|
|
47
47
|
"onOpen": {
|
|
48
48
|
"type": "function",
|
|
@@ -99,7 +99,7 @@
|
|
|
99
99
|
"slotGap": "sys.layout.inline.md",
|
|
100
100
|
"radius": "sys.radius.md",
|
|
101
101
|
"borderWidth": "sys.borderWidth.hairline",
|
|
102
|
-
"activeStrokeWeight": "sys.borderWidth.
|
|
102
|
+
"activeStrokeWeight": "sys.borderWidth.hairline",
|
|
103
103
|
"groupGap": "sys.layout.stack.xs",
|
|
104
104
|
"labelTypo": "sys.typo.label.md",
|
|
105
105
|
"helperTypo": "sys.typo.body.sm",
|
|
@@ -109,28 +109,28 @@
|
|
|
109
109
|
"iconSize": "sys.icon.md"
|
|
110
110
|
},
|
|
111
111
|
"groupColors": {
|
|
112
|
-
"label": "sys.color.
|
|
113
|
-
"helper": "sys.color.
|
|
114
|
-
"helperError": "sys.color.
|
|
115
|
-
"count": "sys.color.
|
|
116
|
-
"countCurrent": "sys.color.
|
|
112
|
+
"label": "sys.color.text.default",
|
|
113
|
+
"helper": "sys.color.text.subtle",
|
|
114
|
+
"helperError": "sys.color.text.danger",
|
|
115
|
+
"count": "sys.color.text.subtle",
|
|
116
|
+
"countCurrent": "sys.color.text.default"
|
|
117
117
|
},
|
|
118
118
|
"appearances": {
|
|
119
119
|
"default": {
|
|
120
120
|
"background": "transparent",
|
|
121
|
-
"text": "sys.color.
|
|
122
|
-
"placeholder": "sys.color.
|
|
123
|
-
"borderRest": "sys.color.
|
|
124
|
-
"borderHover": "sys.color.
|
|
125
|
-
"borderActive": "sys.color.
|
|
121
|
+
"text": "sys.color.text.default",
|
|
122
|
+
"placeholder": "sys.color.border.boldest",
|
|
123
|
+
"borderRest": "sys.color.border.default",
|
|
124
|
+
"borderHover": "sys.color.border.boldest",
|
|
125
|
+
"borderActive": "sys.color.border.focused"
|
|
126
126
|
},
|
|
127
127
|
"error": {
|
|
128
|
-
"background": "sys.color.
|
|
129
|
-
"text": "sys.color.
|
|
130
|
-
"placeholder": "sys.color.
|
|
131
|
-
"borderRest": "sys.color.
|
|
132
|
-
"borderHover": "sys.color.
|
|
133
|
-
"borderActive": "sys.color.
|
|
128
|
+
"background": "sys.color.background.danger",
|
|
129
|
+
"text": "sys.color.text.danger",
|
|
130
|
+
"placeholder": "sys.color.text.danger",
|
|
131
|
+
"borderRest": "sys.color.border.danger",
|
|
132
|
+
"borderHover": "sys.color.border.danger",
|
|
133
|
+
"borderActive": "sys.color.border.danger"
|
|
134
134
|
}
|
|
135
135
|
},
|
|
136
136
|
"states": {
|
|
@@ -157,21 +157,24 @@
|
|
|
157
157
|
"focusRing": {
|
|
158
158
|
"composition": "outward",
|
|
159
159
|
"layer": "::after overlay — position:absolute, inset:0, no reflow (DESIGN.md Focus ring composition)",
|
|
160
|
-
"innerCounterRing": { "width": "sys.borderWidth.hairline", "color": "sys.color.
|
|
161
|
-
"outerRing": { "width": "sys.borderWidth.thin", "color": "sys.color.
|
|
160
|
+
"innerCounterRing": { "width": "sys.borderWidth.hairline", "color": "sys.color.border.focused" },
|
|
161
|
+
"outerRing": { "width": "sys.borderWidth.thin", "color": "sys.color.border.focused" }
|
|
162
162
|
},
|
|
163
|
-
"note": "This IS the trigger's keyboard-focus / open state — `:focus-visible` and the engaged (open) state coincide for the select trigger, so there is no separate `focused` state; the focus ring described here (and in the parallel `focusIndicator` block) is the focus affordance. The stroke re-tones to `borderActive` at `activeStrokeWeight` (
|
|
163
|
+
"note": "This IS the trigger's keyboard-focus / open state — `:focus-visible` and the engaged (open) state coincide for the select trigger, so there is no separate `focused` state; the focus ring described here (and in the parallel `focusIndicator` block) is the focus affordance. The stroke re-tones to `borderActive` at `activeStrokeWeight` (1px, = rest) as an inset box-shadow, pixel-stable (no reflow)."
|
|
164
164
|
},
|
|
165
165
|
"disabled": {
|
|
166
166
|
"overlay": null,
|
|
167
|
-
"background": "sys.color.
|
|
168
|
-
"
|
|
167
|
+
"background": "sys.color.background.disabled",
|
|
168
|
+
"text": "sys.color.text.disabled",
|
|
169
|
+
"placeholder": "sys.color.text.disabled",
|
|
170
|
+
"border": "sys.color.border.bold",
|
|
169
171
|
"suppressFocusRing": true,
|
|
170
|
-
"cursor": "not-allowed"
|
|
172
|
+
"cursor": "not-allowed",
|
|
173
|
+
"note": "Explicit disabled (no opacity): neutral disabled fill + bold border + disabled text/placeholder."
|
|
171
174
|
}
|
|
172
175
|
},
|
|
173
176
|
"focusIndicator": {
|
|
174
|
-
"description": "Same keyboard-focus indicator as Input — outward
|
|
177
|
+
"description": "Same keyboard-focus indicator as Input — outward single ring composed over the active stroke.",
|
|
175
178
|
"composition": "outward",
|
|
176
179
|
"compositionReason": "Action affordance with breathing room around it.",
|
|
177
180
|
"overlay": {
|
|
@@ -179,10 +182,8 @@
|
|
|
179
182
|
"opacity": "sys.state.focus"
|
|
180
183
|
},
|
|
181
184
|
"ring": {
|
|
182
|
-
"
|
|
183
|
-
"
|
|
184
|
-
"insetWidth": "sys.borderWidth.hairline",
|
|
185
|
-
"insetColor": "sys.color.focusInset"
|
|
185
|
+
"width": "sys.borderWidth.hairline",
|
|
186
|
+
"color": "sys.color.border.focused"
|
|
186
187
|
},
|
|
187
188
|
"trigger": ":focus-visible"
|
|
188
189
|
},
|
|
@@ -25,11 +25,9 @@ import { FormField } from '@teamblind-chorus/ui';
|
|
|
25
25
|
/>
|
|
26
26
|
```
|
|
27
27
|
|
|
28
|
-
##
|
|
28
|
+
## Error
|
|
29
29
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
`appearance="error"` re-tones container to `errorContainer` and stroke to `error`. The optional `helper` paints in `sys.color.error` as the error caption.
|
|
30
|
+
`appearance="error"` re-tones container to `errorContainer` and stroke to `error`. The optional `helper` paints in `sys.color.text.danger` as the error caption.
|
|
33
31
|
|
|
34
32
|
```preview
|
|
35
33
|
form-field/textarea-error
|
|
@@ -45,7 +43,7 @@ import { FormField } from '@teamblind-chorus/ui';
|
|
|
45
43
|
/>
|
|
46
44
|
```
|
|
47
45
|
|
|
48
|
-
|
|
46
|
+
## Character count
|
|
49
47
|
|
|
50
48
|
`maxLength` caps value length and renders a `current/max` count below the box (right-aligned). Mutually exclusive with `helper` — count wins.
|
|
51
49
|
|
|
@@ -36,7 +36,7 @@
|
|
|
36
36
|
"helper": {
|
|
37
37
|
"type": "node",
|
|
38
38
|
"optional": true,
|
|
39
|
-
"description": "Assistive text rendered below the field box, left-aligned. Same rules as [input.helper](./input.md): mutually exclusive with `maxLength`, optional on every appearance, re-tones to `sys.color.
|
|
39
|
+
"description": "Assistive text rendered below the field box, left-aligned. Same rules as [input.helper](./input.md): mutually exclusive with `maxLength`, optional on every appearance, re-tones to `sys.color.text.danger` on the error appearance."
|
|
40
40
|
},
|
|
41
41
|
"maxLength": {
|
|
42
42
|
"type": "number",
|
|
@@ -61,7 +61,7 @@
|
|
|
61
61
|
},
|
|
62
62
|
"label": {
|
|
63
63
|
"required": false,
|
|
64
|
-
"description": "Visible label above the box. `sys.typo.label.md`, `sys.color.
|
|
64
|
+
"description": "Visible label above the box. `sys.typo.label.md`, `sys.color.text.default`. Associated with the textarea via `htmlFor`.",
|
|
65
65
|
"accepts": ["text"]
|
|
66
66
|
},
|
|
67
67
|
"container": {
|
|
@@ -92,7 +92,7 @@
|
|
|
92
92
|
"slotGap": "sys.layout.inline.md",
|
|
93
93
|
"radius": "sys.radius.md",
|
|
94
94
|
"borderWidth": "sys.borderWidth.hairline",
|
|
95
|
-
"activeStrokeWeight": "sys.borderWidth.
|
|
95
|
+
"activeStrokeWeight": "sys.borderWidth.hairline",
|
|
96
96
|
"groupGap": "sys.layout.stack.xs",
|
|
97
97
|
"labelTypo": "sys.typo.label.md",
|
|
98
98
|
"helperTypo": "sys.typo.body.sm",
|
|
@@ -104,28 +104,28 @@
|
|
|
104
104
|
"resize": "vertical"
|
|
105
105
|
},
|
|
106
106
|
"groupColors": {
|
|
107
|
-
"label": "sys.color.
|
|
108
|
-
"helper": "sys.color.
|
|
109
|
-
"helperError": "sys.color.
|
|
110
|
-
"count": "sys.color.
|
|
111
|
-
"countCurrent": "sys.color.
|
|
107
|
+
"label": "sys.color.text.default",
|
|
108
|
+
"helper": "sys.color.text.subtle",
|
|
109
|
+
"helperError": "sys.color.text.danger",
|
|
110
|
+
"count": "sys.color.text.subtle",
|
|
111
|
+
"countCurrent": "sys.color.text.default"
|
|
112
112
|
},
|
|
113
113
|
"appearances": {
|
|
114
114
|
"default": {
|
|
115
115
|
"background": "transparent",
|
|
116
|
-
"text": "sys.color.
|
|
117
|
-
"placeholder": "sys.color.
|
|
118
|
-
"borderRest": "sys.color.
|
|
119
|
-
"borderHover": "sys.color.
|
|
120
|
-
"borderActive": "sys.color.
|
|
116
|
+
"text": "sys.color.text.default",
|
|
117
|
+
"placeholder": "sys.color.border.boldest",
|
|
118
|
+
"borderRest": "sys.color.border.default",
|
|
119
|
+
"borderHover": "sys.color.border.boldest",
|
|
120
|
+
"borderActive": "sys.color.border.focused"
|
|
121
121
|
},
|
|
122
122
|
"error": {
|
|
123
|
-
"background": "sys.color.
|
|
124
|
-
"text": "sys.color.
|
|
125
|
-
"placeholder": "sys.color.
|
|
126
|
-
"borderRest": "sys.color.
|
|
127
|
-
"borderHover": "sys.color.
|
|
128
|
-
"borderActive": "sys.color.
|
|
123
|
+
"background": "sys.color.background.danger",
|
|
124
|
+
"text": "sys.color.text.danger",
|
|
125
|
+
"placeholder": "sys.color.text.danger",
|
|
126
|
+
"borderRest": "sys.color.border.danger",
|
|
127
|
+
"borderHover": "sys.color.border.danger",
|
|
128
|
+
"borderActive": "sys.color.border.danger"
|
|
129
129
|
}
|
|
130
130
|
},
|
|
131
131
|
"states": {
|
|
@@ -145,32 +145,33 @@
|
|
|
145
145
|
"focusRing": {
|
|
146
146
|
"composition": "outward",
|
|
147
147
|
"layer": "::after overlay — position:absolute, inset:0, no reflow (DESIGN.md Focus ring composition)",
|
|
148
|
-
"innerCounterRing": { "width": "sys.borderWidth.hairline", "color": "sys.color.
|
|
149
|
-
"outerRing": { "width": "sys.borderWidth.thin", "color": "sys.color.
|
|
148
|
+
"innerCounterRing": { "width": "sys.borderWidth.hairline", "color": "sys.color.border.focused" },
|
|
149
|
+
"outerRing": { "width": "sys.borderWidth.thin", "color": "sys.color.border.focused" }
|
|
150
150
|
},
|
|
151
|
-
"note": "This IS the field's keyboard/input-focus state — `active` (caret visible, input engaged) and `:focus-visible` coincide for a text field, so there is no separate `focused` state; the focus ring described here (and in the parallel `focusIndicator` block) is the focus affordance. Stroke
|
|
151
|
+
"note": "This IS the field's keyboard/input-focus state — `active` (caret visible, input engaged) and `:focus-visible` coincide for a text field, so there is no separate `focused` state; the focus ring described here (and in the parallel `focusIndicator` block) is the focus affordance. Stroke stays at `hairline` (1px), re-toning to `borderActive` as an inset box-shadow (no thickening) — same pixel-stable contract as input."
|
|
152
152
|
},
|
|
153
153
|
"disabled": {
|
|
154
154
|
"overlay": null,
|
|
155
|
-
"background": "sys.color.
|
|
156
|
-
"
|
|
155
|
+
"background": "sys.color.background.disabled",
|
|
156
|
+
"text": "sys.color.text.disabled",
|
|
157
|
+
"placeholder": "sys.color.text.disabled",
|
|
158
|
+
"border": "sys.color.border.bold",
|
|
157
159
|
"suppressFocusRing": true,
|
|
158
|
-
"cursor": "not-allowed"
|
|
160
|
+
"cursor": "not-allowed",
|
|
161
|
+
"note": "Explicit disabled (no opacity): neutral disabled fill + bold border + disabled text/placeholder."
|
|
159
162
|
}
|
|
160
163
|
},
|
|
161
164
|
"focusIndicator": {
|
|
162
|
-
"description": "Same outward
|
|
165
|
+
"description": "Same outward single ring as input.focusIndicator.",
|
|
163
166
|
"composition": "outward",
|
|
164
|
-
"compositionReason": "Action affordance with breathing room around it; the
|
|
167
|
+
"compositionReason": "Action affordance with breathing room around it; the 1px outward ring is reserved by the surrounding layout.",
|
|
165
168
|
"overlay": {
|
|
166
169
|
"color": "label",
|
|
167
170
|
"opacity": "sys.state.focus"
|
|
168
171
|
},
|
|
169
172
|
"ring": {
|
|
170
|
-
"
|
|
171
|
-
"
|
|
172
|
-
"insetWidth": "sys.borderWidth.hairline",
|
|
173
|
-
"insetColor": "sys.color.focusInset"
|
|
173
|
+
"width": "sys.borderWidth.hairline",
|
|
174
|
+
"color": "sys.color.border.focused"
|
|
174
175
|
},
|
|
175
176
|
"trigger": ":focus-visible (keyboard / programmatic focus, never plain mouse click)"
|
|
176
177
|
},
|
|
@@ -23,9 +23,7 @@ import { Header } from '@teamblind-chorus/ui';
|
|
|
23
23
|
/>
|
|
24
24
|
```
|
|
25
25
|
|
|
26
|
-
##
|
|
27
|
-
|
|
28
|
-
### With drill-in chevron
|
|
26
|
+
## Drill-in chevron
|
|
29
27
|
|
|
30
28
|
`trailingIcon` mode — the chevron is a [Button `variant="icon"`](../button/icon.md) at `size="medium"` (32 × 32 capsule, 16-glyph) that owns its own tap target. The chevron paints in `onSurfaceVariant` and rotates -90° to read as chevron-right. The surrounding `<header>` element stays non-interactive — clicks land on the Icon Button, not on the row chrome. Use when the trailing affordance is "open this surface", not "commit a labelled action". `headerAction` and `trailingIcon` are mutually exclusive.
|
|
31
29
|
|
|
@@ -42,7 +40,7 @@ import { Header } from '@teamblind-chorus/ui';
|
|
|
42
40
|
/>
|
|
43
41
|
```
|
|
44
42
|
|
|
45
|
-
|
|
43
|
+
## Dropdown
|
|
46
44
|
|
|
47
45
|
`headerDropdown` mode — the trailing affordance is a [Text Button dropdown](../button/text.md#dropdown) (`size="xsmall"`, default appearance) whose **label is the current value** ("Top", "Newest", "All time") and whose trailing chevron flips with `open` as a state signal. Use when the heading row needs an inline sort / filter / range disclosure — "Recommended channels [Top ▾]", "Posts [Last 7 days ▾]". Consumer owns the menu surface (Menu, popover, ListBox) and the `open` state; Header only renders the trigger.
|
|
48
46
|
|
|
@@ -69,7 +67,7 @@ function Example() {
|
|
|
69
67
|
}
|
|
70
68
|
```
|
|
71
69
|
|
|
72
|
-
|
|
70
|
+
## Label only
|
|
73
71
|
|
|
74
72
|
Set only `label` (omit `headerAction`, `trailingIcon`, and `headerDropdown`) for a labelled region that needs a heading without a trailing affordance. The row collapses to a single heading at the requested size — no spacer, no empty container.
|
|
75
73
|
|
|
@@ -84,7 +82,7 @@ import { Header } from '@teamblind-chorus/ui';
|
|
|
84
82
|
## Slots
|
|
85
83
|
|
|
86
84
|
- **container** — outer row. Flex with `space-between`; label leads, action / icon / dropdown trails, 8px (`layout.inline.md`) gap. Full-bleed — transparent background and its own padding: `container.md` (16) inline (the shared content rail), `stack.lg` (24) block-start, `stack.md` (16) block-end — so the label lands on the same rail as the rows beneath it; bundling hosts add no inline padding of their own and absorb only the block padding. Stays a non-interactive `<header>` / `<div>` — the trailing affordance owns its own hit target.
|
|
87
|
-
- **label** *(optional)* — heading text. `<h3>` by default; override the wrapper with `as="div"` when the surrounding host already owns the heading semantics. Color `sys.color.
|
|
85
|
+
- **label** *(optional)* — heading text. `<h3>` by default; override the wrapper with `as="div"` when the surrounding host already owns the heading semantics. Color `sys.color.text.default`. Typo per size.
|
|
88
86
|
- **action** *(optional, headerAction mode)* — trailing Text Button. Fixed at `size="xsmall"`, `appearance="accent"`.
|
|
89
87
|
- **icon** *(optional, trailingIcon mode)* — trailing [Icon Button](../button/icon.md) (`variant="icon"` `size="medium"`) hosting a 16px glyph (canonical: chevron-right). Its own tap target — clicks land on the Icon Button, not the surrounding header. Supply `aria-label` for the icon-only button (defaults to `"Open <label>"` when `label` is a string).
|
|
90
88
|
- **dropdown** *(optional, headerDropdown mode)* — trailing [Text Button dropdown](../button/text.md#dropdown) (`size="xsmall"`, default appearance). Label is the current value; trailing chevron flips between `ChevronDownIcon` (closed) and `ChevronUpIcon` (open) tied to `headerDropdown.open`. Owns its own tap target — clicks land on the dropdown trigger, not the surrounding header.
|
|
@@ -56,7 +56,7 @@
|
|
|
56
56
|
},
|
|
57
57
|
"label": {
|
|
58
58
|
"required": false,
|
|
59
|
-
"description": "Heading text. `<h3>` by default. Size `large` → `sys.typo.heading.md`; size `medium` → `sys.typo.heading.sm`. Color `sys.color.
|
|
59
|
+
"description": "Heading text. `<h3>` by default. Size `large` → `sys.typo.heading.md`; size `medium` → `sys.typo.heading.sm`. Color `sys.color.text.default`."
|
|
60
60
|
},
|
|
61
61
|
"action": {
|
|
62
62
|
"required": false,
|
|
@@ -85,10 +85,10 @@
|
|
|
85
85
|
"medium": {
|
|
86
86
|
"labelTypo": "sys.typo.heading.sm (16 / Semibold)"
|
|
87
87
|
},
|
|
88
|
-
"labelColor": "sys.color.
|
|
88
|
+
"labelColor": "sys.color.text.default",
|
|
89
89
|
"actionSize": "Text Button xsmall, appearance accent",
|
|
90
90
|
"iconButtonSize": "Button variant=\"icon\" size=\"medium\" (32 × 32 capsule, 16-glyph)",
|
|
91
|
-
"iconColor": "sys.color.
|
|
91
|
+
"iconColor": "sys.color.text.subtle (glyph inherits via `currentColor` from the Icon Button)"
|
|
92
92
|
},
|
|
93
93
|
"appearance": {
|
|
94
94
|
"background": "transparent",
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
> 🇰🇷 한국어: [`i18n/ko/schema/components/header/sub.md`](../../../i18n/ko/schema/components/header/sub.md)
|
|
4
4
|
|
|
5
|
-
Quiet section-dividing label — a 14px (`sys.typo.label.md`, 14 / Semibold) line in the muted `sys.color.
|
|
5
|
+
Quiet section-dividing label — a 14px (`sys.typo.label.md`, 14 / Semibold) line in the muted `sys.color.text.subtle` tone that names the group of rows beneath it ("Following", "More Topics to follow"). The muted tone is the dividing device: it reads as a region label rather than a heading that competes with the page title, while the label-weight typo keeps it from dissolving into body copy. Rendered as a semantic `<h3>` by default so screen-reader heading navigation lands on the group label.
|
|
6
6
|
|
|
7
7
|
It may carry one quiet trailing affordance — a single [Text Button](../button/text.md) `action` ("See all", "Edit", "Manage") — for a region-level commit that stays subordinate to the muted label. Icon drill-in and dropdown disclosure are *not* SubHeader's: those belong on [Main](./main.md), whose louder `onSurface` heading earns the heavier affordance set.
|
|
8
8
|
|
|
@@ -22,9 +22,7 @@ import { SubHeader } from '@teamblind-chorus/ui';
|
|
|
22
22
|
<SubHeader label="Following" />
|
|
23
23
|
```
|
|
24
24
|
|
|
25
|
-
##
|
|
26
|
-
|
|
27
|
-
### Grouping a list
|
|
25
|
+
## Grouping a list
|
|
28
26
|
|
|
29
27
|
The canonical placement — a SubHeader labels each group of rows, splitting one stacked list into named sections. The muted label sits at the same `16px` inset as the rows; the block rhythm (more above, less below) reads as "new group starts here".
|
|
30
28
|
|
|
@@ -69,7 +67,7 @@ function Example() {
|
|
|
69
67
|
}
|
|
70
68
|
```
|
|
71
69
|
|
|
72
|
-
|
|
70
|
+
## Trailing action
|
|
73
71
|
|
|
74
72
|
Pass `action={{ label, href, onClick }}` for a single quiet commit on the trailing edge — "See all" above a previewed group, "Edit" above an editable cluster. The wrapper becomes a full-bleed flex row: the muted label holds the leading edge, the [Text Button](../button/text.md) (`size="xsmall"`, `appearance="accent"`) hugs the trailing edge at the same 16 inset. The action stays subordinate to the label — this is still a quiet region marker, not a [Main](./main.md) heading row. Use Main instead when the region needs the louder `onSurface` heading, an icon drill-in, or a sort dropdown.
|
|
75
73
|
|
|
@@ -91,7 +89,7 @@ import { SubHeader, List } from '@teamblind-chorus/ui';
|
|
|
91
89
|
</div>
|
|
92
90
|
```
|
|
93
91
|
|
|
94
|
-
|
|
92
|
+
## Host owns the heading
|
|
95
93
|
|
|
96
94
|
When the surrounding host already carries the section semantics, override the wrapping element with `as="div"` so the document outline doesn't gain a stray heading — the label keeps its muted tone and inset.
|
|
97
95
|
|
|
@@ -106,7 +104,7 @@ import { SubHeader } from '@teamblind-chorus/ui';
|
|
|
106
104
|
## Slots
|
|
107
105
|
|
|
108
106
|
- **container** — the full-bleed label line. `16px` (`layout.container.md`) inline inset to align with the rows beneath, `24px` (`layout.stack.lg`) block padding above and `8px` (`layout.stack.xs`) below. Non-interactive. Without `action` the container *is* the heading element (`as`); with `action` it is a non-semantic flex `<div>` (`space-between`, `8px` `layout.inline.md` gap) holding the nested label and the trailing action on opposite edges.
|
|
109
|
-
- **label** — required. Section label text. `<h3>` by default (override via `as`). Typo `sys.typo.label.md` (14 / Semibold); color `sys.color.
|
|
107
|
+
- **label** — required. Section label text. `<h3>` by default (override via `as`). Typo `sys.typo.label.md` (14 / Semibold); color `sys.color.text.subtle`. Single line.
|
|
110
108
|
- **action** — optional. Trailing [Text Button](../button/text.md) (`size="xsmall"`, `appearance="accent"`) from `action={{ label, href, onClick }}`. The one trailing affordance SubHeader carries; owns its own tap target. Present only when `action` is set.
|
|
111
109
|
|
|
112
110
|
## Anatomy
|
|
@@ -114,7 +112,7 @@ import { SubHeader } from '@teamblind-chorus/ui';
|
|
|
114
112
|
| Slot | Token bindings |
|
|
115
113
|
|-----------|----------------|
|
|
116
114
|
| container | `sys.layout.container.md` (16) inline padding, `sys.layout.stack.lg` (24) block-start, `sys.layout.stack.xs` (8) block-end, full inline width, no border, no radius; in `action` mode flex `space-between` with `sys.layout.inline.md` (8) gap |
|
|
117
|
-
| label | `sys.typo.label.md` (14 / Semibold) typo, `sys.color.
|
|
115
|
+
| label | `sys.typo.label.md` (14 / Semibold) typo, `sys.color.text.subtle` color |
|
|
118
116
|
| action | [Text Button](../button/text.md) `size="xsmall"`, `appearance="accent"`; intrinsic width, never grows |
|
|
119
117
|
|
|
120
118
|
## Appearance
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"name": "SubHeader",
|
|
4
4
|
"family": "header",
|
|
5
5
|
"subcomponent": "sub",
|
|
6
|
-
"description": "The quiet member of the Header family — a section-dividing label. A single line of 14px (`sys.typo.label.md`, 14 / Semibold) text in the muted `sys.color.
|
|
6
|
+
"description": "The quiet member of the Header family — a section-dividing label. A single line of 14px (`sys.typo.label.md`, 14 / Semibold) text in the muted `sys.color.text.subtle` tone that names the group of rows beneath it (\"Following\", \"More Topics to follow\"). No size axis — the muted tone is the dividing device, and the label-weight typo keeps it reading as a section label rather than body copy. Reached as `<SubHeader>`; its louder sibling is [Main](./main.md) (`<Header>`), an `onSurface` heading at 16 / 20. Optionally carries a single trailing `action` Text Button (`size=\"xsmall\"`, `appearance=\"accent\"`) for a quiet region-level commit (\"See all\", \"Edit\", \"Manage\"); icon drill-in and dropdown disclosure stay Main's territory, where the louder `onSurface` heading tone belongs. Rendered as a semantic `<h3>` by default so screen-reader heading navigation lands on the group label; consumers may override the wrapping element via the `as` prop (e.g. `as=\"div\"` when the host already owns the heading semantics). Full-bleed: pays the same 16 (`sys.layout.container.md`) inline inset as the List rows it labels, with asymmetric block padding (24 above to break from the previous region, 8 below to bind the label to its group).",
|
|
7
7
|
"element": "h3",
|
|
8
8
|
"props": {
|
|
9
9
|
"label": {
|
|
@@ -40,7 +40,7 @@
|
|
|
40
40
|
},
|
|
41
41
|
"label": {
|
|
42
42
|
"required": false,
|
|
43
|
-
"description": "Section label text. `<h3>` by default (overridable via `as`). Typo `sys.typo.label.md` (14 / Semibold); color `sys.color.
|
|
43
|
+
"description": "Section label text. `<h3>` by default (overridable via `as`). Typo `sys.typo.label.md` (14 / Semibold); color `sys.color.text.subtle`. Single line.",
|
|
44
44
|
"accepts": ["text"]
|
|
45
45
|
},
|
|
46
46
|
"action": {
|
|
@@ -53,7 +53,7 @@
|
|
|
53
53
|
"appearance": {
|
|
54
54
|
"containerFill": "transparent",
|
|
55
55
|
"labelTypo": "sys.typo.label.md",
|
|
56
|
-
"labelColor": "sys.color.
|
|
56
|
+
"labelColor": "sys.color.text.subtle",
|
|
57
57
|
"paddingInline": "sys.layout.container.md",
|
|
58
58
|
"paddingBlockStart": "sys.layout.stack.lg",
|
|
59
59
|
"paddingBlockEnd": "sys.layout.stack.xs",
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
> 🇰🇷 한국어: [`i18n/ko/schema/components/list/accordion.md`](../../../i18n/ko/schema/components/list/accordion.md)
|
|
4
4
|
|
|
5
|
-
Expandable-row List sub. Each item exposes a List-row trigger (label + auto-rendered trailing chevron that rotates `180°` on expand) and a body that paints below it when open. Rows tile flush with the family hairline `
|
|
5
|
+
Expandable-row List sub. Each item exposes a List-row trigger (label + auto-rendered trailing chevron that rotates `180°` on expand) and a body that paints below it when open. Rows tile flush with the family hairline `border.default` divider between them; an extra rule paints between the open trigger and its child row group.
|
|
6
6
|
|
|
7
7
|
**Reach for this when** a list of titled sections is too long to keep open at once — FAQs, T&C sections, expandable filter groups, settings groups with infrequent edits, hierarchical menus (companies → channels, regions → cities). **Skip when** bodies are short enough to read inline (use [Carousel](../carousel/carousel.md) per group), the user needs to act on labels (use a [List/standard](./standard.md) drill-in row or [List/radio](./radio.md)), or every item should be visible at once (stack [Carousel](../carousel/carousel.md)s).
|
|
8
8
|
|
|
@@ -12,7 +12,7 @@ Row geometry, label typography, divider, state overlays, and inward focus ring a
|
|
|
12
12
|
|
|
13
13
|
## Default
|
|
14
14
|
|
|
15
|
-
|
|
15
|
+
A single expandable row — the atomic Accordion item. Click the trigger to expand its body; click again to collapse (`collapsible` by default). The Group / Multiple sections below stack several. `type="single"` keeps one item open at a time.
|
|
16
16
|
|
|
17
17
|
```preview
|
|
18
18
|
accordion/default
|
|
@@ -23,42 +23,12 @@ import { Accordion } from '@teamblind-chorus/ui';
|
|
|
23
23
|
<Accordion.Item value="why" label="Why does Blind anonymise posts?">
|
|
24
24
|
Anonymity is the only way employees can compare salaries, escalate misconduct, or ask candid culture questions without retaliation. Verified company badges keep the channel trustworthy without unmasking the author.
|
|
25
25
|
</Accordion.Item>
|
|
26
|
-
<Accordion.Item value="verify" label="How is my company verified?">
|
|
27
|
-
Sign up with your corporate email — the verification code lands in your inbox, never on a public profile. Once verified, the badge persists across job changes (we re-verify when you update your employer).
|
|
28
|
-
</Accordion.Item>
|
|
29
|
-
<Accordion.Item value="data" label="What can other users see?">
|
|
30
|
-
Other users see your company badge, your career stage, and the content you post. Your email, name, and exact title are never exposed.
|
|
31
|
-
</Accordion.Item>
|
|
32
|
-
</Accordion>
|
|
33
|
-
```
|
|
34
|
-
|
|
35
|
-
## Use cases
|
|
36
|
-
|
|
37
|
-
### Multiple
|
|
38
|
-
|
|
39
|
-
`type="multiple"` lets the user open any number of items at once. Use when rows are independent and the user reads across several — comparing T&C clauses, expanding filter groups, reviewing policy sections.
|
|
40
|
-
|
|
41
|
-
```preview
|
|
42
|
-
accordion/multiple
|
|
43
|
-
---
|
|
44
|
-
import { Accordion } from '@teamblind-chorus/ui';
|
|
45
|
-
|
|
46
|
-
<Accordion type="multiple" defaultValue={['salary', 'tenure']} aria-label="Active filters">
|
|
47
|
-
<Accordion.Item value="salary" label="Compensation">
|
|
48
|
-
Filter posts by salary range, equity, sign-on bonus.
|
|
49
|
-
</Accordion.Item>
|
|
50
|
-
<Accordion.Item value="tenure" label="Tenure">
|
|
51
|
-
Filter authors by years at current company.
|
|
52
|
-
</Accordion.Item>
|
|
53
|
-
<Accordion.Item value="role" label="Role">
|
|
54
|
-
Filter by IC level, manager track, or specialty.
|
|
55
|
-
</Accordion.Item>
|
|
56
26
|
</Accordion>
|
|
57
27
|
```
|
|
58
28
|
|
|
59
|
-
|
|
29
|
+
## Nested list
|
|
60
30
|
|
|
61
|
-
When the body holds a same-kind row group rather than prose — directory sub-entries, settings sub-options, filter children, menu sub-items — drop a `<List>` into the content slot with `embedded={true}` so the list defers its own chrome to the host. The body recognises the embedded child via `:has([data-embedded='true'])` and switches to compact-host geometry: body inline padding flips from prose's `32 leading / 16 trailing` to `16 / 0` (sub-list stretches flush right), a hairline `
|
|
31
|
+
When the body holds a same-kind row group rather than prose — directory sub-entries, settings sub-options, filter children, menu sub-items — drop a `<List>` into the content slot with `embedded={true}` so the list defers its own chrome to the host. The body recognises the embedded child via `:has([data-embedded='true'])` and switches to compact-host geometry: body inline padding flips from prose's `32 leading / 16 trailing` to `16 / 0` (sub-list stretches flush right), a hairline `border.default` top group-divider paints via the body's `::before`, and sub-list rows compress to `body.sm` at `40px` min-height with inter-row dividers suppressed. Call sites pass no extra mode prop — dropping `<List embedded>` (or a nested `<Accordion embedded>`) activates it automatically.
|
|
62
32
|
|
|
63
33
|
```preview
|
|
64
34
|
accordion/nested-list
|
|
@@ -107,7 +77,7 @@ import { Accordion, List } from '@teamblind-chorus/ui';
|
|
|
107
77
|
</Accordion>
|
|
108
78
|
```
|
|
109
79
|
|
|
110
|
-
|
|
80
|
+
## Disabled item
|
|
111
81
|
|
|
112
82
|
A `disabled` row fades to `sys.state.disabled` opacity and ignores click / keyboard activation. Stays in the DOM so surrounding items keep their stable index.
|
|
113
83
|
|
|
@@ -123,8 +93,27 @@ import { Accordion } from '@teamblind-chorus/ui';
|
|
|
123
93
|
<Accordion.Item value="billing" label="Billing" disabled>
|
|
124
94
|
Available for verified enterprise accounts only.
|
|
125
95
|
</Accordion.Item>
|
|
126
|
-
|
|
127
|
-
|
|
96
|
+
</Accordion>
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
## Group
|
|
100
|
+
|
|
101
|
+
`type="multiple"` lets the user open any number of items at once. Use when rows are independent and the user reads across several — comparing T&C clauses, expanding filter groups, reviewing policy sections.
|
|
102
|
+
|
|
103
|
+
```preview
|
|
104
|
+
accordion/multiple
|
|
105
|
+
---
|
|
106
|
+
import { Accordion } from '@teamblind-chorus/ui';
|
|
107
|
+
|
|
108
|
+
<Accordion type="multiple" defaultValue={['salary', 'tenure']} aria-label="Active filters">
|
|
109
|
+
<Accordion.Item value="salary" label="Compensation">
|
|
110
|
+
Filter posts by salary range, equity, sign-on bonus.
|
|
111
|
+
</Accordion.Item>
|
|
112
|
+
<Accordion.Item value="tenure" label="Tenure">
|
|
113
|
+
Filter authors by years at current company.
|
|
114
|
+
</Accordion.Item>
|
|
115
|
+
<Accordion.Item value="role" label="Role">
|
|
116
|
+
Filter by IC level, manager track, or specialty.
|
|
128
117
|
</Accordion.Item>
|
|
129
118
|
</Accordion>
|
|
130
119
|
```
|
|
@@ -132,25 +121,25 @@ import { Accordion } from '@teamblind-chorus/ui';
|
|
|
132
121
|
## Slots
|
|
133
122
|
|
|
134
123
|
- **container** — outer stack. Transparent fill so the host surface tone reads through. `role="region"` carries the accordion's accessible name (`aria-label`).
|
|
135
|
-
- **item** — single expandable row. Hairline `
|
|
124
|
+
- **item** — single expandable row. Hairline `border.default` divider inset 16px on both inline edges, painted as an `::after` overlay on every row except the last.
|
|
136
125
|
- **trigger** — header button. Holds the label and the auto-rendered trailing chevron. Same geometry as a List row (48px min-height, 8 × 16 padding). `aria-expanded` reflects open-state, `aria-controls` references the content region.
|
|
137
126
|
- **label** — trigger label. `16 / Regular / onSurface` — matches the List family `label` spec so the trigger reads as a List row that happens to expand. Wraps to a second line; no truncation.
|
|
138
127
|
- **chevron** — auto-rendered 16px `ChevronDownIcon`. Rotates `0°` → `180°` over 120ms `ease-out` on expand. Decorative.
|
|
139
|
-
- **content** — body region. Paints below the trigger when open; toggled via the `hidden` attribute when closed. `min-height: 40px` keeps short single-line bodies on a touch-target rhythm. Two padding modes by content kind: *prose body* (text, icon, button, form-field) uses `32 leading / 16 trailing` inline padding so prose reads as nested inside the trigger's label column, body text at `body.sm` (one rung below the trigger label), no top group-divider. *Embedded row group* (`<List embedded>` or nested `<Accordion embedded>`, detected via `:has([data-embedded='true'])`) uses `16 / 0` inline padding with a hairline `
|
|
128
|
+
- **content** — body region. Paints below the trigger when open; toggled via the `hidden` attribute when closed. `min-height: 40px` keeps short single-line bodies on a touch-target rhythm. Two padding modes by content kind: *prose body* (text, icon, button, form-field) uses `32 leading / 16 trailing` inline padding so prose reads as nested inside the trigger's label column, body text at `body.sm` (one rung below the trigger label), no top group-divider. *Embedded row group* (`<List embedded>` or nested `<Accordion embedded>`, detected via `:has([data-embedded='true'])`) uses `16 / 0` inline padding with a hairline `border.default` top divider via `::before`; sub-list rows compress to `body.sm` at `40px` min-height with no inter-row dividers.
|
|
140
129
|
|
|
141
130
|
## Anatomy
|
|
142
131
|
|
|
143
132
|
| Slot | Token bindings |
|
|
144
133
|
|---------------|----------------|
|
|
145
134
|
| container | Transparent fill, no padding (full-bleed, edge-to-edge) |
|
|
146
|
-
| item | Hairline `
|
|
135
|
+
| item | Hairline `border.default` divider inset 16px on both inline edges, omitted on the last row — family-wide List divider |
|
|
147
136
|
| trigger | 48px min-height, 8px block / 16px inline padding, full-row click target |
|
|
148
137
|
| label | `16 / Regular`, `onSurface` — matches the List `label` spec |
|
|
149
138
|
| chevron | 16 × 16, `onSurfaceVariant`, rotates 0° → 180° over 120ms `ease-out` |
|
|
150
139
|
| content (prose) | 8px block padding, `32 leading / 16 trailing` inline padding, `min-height: 40px`, `sys.typo.body.sm` (14 / Regular) at `onSurfaceVariant` |
|
|
151
140
|
| content (embedded group) | 8px block padding, `16 leading / 0 trailing` inline padding; sub-list rows render at `body.sm` (14 / Regular), `min-height: 40px`, with `::after` row dividers suppressed |
|
|
152
|
-
| divider | `sys.borderWidth.hairline` × `sys.color.
|
|
153
|
-
| groupDivider | `sys.borderWidth.hairline` × `sys.color.
|
|
141
|
+
| divider | `sys.borderWidth.hairline` × `sys.color.border.default`, inset 16px on both inline edges via `::after` overlay |
|
|
142
|
+
| groupDivider | `sys.borderWidth.hairline` × `sys.color.border.default`, inset 16px on both inline edges via `::before` overlay on the content body, painted ONLY when the body hosts a `<List embedded>` child group |
|
|
154
143
|
|
|
155
144
|
## Appearance
|
|
156
145
|
|
|
@@ -167,14 +156,14 @@ A single appearance — Accordion paints no fill of its own and offers no emphas
|
|
|
167
156
|
|
|
168
157
|
## Focus indicator
|
|
169
158
|
|
|
170
|
-
Inward
|
|
159
|
+
Inward single ring painted inside the trigger's footprint via a `::before` overlay. Trigger: `:focus-visible`. Items tile flush with only a hairline divider between them, so an outward ring would overlap the divider and the neighbouring row — see [Focus ring composition](../../DESIGN.md#focus-ring-composition).
|
|
171
160
|
|
|
172
161
|
## Behavior
|
|
173
162
|
|
|
174
163
|
- **Edge-to-edge composition.** `layoutInset: full-bleed` — direct child of the page shell. Wrapping in another `padding-inline` / `px-*` div double-pays the rail. Use the negative-margin opt-out inside a bounded surface.
|
|
175
|
-
- **Inset divider.** 1px `
|
|
164
|
+
- **Inset divider.** 1px `border.default` rule inset 16px on both inline edges — same as every other List sub.
|
|
176
165
|
- **Indented prose.** Expanded body sits one extra 16px in from the trigger's label edge for parent ↔ child hierarchy.
|
|
177
|
-
- **Embedded groups switch the body to compact-host geometry.** When the body hosts a `<List embedded>`, three things change at once: body inline padding flips from `32 / 16` to `16 / 0` (sub-list flush right); a hairline `
|
|
166
|
+
- **Embedded groups switch the body to compact-host geometry.** When the body hosts a `<List embedded>`, three things change at once: body inline padding flips from `32 / 16` to `16 / 0` (sub-list flush right); a hairline `border.default` rule paints at the body's top edge so parent and child read as a hierarchy; sub-list rows compress to `body.sm` at `40px` min-height with no inter-row dividers.
|
|
178
167
|
- **Whole trigger is the click target.** Chevron is decorative.
|
|
179
168
|
- **Element swap.** Trigger is `<button>`; content region is `<div role="region">` with `hidden` toggled.
|
|
180
169
|
- **Keyboard.** Space / Enter toggle. Arrow up/down moves focus between triggers.
|