@teamblind-chorus/ui 1.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/LICENSE +21 -0
- package/README.md +112 -0
- package/agents/AGENTS.md +143 -0
- package/agents/DESIGN.md +1311 -0
- package/agents/LOVABLE.md +472 -0
- package/agents/anti-patterns.md +533 -0
- package/agents/catalog.md +232 -0
- package/agents/components/avatar-rail/avatar-rail.family.json +46 -0
- package/agents/components/avatar-rail/avatar-rail.md +103 -0
- package/agents/components/avatar-rail/avatar-rail.spec.json +160 -0
- package/agents/components/badge/badge.family.json +45 -0
- package/agents/components/badge/badge.md +10 -0
- package/agents/components/badge/role.md +100 -0
- package/agents/components/badge/role.spec.json +75 -0
- package/agents/components/badge/update.md +132 -0
- package/agents/components/badge/update.spec.json +114 -0
- package/agents/components/banner/banner.family.json +28 -0
- package/agents/components/banner/banner.md +136 -0
- package/agents/components/banner/banner.spec.json +136 -0
- package/agents/components/bottom-sheet/bottom-sheet.family.json +29 -0
- package/agents/components/bottom-sheet/bottom-sheet.md +176 -0
- package/agents/components/bottom-sheet/bottom-sheet.spec.json +168 -0
- package/agents/components/bubble/bubble.family.json +29 -0
- package/agents/components/bubble/bubble.md +134 -0
- package/agents/components/bubble/bubble.spec.json +91 -0
- package/agents/components/button/button.family.json +76 -0
- package/agents/components/button/button.md +31 -0
- package/agents/components/button/check.md +138 -0
- package/agents/components/button/check.spec.json +161 -0
- package/agents/components/button/fab.md +161 -0
- package/agents/components/button/fab.spec.json +106 -0
- package/agents/components/button/icon.md +141 -0
- package/agents/components/button/icon.spec.json +164 -0
- package/agents/components/button/standard.md +219 -0
- package/agents/components/button/standard.spec.json +205 -0
- package/agents/components/button/text.md +186 -0
- package/agents/components/button/text.spec.json +215 -0
- package/agents/components/button/toggle.md +108 -0
- package/agents/components/button/toggle.spec.json +124 -0
- package/agents/components/button/toolbar.md +189 -0
- package/agents/components/button/toolbar.spec.json +109 -0
- package/agents/components/carousel/carousel.family.json +41 -0
- package/agents/components/carousel/carousel.md +40 -0
- package/agents/components/carousel/post.md +148 -0
- package/agents/components/carousel/post.spec.json +229 -0
- package/agents/components/carousel/profile.md +184 -0
- package/agents/components/carousel/profile.spec.json +219 -0
- package/agents/components/chip/chip.family.json +37 -0
- package/agents/components/chip/chip.md +10 -0
- package/agents/components/chip/filter.md +212 -0
- package/agents/components/chip/filter.spec.json +124 -0
- package/agents/components/chip/tag.md +137 -0
- package/agents/components/chip/tag.spec.json +104 -0
- package/agents/components/dialog/dialog.family.json +29 -0
- package/agents/components/dialog/dialog.md +113 -0
- package/agents/components/dialog/dialog.spec.json +156 -0
- package/agents/components/directory-list/directory-list.family.json +46 -0
- package/agents/components/directory-list/directory-list.md +87 -0
- package/agents/components/directory-list/directory-list.spec.json +104 -0
- package/agents/components/divider/divider.family.json +28 -0
- package/agents/components/divider/divider.md +78 -0
- package/agents/components/divider/divider.spec.json +51 -0
- package/agents/components/feed/ad.md +108 -0
- package/agents/components/feed/ad.spec.json +187 -0
- package/agents/components/feed/feed.family.json +48 -0
- package/agents/components/feed/feed.md +30 -0
- package/agents/components/feed/post.md +240 -0
- package/agents/components/feed/post.spec.json +361 -0
- package/agents/components/form-field/form-field.family.json +50 -0
- package/agents/components/form-field/form-field.md +11 -0
- package/agents/components/form-field/input.md +198 -0
- package/agents/components/form-field/input.spec.json +202 -0
- package/agents/components/form-field/search.md +81 -0
- package/agents/components/form-field/search.spec.json +135 -0
- package/agents/components/form-field/select.md +101 -0
- package/agents/components/form-field/select.spec.json +194 -0
- package/agents/components/form-field/textarea.md +89 -0
- package/agents/components/form-field/textarea.spec.json +176 -0
- package/agents/components/header/header.family.json +43 -0
- package/agents/components/header/header.md +18 -0
- package/agents/components/header/main.md +101 -0
- package/agents/components/header/main.spec.json +117 -0
- package/agents/components/header/sub.md +129 -0
- package/agents/components/header/sub.spec.json +81 -0
- package/agents/components/list/accordion.md +183 -0
- package/agents/components/list/accordion.spec.json +201 -0
- package/agents/components/list/entry.md +280 -0
- package/agents/components/list/entry.spec.json +237 -0
- package/agents/components/list/list.family.json +75 -0
- package/agents/components/list/list.md +24 -0
- package/agents/components/list/radio.md +144 -0
- package/agents/components/list/radio.spec.json +186 -0
- package/agents/components/list/standard.md +262 -0
- package/agents/components/list/standard.spec.json +221 -0
- package/agents/components/metadata/compact.md +69 -0
- package/agents/components/metadata/compact.spec.json +69 -0
- package/agents/components/metadata/metadata.family.json +42 -0
- package/agents/components/metadata/metadata.md +26 -0
- package/agents/components/metadata/standard.md +104 -0
- package/agents/components/metadata/standard.spec.json +152 -0
- package/agents/components/nav-card/nav-card.family.json +29 -0
- package/agents/components/nav-card/nav-card.md +179 -0
- package/agents/components/nav-card/nav-card.spec.json +161 -0
- package/agents/components/nav-list/nav-list.family.json +46 -0
- package/agents/components/nav-list/nav-list.md +91 -0
- package/agents/components/nav-list/nav-list.spec.json +107 -0
- package/agents/components/navigation-bar/main.md +201 -0
- package/agents/components/navigation-bar/main.spec.json +109 -0
- package/agents/components/navigation-bar/navigation-bar.family.json +44 -0
- package/agents/components/navigation-bar/navigation-bar.md +21 -0
- package/agents/components/navigation-bar/search.md +96 -0
- package/agents/components/navigation-bar/search.spec.json +142 -0
- package/agents/components/navigation-bar/sub.md +174 -0
- package/agents/components/navigation-bar/sub.spec.json +123 -0
- package/agents/components/page-shell/page-shell.family.json +22 -0
- package/agents/components/page-shell/page-shell.md +51 -0
- package/agents/components/profile-header/profile-header.family.json +29 -0
- package/agents/components/profile-header/profile-header.md +149 -0
- package/agents/components/profile-header/profile-header.spec.json +200 -0
- package/agents/components/progress/progress.family.json +27 -0
- package/agents/components/progress/progress.md +38 -0
- package/agents/components/progress/progress.spec.json +67 -0
- package/agents/components/side-sheet/side-sheet.family.json +30 -0
- package/agents/components/side-sheet/side-sheet.md +154 -0
- package/agents/components/side-sheet/side-sheet.spec.json +109 -0
- package/agents/components/skeleton/skeleton.family.json +28 -0
- package/agents/components/skeleton/skeleton.md +123 -0
- package/agents/components/skeleton/skeleton.spec.json +73 -0
- package/agents/components/status-tag/status-tag.family.json +26 -0
- package/agents/components/status-tag/status-tag.md +114 -0
- package/agents/components/status-tag/status-tag.spec.json +69 -0
- package/agents/components/suggestion-list/suggestion-list.family.json +46 -0
- package/agents/components/suggestion-list/suggestion-list.md +91 -0
- package/agents/components/suggestion-list/suggestion-list.spec.json +178 -0
- package/agents/components/switch/switch.family.json +27 -0
- package/agents/components/switch/switch.md +114 -0
- package/agents/components/switch/switch.spec.json +123 -0
- package/agents/components/tab-bar/tab-bar.family.json +27 -0
- package/agents/components/tab-bar/tab-bar.md +178 -0
- package/agents/components/tab-bar/tab-bar.spec.json +184 -0
- package/agents/components/tabs/rounded.md +150 -0
- package/agents/components/tabs/rounded.spec.json +140 -0
- package/agents/components/tabs/segmented.md +114 -0
- package/agents/components/tabs/segmented.spec.json +100 -0
- package/agents/components/tabs/tabs.family.json +59 -0
- package/agents/components/tabs/tabs.md +18 -0
- package/agents/components/tabs/underline.md +147 -0
- package/agents/components/tabs/underline.spec.json +139 -0
- package/agents/components/thumbnail/thumbnail.family.json +28 -0
- package/agents/components/thumbnail/thumbnail.md +152 -0
- package/agents/components/thumbnail/thumbnail.spec.json +172 -0
- package/agents/components/toast/toast.family.json +28 -0
- package/agents/components/toast/toast.md +133 -0
- package/agents/components/toast/toast.spec.json +89 -0
- package/agents/components/tooltip/tooltip.family.json +29 -0
- package/agents/components/tooltip/tooltip.md +139 -0
- package/agents/components/tooltip/tooltip.spec.json +110 -0
- package/agents/compose.md +240 -0
- package/agents/icons.json +831 -0
- package/agents/images.md +66 -0
- package/agents/manifest.json +87 -0
- package/agents/patterns/README.md +59 -0
- package/agents/patterns/actions.md +50 -0
- package/agents/patterns/browsing.md +52 -0
- package/agents/patterns/communications.md +56 -0
- package/agents/patterns/layout.md +72 -0
- package/agents/patterns/modals.md +50 -0
- package/agents/patterns/visual.md +55 -0
- package/agents/reconstruct.md +55 -0
- package/agents/scoped-adoption.md +111 -0
- package/agents/tokens.usage.json +1657 -0
- package/agents/usage.json +422 -0
- package/dist/icons/index.cjs +1332 -0
- package/dist/icons/index.cjs.map +1 -0
- package/dist/icons/index.d.cts +228 -0
- package/dist/icons/index.d.ts +228 -0
- package/dist/icons/index.js +1114 -0
- package/dist/icons/index.js.map +1 -0
- package/dist/index.cjs +5905 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +896 -0
- package/dist/index.d.ts +896 -0
- package/dist/index.js +5847 -0
- package/dist/index.js.map +1 -0
- package/dist/styles.css +5765 -0
- package/eslint/README.md +79 -0
- package/eslint/index.js +78 -0
- package/eslint/rules.js +472 -0
- package/eslint/test.mjs +135 -0
- package/package.json +96 -0
- package/placeholder.png +0 -0
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "../../spec.schema.json",
|
|
3
|
+
"name": "Button",
|
|
4
|
+
"family": "button",
|
|
5
|
+
"subcomponent": "text",
|
|
6
|
+
"description": "Link-shaped commit surface — looks like a text link at rest (no fill, no border, label-coloured text) but paints a button-like hover overlay and a standard focus ring when interacted with. Reach for this whenever an inline affordance lives next to typographic content and should read as part of the text rhythm rather than as a separate filled control. Three sizes (medium / small / xsmall) for the row's density, four appearances (default / accent / onPrimary / inverse) for emphasis and host surface.",
|
|
7
|
+
"element": "button",
|
|
8
|
+
"props": {
|
|
9
|
+
"variant": {
|
|
10
|
+
"type": "literal",
|
|
11
|
+
"value": "text"
|
|
12
|
+
},
|
|
13
|
+
"size": {
|
|
14
|
+
"type": "enum",
|
|
15
|
+
"values": [
|
|
16
|
+
"medium",
|
|
17
|
+
"small",
|
|
18
|
+
"xsmall"
|
|
19
|
+
],
|
|
20
|
+
"default": "medium"
|
|
21
|
+
},
|
|
22
|
+
"appearance": {
|
|
23
|
+
"type": "enum",
|
|
24
|
+
"values": [
|
|
25
|
+
"default",
|
|
26
|
+
"accent",
|
|
27
|
+
"onPrimary",
|
|
28
|
+
"inverse"
|
|
29
|
+
],
|
|
30
|
+
"default": "default"
|
|
31
|
+
},
|
|
32
|
+
"leadingIcon": {
|
|
33
|
+
"type": "node",
|
|
34
|
+
"optional": true,
|
|
35
|
+
"description": "Optional context glyph rendered before the label (e.g. a back chevron next to a 'Back' label, an external-link glyph next to 'Open'). Sized 16px on every rung — the glyph stays legible on the dense `xsmall` rung where the label drops to 12-rank type."
|
|
36
|
+
},
|
|
37
|
+
"trailingIcon": {
|
|
38
|
+
"type": "node",
|
|
39
|
+
"optional": true,
|
|
40
|
+
"description": "Optional directional / destination glyph rendered after the label (e.g. a chevron-right next to 'Continue', an external-link glyph next to 'Open in new tab'). Same per-size footprint as the leading slot. For the **dropdown** pattern (see `useCases.dropdown`), pass `<ChevronDownIcon />` at rest and flip to `<ChevronUpIcon />` while the menu is expanded — the chevron is a state signal tied to `aria-expanded`, never decorative."
|
|
41
|
+
},
|
|
42
|
+
"disabled": {
|
|
43
|
+
"type": "boolean",
|
|
44
|
+
"default": false
|
|
45
|
+
}
|
|
46
|
+
},
|
|
47
|
+
"slots": {
|
|
48
|
+
"label": {
|
|
49
|
+
"required": true,
|
|
50
|
+
"description": "Button's accessible name. Required, single line.",
|
|
51
|
+
"accepts": [
|
|
52
|
+
"text"
|
|
53
|
+
]
|
|
54
|
+
},
|
|
55
|
+
"leadingIcon": {
|
|
56
|
+
"required": false,
|
|
57
|
+
"description": "Context glyph before the label. 16px (`sys.icon.md`) on every rung — the glyph stays legible on the dense `xsmall` rung where the label drops to 12-rank type. Inherits the label color via `currentColor`. `aria-hidden`.",
|
|
58
|
+
"accepts": [
|
|
59
|
+
"icon"
|
|
60
|
+
]
|
|
61
|
+
},
|
|
62
|
+
"trailingIcon": {
|
|
63
|
+
"required": false,
|
|
64
|
+
"description": "Directional / destination glyph after the label. Same per-size footprint as the leading slot, inherits the label's color. `aria-hidden`.",
|
|
65
|
+
"accepts": [
|
|
66
|
+
"icon"
|
|
67
|
+
]
|
|
68
|
+
}
|
|
69
|
+
},
|
|
70
|
+
"sizes": {
|
|
71
|
+
"medium": {
|
|
72
|
+
"minHeight": "ref.space.500",
|
|
73
|
+
"paddingBlock": "sys.layout.container.xs",
|
|
74
|
+
"paddingInline": "sys.layout.container.xs",
|
|
75
|
+
"radius": "sys.radius.full",
|
|
76
|
+
"labelTypo": "sys.typo.heading.sm",
|
|
77
|
+
"iconSize": "sys.icon.md",
|
|
78
|
+
"slotGap": "sys.layout.inline.sm"
|
|
79
|
+
},
|
|
80
|
+
"small": {
|
|
81
|
+
"minHeight": "ref.space.400",
|
|
82
|
+
"paddingBlock": "sys.layout.container.2xs",
|
|
83
|
+
"paddingInline": "sys.layout.container.xs",
|
|
84
|
+
"radius": "sys.radius.full",
|
|
85
|
+
"labelTypo": "sys.typo.label.md",
|
|
86
|
+
"iconSize": "sys.icon.md",
|
|
87
|
+
"slotGap": "sys.layout.inline.sm"
|
|
88
|
+
},
|
|
89
|
+
"xsmall": {
|
|
90
|
+
"minHeight": "ref.space.300",
|
|
91
|
+
"paddingBlock": "sys.layout.container.2xs",
|
|
92
|
+
"paddingInline": "sys.layout.container.xs",
|
|
93
|
+
"radius": "sys.radius.full",
|
|
94
|
+
"labelTypo": "sys.typo.label.sm",
|
|
95
|
+
"iconSize": "sys.icon.md",
|
|
96
|
+
"slotGap": "sys.layout.inline.sm"
|
|
97
|
+
}
|
|
98
|
+
},
|
|
99
|
+
"appearances": {
|
|
100
|
+
"default": {
|
|
101
|
+
"background": "transparent",
|
|
102
|
+
"border": null,
|
|
103
|
+
"label": "sys.color.onSurfaceVariant",
|
|
104
|
+
"note": "The base neutral inline action — the canonical Text Button. Quiet enough to live next to typographic content without claiming commit-rank attention."
|
|
105
|
+
},
|
|
106
|
+
"accent": {
|
|
107
|
+
"background": "transparent",
|
|
108
|
+
"border": null,
|
|
109
|
+
"label": "sys.color.primary",
|
|
110
|
+
"note": "Brand-blue label for the inline commit affordance. Use sparingly — never two `accent` Text Buttons in the same row.",
|
|
111
|
+
"linkAffordanceRecommendation": "Prefer `accent` whenever the Text Button reads as a **link affordance** — a section header's trailing 'See all' / 'See more', a card-header 'Follow', an inline 'View details' next to a body paragraph. Link-like affordances should carry chromatic emphasis so the navigational intent is unambiguous; `default` (onSurfaceVariant) is for quieter inline commits that should recede into the body copy. Override to `default` only when a parent surface (e.g. a busy chrome bar) already carries enough chromatic weight that an `accent` label would compete."
|
|
112
|
+
},
|
|
113
|
+
"onPrimary": {
|
|
114
|
+
"background": "transparent",
|
|
115
|
+
"border": null,
|
|
116
|
+
"label": "sys.color.onPrimary",
|
|
117
|
+
"note": "Always-white label for use on top of a `primary`-filled host (e.g. the Tooltip `default` appearance). Both `primary` and `onPrimary` are theme-stable (blue / white in light and dark mode), so the label reads as white against the brand-blue fill in either theme — unlike `inverse`, which flips with the theme."
|
|
118
|
+
},
|
|
119
|
+
"inverse": {
|
|
120
|
+
"background": "transparent",
|
|
121
|
+
"border": null,
|
|
122
|
+
"label": "sys.color.inverseOnSurface",
|
|
123
|
+
"note": "Mirror for use inside an inverse host (Toast, coach-mark, snackbar). Label paints in `inverseOnSurface` so it reads against the host's `inverseSurface` fill; state overlays mix from the same token so the recipe carries over without per-host tuning. The `inverseOnSurface` token FLIPS with the theme (white in light mode, dark in dark mode) — that's correct against the also-flipping `inverseSurface` host. When the host fill does NOT flip (e.g. a `primary`-filled Tooltip in `default` appearance), reach for `onPrimary` instead so the label stays white in both themes."
|
|
124
|
+
}
|
|
125
|
+
},
|
|
126
|
+
"flavors": {
|
|
127
|
+
"destructive": {
|
|
128
|
+
"description": "Swaps the label color to the error family across every appearance. Reserved for inline destructive commits next to typographic content (a 'Delete account' trail in a settings row, a 'Discard' inline next to a draft).",
|
|
129
|
+
"appearances": {
|
|
130
|
+
"default": {
|
|
131
|
+
"background": "transparent",
|
|
132
|
+
"border": null,
|
|
133
|
+
"label": "sys.color.error"
|
|
134
|
+
},
|
|
135
|
+
"accent": {
|
|
136
|
+
"background": "transparent",
|
|
137
|
+
"border": null,
|
|
138
|
+
"label": "sys.color.error"
|
|
139
|
+
},
|
|
140
|
+
"onPrimary": {
|
|
141
|
+
"background": "transparent",
|
|
142
|
+
"border": null,
|
|
143
|
+
"label": "sys.color.error"
|
|
144
|
+
},
|
|
145
|
+
"inverse": {
|
|
146
|
+
"background": "transparent",
|
|
147
|
+
"border": null,
|
|
148
|
+
"label": "sys.color.error"
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
},
|
|
153
|
+
"states": {
|
|
154
|
+
"default": {
|
|
155
|
+
"overlay": null
|
|
156
|
+
},
|
|
157
|
+
"hovered": {
|
|
158
|
+
"overlay": {
|
|
159
|
+
"color": "label",
|
|
160
|
+
"opacity": "sys.state.hover"
|
|
161
|
+
}
|
|
162
|
+
},
|
|
163
|
+
"pressed": {
|
|
164
|
+
"overlay": {
|
|
165
|
+
"color": "label",
|
|
166
|
+
"opacity": "sys.state.pressed"
|
|
167
|
+
}
|
|
168
|
+
},
|
|
169
|
+
"disabled": {
|
|
170
|
+
"overlay": null,
|
|
171
|
+
"containerOpacity": "sys.state.disabled",
|
|
172
|
+
"suppressFocusRing": true,
|
|
173
|
+
"cursor": "not-allowed"
|
|
174
|
+
}
|
|
175
|
+
},
|
|
176
|
+
"focusIndicator": {
|
|
177
|
+
"description": "Keyboard-focus visual — an accessibility indicator, not a lifecycle state. Composes over whichever lifecycle state the button is in. The `states.focused` block above is kept for JSX runtime consumers; this block is the parallel external-reader contract.",
|
|
178
|
+
"composition": "outward",
|
|
179
|
+
"compositionReason": "Action affordance with breathing room around it; the 3px outward extent is reserved by the surrounding layout.",
|
|
180
|
+
"overlay": {
|
|
181
|
+
"color": "label",
|
|
182
|
+
"opacity": "sys.state.focus"
|
|
183
|
+
},
|
|
184
|
+
"ring": {
|
|
185
|
+
"outerWidth": "sys.borderWidth.thin",
|
|
186
|
+
"outerColor": "sys.color.focus",
|
|
187
|
+
"insetWidth": "sys.borderWidth.hairline",
|
|
188
|
+
"insetColor": "sys.color.focusInset"
|
|
189
|
+
},
|
|
190
|
+
"trigger": ":focus-visible (keyboard / programmatic focus, never plain mouse click)"
|
|
191
|
+
},
|
|
192
|
+
"behavior": {
|
|
193
|
+
"note": "min-height binds to raw reference-tier space steps (the system tier does not currently expose 24 / 32 / 40 px control-height steps). medium's 40 floor aligns with Icon Button next to it; small's 32 sits one density lower (14-rank label, same 16-glyph), and xsmall's 24 is the densest variant for inline call-sites where even small over-claims (preview-stage toolbars, feed engagement counters).",
|
|
194
|
+
"group": "Horizontally-grouped Text Buttons share a row gap that pairs with the size rung. Because Text Button applies optical alignment by default (each button's chrome bleeds outward by its own padding so the visible label box is the layout box), the chrome-to-chrome gap IS the visible label-to-label distance — there is no per-button padding to add on top: medium and small groups use `sys.layout.inline.xl` (16); xsmall groups tighten to `sys.layout.inline.lg` (12). Wider chrome gaps push the visible labels apart and break the row's grouping; tighter gaps collapse adjacent commits into one visual block.",
|
|
195
|
+
"opticalAlignmentDefault": "Text Button applies optical alignment as the **default** rendering rule, on all four sides: the chrome is transparent at rest, so the visible label box (not the padded chrome) defines the button's layout edges. Implemented as a base `margin: calc(-1 × padding-block) calc(-1 × padding-inline)` on `.chorus-button--text`, which cancels the chrome's contribution to the layout box per-rung. Hover capsule still paints on every side — the chrome becomes a hover-only affordance. Consumers do not opt in; alignment with the surrounding content rail just works."
|
|
196
|
+
},
|
|
197
|
+
"useCases": {
|
|
198
|
+
"dropdown": {
|
|
199
|
+
"description": "Disclosure-trigger pattern: the Text Button reads as the **current value** of a single-select, the trailing chevron flips between `ChevronDownIcon` (closed) and `ChevronUpIcon` (open) as a state signal. Consumer-owned menu surface rendered in a portal — Button only owns the trigger. Default rung is `xsmall` for inline / toolbar dropdowns (preview-stage Size selector, filter chrome, Header trailing); step up to `small` or `medium` when the dropdown is the row's primary commit. Adopted by the docs `ComponentPreview` size selector and by the Header `headerDropdown` mode.",
|
|
200
|
+
"ariaContract": [
|
|
201
|
+
"`aria-haspopup` — `\"listbox\"` for a value-selector pattern; `\"menu\"` for a command palette pattern.",
|
|
202
|
+
"`aria-expanded` — required; mirror the menu's open state so the chevron + AT stay in lockstep.",
|
|
203
|
+
"`aria-controls` (when open) — points to the menu's id so AT can move focus into the listed options."
|
|
204
|
+
],
|
|
205
|
+
"chevronContract": "ChevronDownIcon at rest → ChevronUpIcon while expanded. Never freeze on Down when open — the chevron is a state signal, not decoration. Sized at 16 (`sys.icon.md`) on every rung, same as any other trailing icon.",
|
|
206
|
+
"labelContract": "Label IS the current value (e.g. 'Medium' for a size selector, 'Last 7 days' for a date range). When no value is selected yet, fall back to the field name ('Size', 'Range'). Never use a static verb like 'Choose…' when a value is selected — the label is the value."
|
|
207
|
+
}
|
|
208
|
+
},
|
|
209
|
+
"forbidden": [
|
|
210
|
+
"raw <a> or <span> with Tailwind text-color overrides — link-affordance text is button/text with appearance=accent",
|
|
211
|
+
"appearance=default for navigational copy — link affordance carries appearance=accent so chrome reads as commit",
|
|
212
|
+
"padding override that breaks the optical-alignment leading nudge (the chrome bleeds out by its own padding — re-padding on the consumer side mis-aligns the label rail)",
|
|
213
|
+
"manual color override on the label — re-tone via the `--button-text-label` plumbing var, never via inline color"
|
|
214
|
+
]
|
|
215
|
+
}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
# Toggle
|
|
2
|
+
|
|
3
|
+
Commit-and-record action — a Toolbar-footprint button with two states. **Inactive** invites the commit (`primary` fill); **active** records it (`transparent` fill + hairline `outlineVariant` outline).
|
|
4
|
+
|
|
5
|
+
**Reach for this when** you need a reversible commit that persists across views — *Follow / Following*, *Subscribe / Subscribed*, *Join / Joined*. **Skip when** the action is one-shot ([Standard Button](./standard.md)), the row is a dense toolbar ([Toolbar Button](./toolbar.md)), or the toggle belongs to a filter set ([Filter Chip](../chip/filter.md)).
|
|
6
|
+
|
|
7
|
+
**Layout inset.** `inline` — ships no padding outside its own chrome. Sits inside a host slot (profile card footer, channel header, list-row trailing slot) with the host paying surrounding rhythm. Inside a bounded surface (Card / Dialog / BottomSheet / Sheet), the host already owns the inset — see [`AGENTS.md` § Composition rules](../../../AGENTS.md#composition-rules).
|
|
8
|
+
|
|
9
|
+
## Inactive
|
|
10
|
+
|
|
11
|
+
The at-rest urging form — `primary` fill, no border. Used when the action is offered but not yet taken.
|
|
12
|
+
|
|
13
|
+
```preview
|
|
14
|
+
button/toggle/inactive
|
|
15
|
+
---
|
|
16
|
+
import { Button } from '@teamblind-chorus/ui';
|
|
17
|
+
|
|
18
|
+
<Button variant="toggle">
|
|
19
|
+
Follow
|
|
20
|
+
</Button>
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Active
|
|
24
|
+
|
|
25
|
+
The committed form — `transparent` fill with hairline `outlineVariant` stroke. The transparent fill lets the button sit on any host surface tier (page `surface`, card `surfaceContainer`, raised `surfaceContainerHigh`) without re-painting a background that would clash with the host. Use the same element across both states and toggle the `active` flag; the consumer swaps the label text. Reports state via `aria-pressed`.
|
|
26
|
+
|
|
27
|
+
```preview
|
|
28
|
+
button/toggle/active
|
|
29
|
+
---
|
|
30
|
+
import { Button } from '@teamblind-chorus/ui';
|
|
31
|
+
|
|
32
|
+
<Button variant="toggle" active>
|
|
33
|
+
Following
|
|
34
|
+
</Button>
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## Use cases
|
|
38
|
+
|
|
39
|
+
### With icon
|
|
40
|
+
|
|
41
|
+
A check glyph on commit reinforces the active read. Inactive form stays glyph-less.
|
|
42
|
+
|
|
43
|
+
```preview
|
|
44
|
+
button/toggle/with-icon
|
|
45
|
+
---
|
|
46
|
+
import { Button } from '@teamblind-chorus/ui';
|
|
47
|
+
import { CheckedIcon } from '@teamblind-chorus/ui/icons';
|
|
48
|
+
|
|
49
|
+
<Button variant="toggle" active leadingIcon={<CheckedIcon />}>
|
|
50
|
+
Following
|
|
51
|
+
</Button>
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
### Focus indicator
|
|
55
|
+
|
|
56
|
+
Both forms take the same standard ring; below shows inactive. See [Focus ring composition](../../DESIGN.md#focus-ring-composition).
|
|
57
|
+
|
|
58
|
+
```preview
|
|
59
|
+
button/toggle/focused
|
|
60
|
+
---
|
|
61
|
+
import { Button } from '@teamblind-chorus/ui';
|
|
62
|
+
|
|
63
|
+
<Button variant="toggle" state="focused">
|
|
64
|
+
Follow
|
|
65
|
+
</Button>
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
## Slots
|
|
69
|
+
|
|
70
|
+
- **label** — accessible name. Required, single line. Consumer swaps the verb between states ("Follow" → "Following"); no auto-rewrite.
|
|
71
|
+
- **leadingIcon** (optional) — context glyph before the label. Inherits colour via `currentColor` per the [family rule](./button.md#icon-colour-inheritance-family-wide).
|
|
72
|
+
- **trailingIcon** (optional) — directional/destination glyph after the label. Same contract as [Toolbar Button](./toolbar.md#with-trailing-icon).
|
|
73
|
+
|
|
74
|
+
## Sizes
|
|
75
|
+
|
|
76
|
+
Single fixed footprint — identical to [Toolbar Button](./toolbar.md#sizes) and [Filter chip](../chip/filter.md#sizes).
|
|
77
|
+
|
|
78
|
+
| Property | Value | Token |
|
|
79
|
+
|-----------------------------------|----------------------|-------------------------------------|
|
|
80
|
+
| Min-height | 32px | `ref.space.400` |
|
|
81
|
+
| Padding (block × inline) | 4 × 12 | `sys.layout.container.2xs` × `sys.layout.container.sm` |
|
|
82
|
+
| Label inset (within label slot) | 4px (horizontal) | `sys.layout.container.2xs` |
|
|
83
|
+
| Slot gap (icon ↔ label) | 0 | — |
|
|
84
|
+
| Radius | pill | `sys.radius.full` |
|
|
85
|
+
| Label | 12 / Semibold | `sys.typo.label.sm` |
|
|
86
|
+
| Icon | 16px | `sys.icon.md` |
|
|
87
|
+
|
|
88
|
+
## Variants
|
|
89
|
+
|
|
90
|
+
A single visual variant — inactive/active is expressed as a state on the same button. Container/label pair swaps wholesale on commit, mirroring the [Filter chip](../chip/filter.md#variants) selection contract.
|
|
91
|
+
|
|
92
|
+
| State | Background | Border (always 1px `sys.borderWidth.hairline`) | Label / icon color | Notes |
|
|
93
|
+
|--------------|-------------------------------------|---------------------------------------------------------|-----------------------------------|----------------------------------------------------------------------|
|
|
94
|
+
| inactive | `sys.color.primary` | `transparent` | `sys.color.onPrimary` | Brand-loud fill inviting commit. Border `transparent` but 1px width held so footprint never changes between states. |
|
|
95
|
+
| active | `transparent` | `sys.color.outlineVariant` | `sys.color.onSurface` | Committed form — hairline-outlined ghost over whatever host surface the button sits on. Transparent fill records state without claiming attention or clashing with the host tier. |
|
|
96
|
+
|
|
97
|
+
## States
|
|
98
|
+
|
|
99
|
+
| State | Overlay opacity | Additional |
|
|
100
|
+
|------------|----------------------------|-----------------------------------------------------------------------------|
|
|
101
|
+
| `default` | — | Container + label at rest. |
|
|
102
|
+
| `hovered` | `sys.state.hover` (8%) | Pointer-driven via `:hover`. |
|
|
103
|
+
| `pressed` | `sys.state.pressed` (16%) | Pointer-driven via `:active`. |
|
|
104
|
+
| `disabled` | overlay suppressed | Container at `sys.state.disabled` (40%) opacity, focus ring suppressed, `cursor: not-allowed`. The hairline stroke (active form) is kept because the variant's identity is the stroke. |
|
|
105
|
+
|
|
106
|
+
## Focus indicator
|
|
107
|
+
|
|
108
|
+
Standard ring drawn as a `position: absolute` pseudo-element so it never affects layout. Trigger: `:focus-visible`. See [Focus ring composition](../../DESIGN.md#focus-ring-composition).
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "../../spec.schema.json",
|
|
3
|
+
"name": "Button",
|
|
4
|
+
"family": "button",
|
|
5
|
+
"subcomponent": "toggle",
|
|
6
|
+
"description": "Toolbar-footprint toggle button. Inactive (urging) = primary fill that invites the commit; active (committed) = transparent fill with hairline outline that records the commit without re-soliciting it. Transparent fill lets the button sit on any host surface tier (page surface, surfaceContainer, surfaceContainerHigh) without re-painting a background that would clash with the host. Follow ↔ Following, Subscribe ↔ Subscribed, Join ↔ Joined.",
|
|
7
|
+
"element": "button",
|
|
8
|
+
"props": {
|
|
9
|
+
"variant": {
|
|
10
|
+
"type": "literal",
|
|
11
|
+
"value": "toggle"
|
|
12
|
+
},
|
|
13
|
+
"active": {
|
|
14
|
+
"type": "boolean",
|
|
15
|
+
"default": false,
|
|
16
|
+
"description": "Whether the action has been committed. Maps to aria-pressed."
|
|
17
|
+
},
|
|
18
|
+
"leadingIcon": {
|
|
19
|
+
"type": "node",
|
|
20
|
+
"optional": true
|
|
21
|
+
},
|
|
22
|
+
"trailingIcon": {
|
|
23
|
+
"type": "node",
|
|
24
|
+
"optional": true
|
|
25
|
+
},
|
|
26
|
+
"disabled": {
|
|
27
|
+
"type": "boolean",
|
|
28
|
+
"default": false
|
|
29
|
+
}
|
|
30
|
+
},
|
|
31
|
+
"slots": {
|
|
32
|
+
"label": {
|
|
33
|
+
"required": true,
|
|
34
|
+
"description": "Required, single line. Swap between the urging verb (\"Follow\") and the committed participle (\"Following\") at the consumer.",
|
|
35
|
+
"accepts": [
|
|
36
|
+
"text"
|
|
37
|
+
]
|
|
38
|
+
},
|
|
39
|
+
"leadingIcon": {
|
|
40
|
+
"required": false,
|
|
41
|
+
"description": "Context glyph before the label — most often a check appearing on commit.",
|
|
42
|
+
"accepts": [
|
|
43
|
+
"icon"
|
|
44
|
+
]
|
|
45
|
+
},
|
|
46
|
+
"trailingIcon": {
|
|
47
|
+
"required": false,
|
|
48
|
+
"description": "Directional / destination glyph after the label.",
|
|
49
|
+
"accepts": [
|
|
50
|
+
"icon"
|
|
51
|
+
]
|
|
52
|
+
}
|
|
53
|
+
},
|
|
54
|
+
"sizing": {
|
|
55
|
+
"minHeight": "ref.space.400",
|
|
56
|
+
"paddingBlock": "sys.layout.container.2xs",
|
|
57
|
+
"paddingInline": "sys.layout.container.sm",
|
|
58
|
+
"labelInset": "sys.layout.container.2xs",
|
|
59
|
+
"slotGap": "0",
|
|
60
|
+
"radius": "sys.radius.full",
|
|
61
|
+
"labelTypo": "sys.typo.label.sm",
|
|
62
|
+
"iconSize": "sys.icon.md"
|
|
63
|
+
},
|
|
64
|
+
"selectionStates": {
|
|
65
|
+
"unselected": {
|
|
66
|
+
"background": "sys.color.primary",
|
|
67
|
+
"label": "sys.color.onPrimary",
|
|
68
|
+
"border": null
|
|
69
|
+
},
|
|
70
|
+
"selected": {
|
|
71
|
+
"background": "transparent",
|
|
72
|
+
"label": "sys.color.onSurface",
|
|
73
|
+
"border": {
|
|
74
|
+
"width": "sys.borderWidth.hairline",
|
|
75
|
+
"color": "sys.color.outlineVariant"
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
},
|
|
79
|
+
"states": {
|
|
80
|
+
"default": {
|
|
81
|
+
"overlay": null
|
|
82
|
+
},
|
|
83
|
+
"hovered": {
|
|
84
|
+
"overlay": {
|
|
85
|
+
"color": "label",
|
|
86
|
+
"opacity": "sys.state.hover"
|
|
87
|
+
}
|
|
88
|
+
},
|
|
89
|
+
"pressed": {
|
|
90
|
+
"overlay": {
|
|
91
|
+
"color": "label",
|
|
92
|
+
"opacity": "sys.state.pressed"
|
|
93
|
+
}
|
|
94
|
+
},
|
|
95
|
+
"disabled": {
|
|
96
|
+
"overlay": null,
|
|
97
|
+
"containerOpacity": "sys.state.disabled",
|
|
98
|
+
"suppressFocusRing": true,
|
|
99
|
+
"cursor": "not-allowed"
|
|
100
|
+
}
|
|
101
|
+
},
|
|
102
|
+
"focusIndicator": {
|
|
103
|
+
"description": "Keyboard-focus visual — an accessibility indicator, not a lifecycle state. Composes over whichever lifecycle state the button is in. The `states.focused` block above is kept for JSX runtime consumers; this block is the parallel external-reader contract.",
|
|
104
|
+
"composition": "outward",
|
|
105
|
+
"compositionReason": "Action affordance with breathing room around it; the 3px outward extent is reserved by the surrounding layout.",
|
|
106
|
+
"overlay": {
|
|
107
|
+
"color": "label",
|
|
108
|
+
"opacity": "sys.state.focus"
|
|
109
|
+
},
|
|
110
|
+
"ring": {
|
|
111
|
+
"outerWidth": "sys.borderWidth.thin",
|
|
112
|
+
"outerColor": "sys.color.focus",
|
|
113
|
+
"insetWidth": "sys.borderWidth.hairline",
|
|
114
|
+
"insetColor": "sys.color.focusInset"
|
|
115
|
+
},
|
|
116
|
+
"trigger": ":focus-visible (keyboard / programmatic focus, never plain mouse click)"
|
|
117
|
+
},
|
|
118
|
+
"forbidden": [
|
|
119
|
+
"active state painted with sys.color.primary fill — active is transparent + hairline outline (the active state recedes, not asserts)",
|
|
120
|
+
"active state painted with any opaque surface fill (surface, surfaceContainer, surfaceContainerHigh) — the committed form is transparent so the host surface shows through; do not re-bind to a tier'd fill",
|
|
121
|
+
"rest state without an explicit `active={false}` — toggle is a binary contract, never tristate",
|
|
122
|
+
"manual width override that breaks the full-card stretch when used inside ProfileCarousel.followAction"
|
|
123
|
+
]
|
|
124
|
+
}
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
# Toolbar
|
|
2
|
+
|
|
3
|
+
Dense inline action — a 32-tall capsule for toolbars, table-row actions, and inline menu triggers. Chrome shared with [Filter chip](../chip/filter.md) and [Tabs Segmented](../tabs/segmented.md) so mixed rows read at one density; divergence is intent (Toolbar fires, Filter toggles, Segmented enforces single-select).
|
|
4
|
+
|
|
5
|
+
**Reach for this when** a dense row needs an inline action — toolbar opener, table-row action, inline menu trigger. **Skip when** the standard inline shape fits ([Button](./button.md)), the affordance floats above content ([FAB](./fab.md)), or the row is body-text density ([Text Button](./text.md)).
|
|
6
|
+
|
|
7
|
+
**Layout inset.** inline — content-sized; inherits the surrounding row's padding and gap.
|
|
8
|
+
|
|
9
|
+
## Default
|
|
10
|
+
|
|
11
|
+
Base shape — label-only on Filter-chip chrome. The quiet inline action.
|
|
12
|
+
|
|
13
|
+
```preview
|
|
14
|
+
button/toolbar/default
|
|
15
|
+
---
|
|
16
|
+
import { Button } from '@teamblind-chorus/ui';
|
|
17
|
+
|
|
18
|
+
<Button variant="toolbar">
|
|
19
|
+
Edit
|
|
20
|
+
</Button>
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Use cases
|
|
24
|
+
|
|
25
|
+
### Accent
|
|
26
|
+
|
|
27
|
+
Brand-blue fill, `onPrimary` label — the single-commit form. Used when the Toolbar Button IS the surface's commit affordance.
|
|
28
|
+
|
|
29
|
+
```preview
|
|
30
|
+
button/toolbar/accent
|
|
31
|
+
---
|
|
32
|
+
import { Button } from '@teamblind-chorus/ui';
|
|
33
|
+
|
|
34
|
+
<Button variant="toolbar" appearance="accent">
|
|
35
|
+
Save
|
|
36
|
+
</Button>
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
### Inverse
|
|
40
|
+
|
|
41
|
+
Mirror for inverse hosts (snackbars, coach-mark surfaces). Geometry identical; colour pair flips.
|
|
42
|
+
|
|
43
|
+
```preview
|
|
44
|
+
button/toolbar/inverse
|
|
45
|
+
---
|
|
46
|
+
import { Button } from '@teamblind-chorus/ui';
|
|
47
|
+
|
|
48
|
+
<Button variant="toolbar" appearance="inverse">
|
|
49
|
+
Open
|
|
50
|
+
</Button>
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
### With icon
|
|
54
|
+
|
|
55
|
+
Context glyph before the label — tag for *Filters*, calendar for *Pick date*.
|
|
56
|
+
|
|
57
|
+
```preview
|
|
58
|
+
button/toolbar/leading-icon
|
|
59
|
+
---
|
|
60
|
+
import { Button } from '@teamblind-chorus/ui';
|
|
61
|
+
import { PlusIcon } from '@teamblind-chorus/ui/icons';
|
|
62
|
+
|
|
63
|
+
<Button
|
|
64
|
+
variant="toolbar"
|
|
65
|
+
leadingIcon={<PlusIcon />}
|
|
66
|
+
>
|
|
67
|
+
Add row
|
|
68
|
+
</Button>
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
### With trailing icon
|
|
72
|
+
|
|
73
|
+
Directional/destination glyph — chevron-down to open a menu, "×" to clear. Unlike standard [Button](./button.md), Toolbar Button carries trailing icons because its role is often *trigger* rather than commit.
|
|
74
|
+
|
|
75
|
+
```preview
|
|
76
|
+
button/toolbar/trailing-icon
|
|
77
|
+
---
|
|
78
|
+
import { Button } from '@teamblind-chorus/ui';
|
|
79
|
+
import { CheckedIcon } from '@teamblind-chorus/ui/icons';
|
|
80
|
+
|
|
81
|
+
<Button
|
|
82
|
+
variant="toolbar"
|
|
83
|
+
trailingIcon={<CheckedIcon />}
|
|
84
|
+
>
|
|
85
|
+
Sort by date
|
|
86
|
+
</Button>
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
### Icon only
|
|
90
|
+
|
|
91
|
+
Glyph-only 32×32 square. Requires `aria-label`. When the label slot is absent, inline padding drops to `sys.layout.container.xs` (8) so the glyph centers.
|
|
92
|
+
|
|
93
|
+
```preview
|
|
94
|
+
button/toolbar/icon-only
|
|
95
|
+
---
|
|
96
|
+
import { Button } from '@teamblind-chorus/ui';
|
|
97
|
+
import { PlusIcon } from '@teamblind-chorus/ui/icons';
|
|
98
|
+
|
|
99
|
+
<Button
|
|
100
|
+
variant="toolbar"
|
|
101
|
+
leadingIcon={<PlusIcon />}
|
|
102
|
+
aria-label="Add"
|
|
103
|
+
/>
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
### Group
|
|
107
|
+
|
|
108
|
+
Adjacent Toolbar Buttons share `4px` gap (`sys.layout.inline.sm`) with Filter chips. Mix freely.
|
|
109
|
+
|
|
110
|
+
```preview
|
|
111
|
+
button/toolbar/group
|
|
112
|
+
---
|
|
113
|
+
import { Button } from '@teamblind-chorus/ui';
|
|
114
|
+
import { PlusIcon, CheckedIcon } from '@teamblind-chorus/ui/icons';
|
|
115
|
+
|
|
116
|
+
<div style={{ display: 'flex', gap: 4 }}>
|
|
117
|
+
<Button variant="toolbar" leadingIcon={<PlusIcon />}>
|
|
118
|
+
Add
|
|
119
|
+
</Button>
|
|
120
|
+
<Button variant="toolbar">
|
|
121
|
+
Edit
|
|
122
|
+
</Button>
|
|
123
|
+
<Button variant="toolbar" trailingIcon={<CheckedIcon />}>
|
|
124
|
+
Sort
|
|
125
|
+
</Button>
|
|
126
|
+
</div>
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
### Focus indicator
|
|
130
|
+
|
|
131
|
+
Hairline stroke is kept underneath the standard ring.
|
|
132
|
+
|
|
133
|
+
```preview
|
|
134
|
+
button/toolbar/focused
|
|
135
|
+
---
|
|
136
|
+
import { Button } from '@teamblind-chorus/ui';
|
|
137
|
+
import { PlusIcon } from '@teamblind-chorus/ui/icons';
|
|
138
|
+
|
|
139
|
+
<Button variant="toolbar" state="focused" leadingIcon={<PlusIcon />}>
|
|
140
|
+
Add row
|
|
141
|
+
</Button>
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
## Slots
|
|
145
|
+
|
|
146
|
+
- **label** — accessible name. Required, single line.
|
|
147
|
+
- **leadingIcon** (optional) — context glyph before the label.
|
|
148
|
+
- **trailingIcon** (optional) — directional/destination glyph after the label.
|
|
149
|
+
|
|
150
|
+
## Appearance
|
|
151
|
+
|
|
152
|
+
Only the container ↔ label colour pair flips; geometry identical. For inline destructive commits use standard [Button](./button.md) `secondary` flavored as `destructive`.
|
|
153
|
+
|
|
154
|
+
| Appearance | Background | Border | Label / icon | When to reach for it |
|
|
155
|
+
|-------------|-------------------------------------|--------------------------------------------------|------------------------------------|----------------------|
|
|
156
|
+
| `default` | `sys.color.surfaceContainerHigh` | 1px `sys.color.outlineVariant` | `sys.color.onSurface` | Quiet inline action — toolbar opener, "Edit" beside a row title. |
|
|
157
|
+
| `accent` | `sys.color.primary` | none | `sys.color.onPrimary` | The single commit — a [Page](../navigation-bar/sub.md) bar's "Save". Never two in a row. |
|
|
158
|
+
| `inverse` | `sys.color.inverseSurface` | none | `sys.color.inverseOnSurface` | Inside an inverse host (Toast, coach-mark). |
|
|
159
|
+
|
|
160
|
+
## Sizes
|
|
161
|
+
|
|
162
|
+
A single fixed 32px footprint across breakpoints.
|
|
163
|
+
|
|
164
|
+
| Property | Value | Token |
|
|
165
|
+
|-----------------------------------|----------------------|-------------------------------------|
|
|
166
|
+
| Min-height | 32px | `ref.space.400` ‡ |
|
|
167
|
+
| Padding (block × inline) | 4 × 12 | `sys.layout.container.2xs` × `sys.layout.container.sm` |
|
|
168
|
+
| Label inset (within label slot) | 4px (horizontal) | `sys.layout.container.2xs` |
|
|
169
|
+
| Slot gap (icon ↔ label) | 0 | — † |
|
|
170
|
+
| Radius | pill | `sys.radius.full` |
|
|
171
|
+
| Label | 12 / Semibold | `sys.typo.label.sm` |
|
|
172
|
+
| Icon | 16px | `sys.icon.md` |
|
|
173
|
+
|
|
174
|
+
‡ Footprint shared with [Filter chip](../chip/filter.md) and [Tabs Segmented](../tabs/segmented.md).
|
|
175
|
+
|
|
176
|
+
† See [Filter → Sizes](../chip/filter.md#sizes).
|
|
177
|
+
|
|
178
|
+
## States
|
|
179
|
+
|
|
180
|
+
| State | Overlay opacity | Additional |
|
|
181
|
+
|------------|----------------------------|-----------------------------------------------------------------------------|
|
|
182
|
+
| `default` | — | Container + label at rest. |
|
|
183
|
+
| `hovered` | `sys.state.hover` (8%) | `:hover`. |
|
|
184
|
+
| `pressed` | `sys.state.pressed` (16%) | `:active`. |
|
|
185
|
+
| `disabled` | overlay suppressed | Container at `sys.state.disabled` (40%) opacity, hairline stroke kept, focus ring suppressed, `cursor: not-allowed`. |
|
|
186
|
+
|
|
187
|
+
## Focus indicator
|
|
188
|
+
|
|
189
|
+
Standard outward ring with hairline stroke kept underneath. Trigger: `:focus-visible`. See [Focus ring composition](../../DESIGN.md#focus-ring-composition).
|