@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
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
"timestamp": {
|
|
25
25
|
"type": "string",
|
|
26
26
|
"optional": true,
|
|
27
|
-
"description": "Inline timestamp painted after the name on the primary line, in `label.sm` / `sys.color.
|
|
27
|
+
"description": "Inline timestamp painted after the name on the primary line, in `label.sm` / `sys.color.border.boldest` — one tonal step further than the name. Reach for it on Feed Post; omit on Feed Ad."
|
|
28
28
|
},
|
|
29
29
|
"followAction": {
|
|
30
30
|
"type": "boolean",
|
|
@@ -78,26 +78,26 @@
|
|
|
78
78
|
},
|
|
79
79
|
"name": {
|
|
80
80
|
"required": true,
|
|
81
|
-
"description": "Entity name. `sys.typo.label.sm` (12 / Semibold) / `sys.color.
|
|
81
|
+
"description": "Entity name. `sys.typo.label.sm` (12 / Semibold) / `sys.color.text.default`. Renders as `<a>` when `nameHref` is set, `<span>` otherwise. Single line; truncates with ellipsis.",
|
|
82
82
|
"accepts": [
|
|
83
83
|
"text"
|
|
84
84
|
]
|
|
85
85
|
},
|
|
86
86
|
"timestamp": {
|
|
87
87
|
"required": false,
|
|
88
|
-
"description": "Inline timestamp after the name. `sys.typo.label.sm` (12 / Semibold) / `sys.color.
|
|
88
|
+
"description": "Inline timestamp after the name. `sys.typo.label.sm` (12 / Semibold) / `sys.color.border.boldest` — one tonal step further than the name so the timestamp recedes.",
|
|
89
89
|
"accepts": [
|
|
90
90
|
"text"
|
|
91
91
|
]
|
|
92
92
|
},
|
|
93
93
|
"followAction": {
|
|
94
94
|
"required": false,
|
|
95
|
-
"description": "Inline follow toggle at the primary line's trailing edge. Bare text affordance (no chrome) — `sys.color.primary` at rest, `sys.color.
|
|
95
|
+
"description": "Inline follow toggle at the primary line's trailing edge. Bare text affordance (no chrome) — `sys.color.background.primary` at rest, `sys.color.text.subtle` when active. A `·` separator precedes it.",
|
|
96
96
|
"intrinsic": true
|
|
97
97
|
},
|
|
98
98
|
"subtitle": {
|
|
99
99
|
"required": false,
|
|
100
|
-
"description": "Secondary line plain text. `sys.typo.label.sm` / `sys.color.
|
|
100
|
+
"description": "Secondary line plain text. `sys.typo.label.sm` / `sys.color.text.subtle`. Mutually exclusive with `meta`.",
|
|
101
101
|
"accepts": [
|
|
102
102
|
"text"
|
|
103
103
|
]
|
|
@@ -121,14 +121,14 @@
|
|
|
121
121
|
"containerAlign": "center",
|
|
122
122
|
"avatarSize": 32,
|
|
123
123
|
"nameTypo": "sys.typo.label.sm",
|
|
124
|
-
"nameColor": "sys.color.
|
|
124
|
+
"nameColor": "sys.color.text.default",
|
|
125
125
|
"timestampTypo": "sys.typo.label.sm",
|
|
126
|
-
"timestampColor": "sys.color.
|
|
126
|
+
"timestampColor": "sys.color.border.boldest",
|
|
127
127
|
"subtitleTypo": "sys.typo.label.sm",
|
|
128
|
-
"subtitleColor": "sys.color.
|
|
128
|
+
"subtitleColor": "sys.color.text.subtle",
|
|
129
129
|
"metaTypo": "sys.typo.label.sm",
|
|
130
|
-
"metaColor": "sys.color.
|
|
131
|
-
"dotColor": "sys.color.
|
|
130
|
+
"metaColor": "sys.color.text.subtle",
|
|
131
|
+
"dotColor": "sys.color.border.boldest",
|
|
132
132
|
"dotLineHeight": "1",
|
|
133
133
|
"dotLineHeightNote": "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 — keeps the row's text-line tight even when the inherited line-height would otherwise allow extra vertical space around the middot.",
|
|
134
134
|
"primaryRowGap": "sys.layout.inline.md",
|
|
@@ -20,9 +20,7 @@ import { NavCard } from '@teamblind-chorus/ui';
|
|
|
20
20
|
<NavCard label="Cell label here" href="#" />
|
|
21
21
|
```
|
|
22
22
|
|
|
23
|
-
##
|
|
24
|
-
|
|
25
|
-
### Nav (with trailing chevron)
|
|
23
|
+
## Nav
|
|
26
24
|
|
|
27
25
|
`variant="nav"` auto-renders the right-pointing chevron — the explicit drill-in form. Reach for it when the card routes into another surface (settings detail, picker, sub-flow).
|
|
28
26
|
|
|
@@ -34,7 +32,7 @@ import { NavCard } from '@teamblind-chorus/ui';
|
|
|
34
32
|
<NavCard variant="nav" label="Cell label here" href="#" />
|
|
35
33
|
```
|
|
36
34
|
|
|
37
|
-
|
|
35
|
+
## Supporting text
|
|
38
36
|
|
|
39
37
|
Two-line variant — primary label on top, supporting metadata below at `onSurfaceVariant`. Works with either variant; pair with `nav` when the drill-in is metadata-bearing.
|
|
40
38
|
|
|
@@ -51,7 +49,7 @@ import { NavCard } from '@teamblind-chorus/ui';
|
|
|
51
49
|
/>
|
|
52
50
|
```
|
|
53
51
|
|
|
54
|
-
|
|
52
|
+
## Leading icon
|
|
55
53
|
|
|
56
54
|
A leading 16 × 16 glyph at the inline padding edge. The icon vertically centres on the row's parent block — same `align-items: center` axis as the label column and trailing slot, so the glyph sits on the same midline as the label (one-line) or label + supportingText block (two-line).
|
|
57
55
|
|
|
@@ -69,7 +67,7 @@ import { BellIcon } from '@teamblind-chorus/ui/icons';
|
|
|
69
67
|
/>
|
|
70
68
|
```
|
|
71
69
|
|
|
72
|
-
|
|
70
|
+
## Leading thumbnail
|
|
73
71
|
|
|
74
72
|
A leading 32-rung [Thumbnail](../thumbnail/thumbnail.md) — used when the drill target is an entity (channel, person, brand) rather than a chrome action. The thumbnail block-centres on the row's vertical midline, same as an icon leading.
|
|
75
73
|
|
|
@@ -87,24 +85,7 @@ import { NavCard, Thumbnail } from '@teamblind-chorus/ui';
|
|
|
87
85
|
/>
|
|
88
86
|
```
|
|
89
87
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
Multiple NavCards stacked vertically as a `NavCardGroup` — each card stays its own outlined affordance, separated by `sys.layout.stack.xs` (8px) gap. Use when several drill-in cards share a section but should read as discrete cards (vs a List drill-in rail — `text` rows with `nav: true` — where rows tile flush with hairline dividers).
|
|
93
|
-
|
|
94
|
-
```preview
|
|
95
|
-
nav-card/group
|
|
96
|
-
---
|
|
97
|
-
import { NavCard, NavCardGroup } from '@teamblind-chorus/ui';
|
|
98
|
-
import { BellIcon, BookmarkIcon, ProfileIcon } from '@teamblind-chorus/ui/icons';
|
|
99
|
-
|
|
100
|
-
<NavCardGroup aria-label="Account">
|
|
101
|
-
<NavCard variant="nav" label="Profile" supportingText="Display name, avatar, bio" leading={<ProfileIcon size={16} />} href="#" />
|
|
102
|
-
<NavCard variant="nav" label="Saved posts" supportingText="47 posts across 9 channels" leading={<BookmarkIcon size={16} />} href="#" />
|
|
103
|
-
<NavCard variant="nav" label="Notifications" leading={<BellIcon size={16} />} href="#" />
|
|
104
|
-
</NavCardGroup>
|
|
105
|
-
```
|
|
106
|
-
|
|
107
|
-
### Surface (opaque tier on a non-`surface` host)
|
|
88
|
+
## Surface
|
|
108
89
|
|
|
109
90
|
`appearance="surface"` paints the card with its own `sys.color.surface` fill so it reads as an opaque tier. Reach for it when the card sits on a transparent / non-`surface` host (coloured hero, tonal band, BottomSheet content slot) and the default transparent fill would let the card blend in.
|
|
110
91
|
|
|
@@ -129,9 +110,26 @@ import { BellIcon, BookmarkIcon, ProfileIcon } from '@teamblind-chorus/ui/icons'
|
|
|
129
110
|
</div>
|
|
130
111
|
```
|
|
131
112
|
|
|
113
|
+
## Group
|
|
114
|
+
|
|
115
|
+
Multiple NavCards stacked vertically as a `NavCardGroup` — each card stays its own outlined affordance, separated by `sys.layout.stack.xs` (8px) gap. Use when several drill-in cards share a section but should read as discrete cards (vs a List drill-in rail — `text` rows with `nav: true` — where rows tile flush with hairline dividers).
|
|
116
|
+
|
|
117
|
+
```preview
|
|
118
|
+
nav-card/group
|
|
119
|
+
---
|
|
120
|
+
import { NavCard, NavCardGroup } from '@teamblind-chorus/ui';
|
|
121
|
+
import { BellIcon, BookmarkIcon, ProfileIcon } from '@teamblind-chorus/ui/icons';
|
|
122
|
+
|
|
123
|
+
<NavCardGroup aria-label="Account">
|
|
124
|
+
<NavCard variant="nav" label="Profile" supportingText="Display name, avatar, bio" leading={<ProfileIcon size={16} />} href="#" />
|
|
125
|
+
<NavCard variant="nav" label="Saved posts" supportingText="47 posts across 9 channels" leading={<BookmarkIcon size={16} />} href="#" />
|
|
126
|
+
<NavCard variant="nav" label="Notifications" leading={<BellIcon size={16} />} href="#" />
|
|
127
|
+
</NavCardGroup>
|
|
128
|
+
```
|
|
129
|
+
|
|
132
130
|
## Slots
|
|
133
131
|
|
|
134
|
-
- **container** — outlined rounded box. `surface` fill, `radius.md` corners, hairline `
|
|
132
|
+
- **container** — outlined rounded box. `surface` fill, `radius.md` corners, hairline `border.default` stroke painted as inset box-shadow (never `border:`).
|
|
135
133
|
- **leading** *(optional)* — 16px icon (`currentColor`) or 32-rung [Thumbnail](../thumbnail/thumbnail.md). Block-centred on the row's vertical midline — same contract for icon and thumbnail. Omitted by default; label flushes to the inline padding edge.
|
|
136
134
|
- **labelCol** — vertical column holding label and (optional) supportingText. `min-width: 0` so both lines truncate.
|
|
137
135
|
- **label** — primary card text. 14px / Regular / `onSurface`. Single line; truncates.
|
|
@@ -142,7 +140,7 @@ import { BellIcon, BookmarkIcon, ProfileIcon } from '@teamblind-chorus/ui/icons'
|
|
|
142
140
|
|
|
143
141
|
| Slot | Token bindings |
|
|
144
142
|
|----------------|----------------|
|
|
145
|
-
| container | `surface` fill, `radius.md` corners, hairline `
|
|
143
|
+
| container | `surface` fill, `radius.md` corners, hairline `border.default` inset box-shadow, `48px` min-height, `8px` block / `16px` inline padding |
|
|
146
144
|
| leading | 16 × 16 (`sys.icon.md`) glyph in `currentColor` or 32 × 32 [Thumbnail](../thumbnail/thumbnail.md). Block-centred on the row's vertical midline (same contract for icon and thumbnail). `sys.layout.inline.md` (8px) gap to label column |
|
|
147
145
|
| labelCol | Flex column, `min-width: 0`, no inter-line margin (line-height carries the rhythm) |
|
|
148
146
|
| label | `sys.typo.body.sm` (14 / Regular) / `onSurface` |
|
|
@@ -172,7 +170,7 @@ A single rung. Min-height 48 (touch-target floor); consumers cannot shrink or gr
|
|
|
172
170
|
|
|
173
171
|
## Focus indicator
|
|
174
172
|
|
|
175
|
-
Outward
|
|
173
|
+
Outward single ring painted on the container's outer edge via an `::after` overlay (rest stroke sits on `::before`). Trigger: `:focus-visible`. NavCard sits as its own bounded surface with margin to siblings, so an outward ring reads cleanly — see [Focus ring composition](../../DESIGN.md#focus-ring-composition).
|
|
176
174
|
|
|
177
175
|
## Behavior
|
|
178
176
|
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"$schema": "../../spec.schema.json",
|
|
3
3
|
"name": "NavCard",
|
|
4
4
|
"family": "nav-card",
|
|
5
|
-
"description": "Outlined, rounded single-row card. Single label (optional supporting line) over a `surface`-toned box with a hairline `
|
|
5
|
+
"description": "Outlined, rounded single-row card. Single label (optional supporting line) over a `surface`-toned box with a hairline `border.default` inset-shadow stroke and `radius.md` corners. Two variants select the trailing affordance: `default` ships no trailing icon (bare labelled card — settings entry, scope tile, informational drill-target), `nav` auto-renders the right-pointing chevron for explicit drill-in semantics. Whole card is the click target — keyboard focus, hover overlay, and tap commit all sit on the card. Reach for it when one row needs to read as its own discrete affordance rather than as one entry in a [List](../list/list.md) drill-in stack (`text` rows with `nav: true`).",
|
|
6
6
|
"element": "button",
|
|
7
7
|
"props": {
|
|
8
8
|
"variant": {
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
"type": "enum",
|
|
36
36
|
"values": ["default", "surface"],
|
|
37
37
|
"default": "default",
|
|
38
|
-
"description": "Container fill. `default` is transparent — the card's identity is the outlined chrome (hairline + radius + label + chevron) and the host surface tone reads through. `surface` paints `sys.color.surface` so the card reads as its own opaque tier; reach for it when the card sits on a transparent / non-`surface` host (between bare-surface sections, on a tonal band the card needs to break out of)."
|
|
38
|
+
"description": "Container fill. `default` is transparent — the card's identity is the outlined chrome (hairline + radius + label + chevron) and the host surface tone reads through. `surface` paints `sys.color.surface.default` so the card reads as its own opaque tier; reach for it when the card sits on a transparent / non-`surface` host (between bare-surface sections, on a tonal band the card needs to break out of)."
|
|
39
39
|
},
|
|
40
40
|
"href": {
|
|
41
41
|
"type": "string",
|
|
@@ -60,7 +60,7 @@
|
|
|
60
60
|
"slots": {
|
|
61
61
|
"container": {
|
|
62
62
|
"required": true,
|
|
63
|
-
"description": "Outlined rounded box. Transparent fill by default (host tone reads through) — `surface` fill via `appearance=\"surface\"`. `radius.md` corners, hairline `
|
|
63
|
+
"description": "Outlined rounded box. Transparent fill by default (host tone reads through) — `surface` fill via `appearance=\"surface\"`. `radius.md` corners, hairline `border.default` stroke painted as inset box-shadow (never `border:`). Whole container is the interactive target.",
|
|
64
64
|
"intrinsic": true
|
|
65
65
|
},
|
|
66
66
|
"leading": {
|
|
@@ -95,17 +95,17 @@
|
|
|
95
95
|
"paddingBlock": "sys.layout.container.xs",
|
|
96
96
|
"paddingInline": "sys.layout.container.md",
|
|
97
97
|
"outlineWidth": "sys.borderWidth.hairline",
|
|
98
|
-
"outlineColor": "sys.color.
|
|
98
|
+
"outlineColor": "sys.color.border.default",
|
|
99
99
|
"leadingGap": "sys.layout.inline.md",
|
|
100
100
|
"trailingGap": "sys.layout.inline.md",
|
|
101
101
|
"leadingIconSize": "sys.icon.md",
|
|
102
102
|
"labelTypo": "sys.typo.body.sm",
|
|
103
|
-
"labelColor": "sys.color.
|
|
103
|
+
"labelColor": "sys.color.text.default",
|
|
104
104
|
"supportingTypo": "sys.typo.label.sm",
|
|
105
|
-
"supportingColor": "sys.color.
|
|
105
|
+
"supportingColor": "sys.color.text.subtle",
|
|
106
106
|
"supportingOffset": "0",
|
|
107
107
|
"trailingIconSize": "sys.icon.md",
|
|
108
|
-
"trailingIconColor": "sys.color.
|
|
108
|
+
"trailingIconColor": "sys.color.text.subtle",
|
|
109
109
|
"groupGap": "sys.layout.stack.xs"
|
|
110
110
|
},
|
|
111
111
|
"appearances": {
|
|
@@ -114,7 +114,7 @@
|
|
|
114
114
|
"note": "No fill — the host surface tone reads through. The canonical NavCard: its identity is the outlined chrome (hairline + radius + label + chevron), not a fill. Hover / pressed paint as label-tone overlays mixed on the transparent base so the host tone keeps reading through underneath the state paint."
|
|
115
115
|
},
|
|
116
116
|
"surface": {
|
|
117
|
-
"background": "sys.color.surface",
|
|
117
|
+
"background": "sys.color.surface.default",
|
|
118
118
|
"note": "Opaque `surface` fill. Reach for it when the card sits on a transparent / non-`surface` host (a `surfaceContainerLow` tonal band, a coloured hero, a BottomSheet's content slot) and should read as its own opaque tier rather than blending into the host. Outline, label, chevron, and state overlays are unchanged."
|
|
119
119
|
}
|
|
120
120
|
},
|
|
@@ -130,26 +130,26 @@
|
|
|
130
130
|
"focusRing": {
|
|
131
131
|
"composition": "outward",
|
|
132
132
|
"layer": "::after overlay — position:absolute, inset:0, no reflow (DESIGN.md Focus ring composition)",
|
|
133
|
-
"innerCounterRing": { "width": "sys.borderWidth.hairline", "color": "sys.color.
|
|
134
|
-
"outerRing": { "width": "sys.borderWidth.thin", "color": "sys.color.
|
|
133
|
+
"innerCounterRing": { "width": "sys.borderWidth.hairline", "color": "sys.color.border.focused" },
|
|
134
|
+
"outerRing": { "width": "sys.borderWidth.thin", "color": "sys.color.border.focused" }
|
|
135
135
|
},
|
|
136
|
-
"note": "Keyboard-focus (:focus-visible) visual — a
|
|
136
|
+
"note": "Keyboard-focus (:focus-visible) visual — a single outward ring on the card's outer edge, with no state-overlay tint (the ring alone carries focus here). Mirrors the `focusIndicator` block for spec-only renderers. Composes over the lifecycle state the card is in."
|
|
137
137
|
},
|
|
138
138
|
"disabled": {
|
|
139
|
-
"
|
|
140
|
-
"
|
|
139
|
+
"text": "sys.color.text.disabled",
|
|
140
|
+
"icon": "sys.color.icon.disabled",
|
|
141
|
+
"pointerEvents": "none",
|
|
142
|
+
"note": "Explicit disabled (no opacity): card text to text.disabled, icons to icon.disabled. Border stays border.default."
|
|
141
143
|
}
|
|
142
144
|
},
|
|
143
145
|
"focusIndicator": {
|
|
144
|
-
"description": "Keyboard-focus visual painted as a
|
|
146
|
+
"description": "Keyboard-focus visual painted as a single ring on the card's outer edge. Composes over whichever lifecycle state the card is in.",
|
|
145
147
|
"composition": "outward",
|
|
146
148
|
"compositionReason": "NavCard sits as its own bounded surface with margin to siblings; an outward ring reads cleanly without colliding with a neighbouring row's stroke.",
|
|
147
149
|
"ring": {
|
|
148
|
-
"
|
|
149
|
-
"
|
|
150
|
-
"
|
|
151
|
-
"insetColor": "sys.color.focusInset",
|
|
152
|
-
"implementation": "outset box-shadow on the container's `::after` overlay; the rest stroke stays painted on `::before` so the two layers don't fight."
|
|
150
|
+
"width": "sys.borderWidth.hairline",
|
|
151
|
+
"color": "sys.color.border.focused",
|
|
152
|
+
"implementation": "outset box-shadow on the container's `::after` overlay; the rest stroke stays painted on `::before` so the rest stroke and focus ring don't fight."
|
|
153
153
|
},
|
|
154
154
|
"trigger": ":focus-visible (keyboard / programmatic focus, never plain mouse click)"
|
|
155
155
|
},
|
|
@@ -33,9 +33,7 @@ import { NavList } from '@teamblind-chorus/ui';
|
|
|
33
33
|
/>
|
|
34
34
|
```
|
|
35
35
|
|
|
36
|
-
##
|
|
37
|
-
|
|
38
|
-
### With header action
|
|
36
|
+
## Header action
|
|
39
37
|
|
|
40
38
|
Extends the header with a trailing `accent` Text Button when the screen has a broader index page or "Manage" route.
|
|
41
39
|
|
|
@@ -48,11 +46,7 @@ import { NavList } from '@teamblind-chorus/ui';
|
|
|
48
46
|
label="Settings"
|
|
49
47
|
headerAction={{ label: 'Manage', href: '/settings/manage' }}
|
|
50
48
|
items={[
|
|
51
|
-
{ value: 'account',
|
|
52
|
-
{ value: 'notifications',label: 'Notifications', href: '/settings/notifications' },
|
|
53
|
-
{ value: 'privacy', label: 'Privacy', href: '/settings/privacy' },
|
|
54
|
-
{ value: 'appearance', label: 'Appearance', href: '/settings/appearance' },
|
|
55
|
-
{ value: 'language', label: 'Language', href: '/settings/language' },
|
|
49
|
+
{ value: 'account', label: 'Account', href: '/settings/account' },
|
|
56
50
|
]}
|
|
57
51
|
/>
|
|
58
52
|
```
|
|
@@ -55,17 +55,17 @@
|
|
|
55
55
|
}
|
|
56
56
|
},
|
|
57
57
|
"sizing": {
|
|
58
|
-
"containerFill": "sys.color.surface",
|
|
58
|
+
"containerFill": "sys.color.surface.default",
|
|
59
59
|
"containerPaddingBlock": "sys.layout.container.lg",
|
|
60
60
|
"containerPaddingInline": "sys.layout.container.md",
|
|
61
61
|
"headerToListGap": "sys.layout.stack.md",
|
|
62
62
|
"labelTypo": "sys.typo.heading.md",
|
|
63
|
-
"labelColor": "sys.color.
|
|
63
|
+
"labelColor": "sys.color.text.default",
|
|
64
64
|
"headerActionRendersAs": "Button variant='text' size='xsmall' appearance='accent' — link-affordance accent rule.",
|
|
65
65
|
"rowComposition": "list/entry label-only row (no thumbnail) — label.md primary, optional label.sm description line, family-default min-height (ref.space.600 = 48), leading column collapsed (0 leading-to-text gap), trailing slot filled with a default Icon Button (variant='icon', size='medium', icon=<ChevronRightIcon />).",
|
|
66
66
|
"rowInlinePaddingNote": "Each row keeps the list/entry native sys.layout.container.md inline padding (the tap target reaches the surface edge) and adds margin-inline: calc(-1 * sys.layout.container.md) so the visible label lines up with the section's content rail (aligned with the header label at 16 from the surface).",
|
|
67
67
|
"dividerWidth": "sys.borderWidth.hairline",
|
|
68
|
-
"dividerColor": "sys.color.
|
|
68
|
+
"dividerColor": "sys.color.border.default",
|
|
69
69
|
"dividerInset": "list/entry default — 16 / 16 inset from both row edges. Label-only rows always take the default inset regardless of `size` (no avatar column to anchor against)."
|
|
70
70
|
},
|
|
71
71
|
"rowProps": {
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
> 🇰🇷 한국어: [`i18n/ko/schema/components/navigation-bar/main.md`](../../../i18n/ko/schema/components/navigation-bar/main.md)
|
|
4
4
|
|
|
5
|
-
The landing-screen top bar — anchored to a tab root (feed, inbox, profile). A leading menu glyph plus left-aligned page name sit at the start; **up to four** trailing icon actions (conventionally search, chat, profile) sit at the end. Title carries the system's largest page-level rung (`typo.heading.lg`, 24/Semibold). The same row also serves a **drill-in (content-detail) screen** — pass `onBack` instead of `onMenuClick` and the leading glyph swaps to a back chevron, with the trailing cluster carrying up to four actions (see [
|
|
5
|
+
The landing-screen top bar — anchored to a tab root (feed, inbox, profile). A leading menu glyph plus left-aligned page name sit at the start; **up to four** trailing icon actions (conventionally search, chat, profile) sit at the end. Title carries the system's largest page-level rung (`typo.heading.lg`, 24/Semibold). The same row also serves a **drill-in (content-detail) screen** — pass `onBack` instead of `onMenuClick` and the leading glyph swaps to a back chevron, with the trailing cluster carrying up to four actions (see [Drill-in](#drill-in)).
|
|
6
6
|
|
|
7
7
|
**Reach for this when** the screen is a tab root and needs the menu drawer plus a small set of global affordances, **or** when it's a content-detail drill-in (a post / article reached from a feed) needing back navigation plus a share-and-save cluster. **Skip when** you need a centred title with a single action (use [Sub](./sub.md)) or are on a dedicated search page (use [Search](./search.md)).
|
|
8
8
|
|
|
@@ -36,9 +36,7 @@ import { SearchIcon, ChatIcon, ProfileIcon } from '@teamblind-chorus/ui/icons';
|
|
|
36
36
|
/>
|
|
37
37
|
```
|
|
38
38
|
|
|
39
|
-
##
|
|
40
|
-
|
|
41
|
-
### With a text title in place of the logotype
|
|
39
|
+
## Text title
|
|
42
40
|
|
|
43
41
|
Names the screen in words. Plain text at `typo.heading.lg` (24/Semibold) `onSurface`; same 24-tall rhythm as the logotype, ellipsis on narrow.
|
|
44
42
|
|
|
@@ -60,7 +58,7 @@ import { SearchIcon, ChatIcon, ProfileIcon } from '@teamblind-chorus/ui/icons';
|
|
|
60
58
|
/>
|
|
61
59
|
```
|
|
62
60
|
|
|
63
|
-
|
|
61
|
+
## Single action
|
|
64
62
|
|
|
65
63
|
Single trailing affordance — e.g. search on an Inbox screen.
|
|
66
64
|
|
|
@@ -79,7 +77,7 @@ import { SearchIcon } from '@teamblind-chorus/ui/icons';
|
|
|
79
77
|
/>
|
|
80
78
|
```
|
|
81
79
|
|
|
82
|
-
|
|
80
|
+
## Drill-in
|
|
83
81
|
|
|
84
82
|
A content-detail screen reached from a feed / list (a post, an article, a saved item). Pass `onBack` — the leading glyph becomes a back chevron — and the trailing cluster carries up to four actions (share / notify / bookmark / more). Same row, same 56-tall geometry; only the entry point differs.
|
|
85
83
|
|
|
@@ -108,7 +106,7 @@ import { ShareIcon, BellOffIcon, BookmarkIcon, EllipsisHorizontalIcon } from '@t
|
|
|
108
106
|
/>
|
|
109
107
|
```
|
|
110
108
|
|
|
111
|
-
|
|
109
|
+
## Drill-in text title
|
|
112
110
|
|
|
113
111
|
Names the source — the channel or author the post belongs to. The cluster is `1..4` actions; drop to just the essentials (share + more) when the screen has fewer affordances.
|
|
114
112
|
|
|
@@ -129,7 +127,7 @@ import { ShareIcon, EllipsisHorizontalIcon } from '@teamblind-chorus/ui/icons';
|
|
|
129
127
|
/>
|
|
130
128
|
```
|
|
131
129
|
|
|
132
|
-
|
|
130
|
+
## Truncation
|
|
133
131
|
|
|
134
132
|
Long page name truncates with ellipsis. Author concise titles (*Home*, *Inbox*) so the bar never resorts to ellipsis.
|
|
135
133
|
|
|
@@ -162,9 +160,9 @@ import { SearchIcon, ChatIcon, ProfileIcon } from '@teamblind-chorus/ui/icons';
|
|
|
162
160
|
| Slot | Container | Color |
|
|
163
161
|
|-----------------------|--------------------|------------------------------------------|
|
|
164
162
|
| **Bar container** | `sys.color.surface` fill, 8px block / 16px inline padding, no border, no shadow at rest. | — |
|
|
165
|
-
| **Leading icon** | Transparent capsule, 24px glyph centred. | `sys.color.
|
|
166
|
-
| **Title** | Brand logotype `<img>` at 24px tall (width auto) by default; plain-text fallback at `heading.lg`. Not interactive. | `sys.color.
|
|
167
|
-
| **Trailing icon(s)** | Transparent capsule, 24px glyph centred. Capsules sit flush, no inter-icon gap. | `sys.color.
|
|
163
|
+
| **Leading icon** | Transparent capsule, 24px glyph centred. | `sys.color.text.default` |
|
|
164
|
+
| **Title** | Brand logotype `<img>` at 24px tall (width auto) by default; plain-text fallback at `heading.lg`. Not interactive. | `sys.color.text.default` (text fallback) |
|
|
165
|
+
| **Trailing icon(s)** | Transparent capsule, 24px glyph centred. Capsules sit flush, no inter-icon gap. | `sys.color.text.default` |
|
|
168
166
|
|
|
169
167
|
## Sizes
|
|
170
168
|
|
|
@@ -80,13 +80,13 @@
|
|
|
80
80
|
"trailingIconSize": "sys.icon.lg"
|
|
81
81
|
},
|
|
82
82
|
"appearance": {
|
|
83
|
-
"containerFill": "sys.color.surface",
|
|
84
|
-
"leadingIconColor": "sys.color.
|
|
85
|
-
"titleColor": "sys.color.
|
|
86
|
-
"trailingIconColor": "sys.color.
|
|
83
|
+
"containerFill": "sys.color.surface.default",
|
|
84
|
+
"leadingIconColor": "sys.color.icon.default",
|
|
85
|
+
"titleColor": "sys.color.text.default",
|
|
86
|
+
"trailingIconColor": "sys.color.icon.default"
|
|
87
87
|
},
|
|
88
88
|
"states": {
|
|
89
|
-
"note": "Bar itself has no interactive state. Icon slots carry the standard Icon Button state recipe — default / hovered (sys.state.hover overlay) / pressed (sys.state.pressed overlay) / disabled / focused (
|
|
89
|
+
"note": "Bar itself has no interactive state. Icon slots carry the standard Icon Button state recipe — default / hovered (sys.state.hover overlay) / pressed (sys.state.pressed overlay) / disabled / focused (single focus ring). Title carries no states."
|
|
90
90
|
},
|
|
91
91
|
"focusIndicator": {
|
|
92
92
|
"description": "The bar itself isn't a focus target. Its action slots (leading menu, trailing icons) inherit each control's own focus composition — Icon Button → Outward — so the ring belongs to whichever capsule the keyboard lands on. See the contained sub-components for the visual contract.",
|
|
@@ -100,7 +100,7 @@
|
|
|
100
100
|
"trailingIconCeiling": "Three is the conventional ceiling on a tab root; the drill-in (content-detail) action cluster uses up to four (share / notify / bookmark / more). A fifth belongs in the `more` (•••) overflow — the component slices to four."
|
|
101
101
|
},
|
|
102
102
|
"forbidden": [
|
|
103
|
-
"brand color on the title / wordmark — header chrome stays on sys.color.surface; the wordmark paints sys.color.
|
|
103
|
+
"brand color on the title / wordmark — header chrome stays on sys.color.surface.default; the wordmark paints sys.color.text.default",
|
|
104
104
|
"more than four trailing actions — the action cluster caps at four; a fifth belongs in the `more` overflow",
|
|
105
105
|
"leading glyph other than the menu (tab root) or back chevron (drill-in, via onBack) — never an arbitrary custom glyph",
|
|
106
106
|
"centred title — the Main title is left-aligned next to the leading glyph (a centred title is the `sub` variant)",
|
|
@@ -20,9 +20,7 @@ import { NavigationBar } from '@teamblind-chorus/ui';
|
|
|
20
20
|
<NavigationBar variant="search" placeholder="Search by keyword" onBack={() => {}} />
|
|
21
21
|
```
|
|
22
22
|
|
|
23
|
-
##
|
|
24
|
-
|
|
25
|
-
### With value (clear visible)
|
|
23
|
+
## Value
|
|
26
24
|
|
|
27
25
|
A non-empty value swaps placeholder for `onSurface` text and reveals the trailing clear (*×*) at the medium 32 × 32 capsule — smaller than the leading back-arrow so it never out-shouts the input. Clicking clear wipes the value, returns focus, and the trailing column collapses; the input's leading edge stays pixel-stable.
|
|
28
26
|
|
|
@@ -42,7 +40,7 @@ import { NavigationBar } from '@teamblind-chorus/ui';
|
|
|
42
40
|
## Slots
|
|
43
41
|
|
|
44
42
|
- **leading** *(required)* — 24px back-arrow as the canonical [Icon Button](../button/icon.md) capsule (40 × 40 transparent, 24px glyph).
|
|
45
|
-
- **input** *(required)* — single-line *bare* text input filling the leftover middle column. Bare means no border, no background, no inset stroke — not a [Search](../form-field/search.md) field. Value in `sys.color.
|
|
43
|
+
- **input** *(required)* — single-line *bare* text input filling the leftover middle column. Bare means no border, no background, no inset stroke — not a [Search](../form-field/search.md) field. Value in `sys.color.text.default`, placeholder in `sys.color.border.boldest` (`typo.body.md`, 16/Regular). Caret follows the [system caret rule](../../DESIGN.md#caret).
|
|
46
44
|
- **trailing** *(conditional)* — clear (*×*) [Icon Button](../button/icon.md) hosting `XCircleFillIcon`. Always Icon Button's `medium` size (32 × 32 capsule, 16px glyph) so it never over-claims weight against the bare input. Rendered only when value is non-empty; wipes value and returns focus.
|
|
47
45
|
|
|
48
46
|
## Anatomy
|
|
@@ -51,10 +49,10 @@ Three-column grid (leading / input / trailing) — side columns size to content,
|
|
|
51
49
|
|
|
52
50
|
| Slot | Container | Color |
|
|
53
51
|
|-----------------------|------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------|
|
|
54
|
-
| **Bar container** | `sys.color.surface` fill, 8px block / 16px inline padding, no shadow at rest, **1px bottom divider in `sys.color.
|
|
55
|
-
| **Leading** | Transparent Icon Button capsule (8 padding around 24px glyph). | `sys.color.
|
|
56
|
-
| **Input** | Bare text — no border, no background, no inset stroke. | Value: `sys.color.
|
|
57
|
-
| **Trailing (clear)** | Transparent Icon Button **medium** capsule (32 × 32, 16px glyph). | `sys.color.
|
|
52
|
+
| **Bar container** | `sys.color.surface` fill, 8px block / 16px inline padding, no shadow at rest, **1px bottom divider in `sys.color.border.default`** painted as inset `box-shadow`. | — |
|
|
53
|
+
| **Leading** | Transparent Icon Button capsule (8 padding around 24px glyph). | `sys.color.text.default` |
|
|
54
|
+
| **Input** | Bare text — no border, no background, no inset stroke. | Value: `sys.color.text.default`. Placeholder: `sys.color.border.boldest`. Caret: `sys.color.background.primary`. |
|
|
55
|
+
| **Trailing (clear)** | Transparent Icon Button **medium** capsule (32 × 32, 16px glyph). | `sys.color.text.default` |
|
|
58
56
|
|
|
59
57
|
The 1px bottom divider (unique to Search) keeps the bare input from bleeding into the results list below; painted as inset `box-shadow` so it never participates in layout.
|
|
60
58
|
|
|
@@ -65,7 +65,7 @@
|
|
|
65
65
|
},
|
|
66
66
|
"input": {
|
|
67
67
|
"required": true,
|
|
68
|
-
"description": "Single-line bare text input that fills the leftover middle column. Bare = no `border`, no `background`, no inset stroke — it is *not* a [Search bar](../form-field/search.md) field. Renders the value in `sys.color.
|
|
68
|
+
"description": "Single-line bare text input that fills the leftover middle column. Bare = no `border`, no `background`, no inset stroke — it is *not* a [Search bar](../form-field/search.md) field. Renders the value in `sys.color.text.default` when present; renders the placeholder in `sys.color.border.boldest` when empty. `body.md` (16/Regular). The bar carries the visible search affordance via the placeholder and the page context; the field itself has no chrome of its own.",
|
|
69
69
|
"accepts": [
|
|
70
70
|
"text"
|
|
71
71
|
]
|
|
@@ -91,20 +91,20 @@
|
|
|
91
91
|
"trailingIconSize": "sys.icon.md"
|
|
92
92
|
},
|
|
93
93
|
"appearance": {
|
|
94
|
-
"containerFill": "sys.color.surface",
|
|
95
|
-
"containerBottomDivider": "sys.color.
|
|
96
|
-
"leadingColor": "sys.color.
|
|
97
|
-
"inputText": "sys.color.
|
|
98
|
-
"inputPlaceholder": "sys.color.
|
|
99
|
-
"inputCaret": "sys.color.
|
|
100
|
-
"trailingColor": "sys.color.
|
|
94
|
+
"containerFill": "sys.color.surface.default",
|
|
95
|
+
"containerBottomDivider": "sys.color.border.default",
|
|
96
|
+
"leadingColor": "sys.color.text.default",
|
|
97
|
+
"inputText": "sys.color.text.default",
|
|
98
|
+
"inputPlaceholder": "sys.color.border.boldest",
|
|
99
|
+
"inputCaret": "sys.color.text.default",
|
|
100
|
+
"trailingColor": "sys.color.text.default"
|
|
101
101
|
},
|
|
102
102
|
"layout": {
|
|
103
103
|
"grid": "Three-column grid: leading / input / trailing. Side columns size to content (auto); the input column takes the leftover space (`minmax(0, 1fr)`). Same shape as Page's grid — only the centre slot's contents differ (a bare input instead of a centred title). When the trailing column collapses (value empty → clear hidden), the input column expands to consume the freed space; the field never reflows its leading edge.",
|
|
104
104
|
"noTitleSlot": "There is no separate title slot; the input fills the role the Page bar would give the title. Adding a title above or beside the input would compete with the placeholder for the search affordance and break the bar's 56 footprint."
|
|
105
105
|
},
|
|
106
106
|
"states": {
|
|
107
|
-
"note": "The bar carries no interactive state of its own. The leading and trailing slots inherit Icon Button's recipe (default / hovered / pressed / focused — overlays +
|
|
107
|
+
"note": "The bar carries no interactive state of its own. The leading and trailing slots inherit Icon Button's recipe (default / hovered / pressed / focused — overlays + single focus ring). The Search bar deliberately omits a `disabled` state: the only screen a `navigation-bar/search` ever lives on is the search results page itself, and a non-typable search bar on that page reduces the surface to a dead chrome strip with no escape affordance beyond the back-arrow. If search must be gated (offline / throttled / paused indexing), gate the *trigger* on the prior screen instead and never route into this bar. The input slot follows the bare-text-field shape — no border, no fill, no rest-vs-active stroke; only the caret and the placeholder→value colour swap signal interaction. See `inputStates` for the input-only details.",
|
|
108
108
|
"inputStates": {
|
|
109
109
|
"default": {
|
|
110
110
|
"caret": "hidden",
|
|
@@ -25,9 +25,7 @@ import { NavigationBar, Button } from '@teamblind-chorus/ui';
|
|
|
25
25
|
/>
|
|
26
26
|
```
|
|
27
27
|
|
|
28
|
-
##
|
|
29
|
-
|
|
30
|
-
### With icon trailing
|
|
28
|
+
## Icon trailing
|
|
31
29
|
|
|
32
30
|
The trailing slot carries a single icon.
|
|
33
31
|
|
|
@@ -44,7 +42,7 @@ import { NavigationBar } from '@teamblind-chorus/ui';
|
|
|
44
42
|
/>
|
|
45
43
|
```
|
|
46
44
|
|
|
47
|
-
|
|
45
|
+
## Text Button trailing
|
|
48
46
|
|
|
49
47
|
The trailing slot carries a [Text Button](../button/text.md) — *Skip* or *Done*. Reads as inline 16/Semibold `primary` type at rest.
|
|
50
48
|
|
|
@@ -61,7 +59,7 @@ import { NavigationBar } from '@teamblind-chorus/ui';
|
|
|
61
59
|
/>
|
|
62
60
|
```
|
|
63
61
|
|
|
64
|
-
|
|
62
|
+
## Text Button pair
|
|
65
63
|
|
|
66
64
|
Modal composer bars carry a [Text Button](../button/text.md) on **both** sides — leading `default` appearance for the dismissing *Cancel*, trailing `accent` for the committing *Post* — and **omit the page title**: the pair says everything, and the centre cell keeps a non-heading placeholder so both buttons hold their edge columns. The accent side is the single CTA of the bar; never paint both sides accent, and never swap the sides (commit always trails).
|
|
67
65
|
|
|
@@ -77,7 +75,7 @@ import { Button, NavigationBar } from '@teamblind-chorus/ui';
|
|
|
77
75
|
/>
|
|
78
76
|
```
|
|
79
77
|
|
|
80
|
-
|
|
78
|
+
## External page
|
|
81
79
|
|
|
82
80
|
External content visited in-app (embedded webview, in-app browser). Leading drops — no flow to step back; trailing is a single close (×) [Icon Button](../button/icon.md).
|
|
83
81
|
|
|
@@ -93,7 +91,7 @@ import { NavigationBar } from '@teamblind-chorus/ui';
|
|
|
93
91
|
/>
|
|
94
92
|
```
|
|
95
93
|
|
|
96
|
-
|
|
94
|
+
## Title only
|
|
97
95
|
|
|
98
96
|
Both side slots empty — for non-dismissible sub-pages (forced confirmation, terms gate).
|
|
99
97
|
|
|
@@ -108,7 +106,7 @@ import { NavigationBar } from '@teamblind-chorus/ui';
|
|
|
108
106
|
/>
|
|
109
107
|
```
|
|
110
108
|
|
|
111
|
-
|
|
109
|
+
## Overlay
|
|
112
110
|
|
|
113
111
|
`appearance="overlay"` paints a transparent container with fixed-white icons and title — the bar floats over a hero / cover image (canonical host: [Profile header](../profile-header/profile-header.md)). The image beneath provides contrast; theme tokens don't apply. The bar still pays its own `env(safe-area-inset-top)` so the status-bar zone reads cleanly. Staged here via [ProfileHeader](../profile-header/profile-header.md) so the preview matches a real-world consumer.
|
|
114
112
|
|
|
@@ -137,9 +135,9 @@ import { ProfileHeader } from '@teamblind-chorus/ui';
|
|
|
137
135
|
| Slot | Container | Color |
|
|
138
136
|
|-----------------------|--------------------|------------------------------------------|
|
|
139
137
|
| **Bar container** | `sys.color.surface` fill, 8px block / 16px inline padding, no border, no shadow at rest. | — |
|
|
140
|
-
| **Leading** | Transparent icon capsule (8 padding around 24px glyph). | `sys.color.
|
|
141
|
-
| **Title** | Plain text, centred horizontally — not interactive. | `sys.color.
|
|
142
|
-
| **Trailing** | Transparent icon capsule, [Toolbar Button](../button/toolbar.md), or [Text Button](../button/text.md). | `sys.color.
|
|
138
|
+
| **Leading** | Transparent icon capsule (8 padding around 24px glyph). | `sys.color.text.default` |
|
|
139
|
+
| **Title** | Plain text, centred horizontally — not interactive. | `sys.color.text.default` |
|
|
140
|
+
| **Trailing** | Transparent icon capsule, [Toolbar Button](../button/toolbar.md), or [Text Button](../button/text.md). | `sys.color.text.default` (icon / Toolbar) or `sys.color.background.primary` (Text Button) |
|
|
143
141
|
|
|
144
142
|
## Sizes
|
|
145
143
|
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
"overlay"
|
|
18
18
|
],
|
|
19
19
|
"default": "surface",
|
|
20
|
-
"description": "Container fill and foreground tone. `surface` (default) — opaque `sys.color.surface` fill, `onSurface` icons and title; the canonical page-chrome treatment. `overlay` — transparent container with **fixed white** icons (`ref.palette.white.1000`), used when the bar floats over a hero/cover image (e.g. inside [Profile header](../profile-header/profile-header.md)). In `overlay`, the title slot is intentionally muted — pass `title=\"\"` (empty string) when the host already carries page identity below the bar."
|
|
20
|
+
"description": "Container fill and foreground tone. `surface` (default) — opaque `sys.color.surface.default` fill, `onSurface` icons and title; the canonical page-chrome treatment. `overlay` — transparent container with **fixed white** icons (`ref.palette.white.1000`), used when the bar floats over a hero/cover image (e.g. inside [Profile header](../profile-header/profile-header.md)). In `overlay`, the title slot is intentionally muted — pass `title=\"\"` (empty string) when the host already carries page identity below the bar."
|
|
21
21
|
},
|
|
22
22
|
"title": {
|
|
23
23
|
"type": "string",
|
|
@@ -74,11 +74,11 @@
|
|
|
74
74
|
},
|
|
75
75
|
"appearances": {
|
|
76
76
|
"surface": {
|
|
77
|
-
"containerFill": "sys.color.surface",
|
|
78
|
-
"leadingColor": "sys.color.
|
|
79
|
-
"titleColor": "sys.color.
|
|
80
|
-
"trailingColor": "sys.color.
|
|
81
|
-
"trailingTextButtonColor": "sys.color.
|
|
77
|
+
"containerFill": "sys.color.surface.default",
|
|
78
|
+
"leadingColor": "sys.color.text.default",
|
|
79
|
+
"titleColor": "sys.color.text.default",
|
|
80
|
+
"trailingColor": "sys.color.text.default",
|
|
81
|
+
"trailingTextButtonColor": "sys.color.text.link",
|
|
82
82
|
"default": true,
|
|
83
83
|
"note": "Canonical page chrome — opaque surface fill with onSurface foreground."
|
|
84
84
|
},
|
|
@@ -96,7 +96,7 @@
|
|
|
96
96
|
"titleCentring": "Title is anchored to the bar's geometric horizontal centre — measured against the page component container, not against the side slots. A 40px back arrow paired with a 80px primary Toolbar Button still leaves the title perfectly centred; the side slots anchor at the start / end of their respective halves and never displace the title. The action always stays intact; title truncation (ellipsis) kicks in if the natural width exceeds what the bar can give it."
|
|
97
97
|
},
|
|
98
98
|
"states": {
|
|
99
|
-
"note": "Bar itself has no interactive state. Side slots inherit the state recipe of whichever control they host (Icon Button / Toolbar Button / Text Button) — default / hovered / pressed / disabled / focused via the standard overlays +
|
|
99
|
+
"note": "Bar itself has no interactive state. Side slots inherit the state recipe of whichever control they host (Icon Button / Toolbar Button / Text Button) — default / hovered / pressed / disabled / focused via the standard overlays + single focus ring. Title carries no states."
|
|
100
100
|
},
|
|
101
101
|
"focusIndicator": {
|
|
102
102
|
"description": "The bar itself isn't a focus target. Its action slots inherit each control's own focus composition — Icon Button → Outward, Toolbar Button → Outward, Text Button → Outward — so the ring belongs to whichever control the keyboard lands on. See the contained sub-components for the visual contract.",
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"$schema": "../../family.schema.json",
|
|
3
3
|
"family": "pagination",
|
|
4
4
|
"name": "Pagination",
|
|
5
|
-
"description": "Decorative dot-position indicator for one-page-at-a-time pagers. An inline element (`span` / inline-flex) sized to its dots — one 6px dot per page; the active dot paints `sys.color.
|
|
5
|
+
"description": "Decorative dot-position indicator for one-page-at-a-time pagers. An inline element (`span` / inline-flex) sized to its dots — one 6px dot per page; the active dot paints `sys.color.text.default`, the rest paint `sys.color.border.default`. Presentational only (`aria-hidden`) — the host pager owns horizontal placement (the carousels center it), scroll position, active-index tracking, and keyboard reach; tapping a dot does not navigate. Renders nothing below two pages. Single appearance, single rung. Single-spec family.",
|
|
6
6
|
"useCases": [
|
|
7
7
|
"carousel page position (PostCarousel / ProfileCarousel)",
|
|
8
8
|
"swipeable media gallery position",
|