@teamblind-chorus/ui 1.0.0 → 1.1.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/agents/AGENTS.md +4 -6
- package/agents/DESIGN.md +2 -0
- package/agents/LOVABLE.md +167 -373
- package/agents/anti-patterns.md +2 -2
- package/agents/catalog.md +7 -3
- package/agents/components/avatar-rail/avatar-rail.md +2 -0
- package/agents/components/badge/badge.md +2 -0
- package/agents/components/badge/role.md +2 -0
- package/agents/components/badge/update.md +2 -0
- package/agents/components/banner/banner.md +72 -9
- package/agents/components/banner/banner.spec.json +40 -2
- package/agents/components/bottom-sheet/bottom-sheet.md +2 -0
- package/agents/components/bubble/bubble.md +2 -0
- package/agents/components/button/button.family.json +8 -2
- package/agents/components/button/button.md +2 -0
- package/agents/components/button/check.md +2 -0
- package/agents/components/button/fab.md +2 -0
- package/agents/components/button/group.spec.json +65 -0
- package/agents/components/button/icon.md +2 -0
- package/agents/components/button/standard.md +45 -19
- package/agents/components/button/text.md +2 -0
- package/agents/components/button/toggle.md +2 -0
- package/agents/components/button/toolbar.md +2 -0
- package/agents/components/carousel/carousel.md +2 -0
- package/agents/components/carousel/post.md +5 -3
- package/agents/components/carousel/post.spec.json +4 -6
- package/agents/components/carousel/profile.md +4 -2
- package/agents/components/carousel/profile.spec.json +4 -6
- package/agents/components/chip/chip.md +2 -0
- package/agents/components/chip/filter.md +2 -0
- package/agents/components/chip/tag.md +2 -0
- package/agents/components/dialog/dialog.md +2 -0
- package/agents/components/directory-list/directory-list.md +2 -0
- package/agents/components/divider/divider.md +2 -0
- package/agents/components/feed/ad.md +2 -0
- package/agents/components/feed/feed.md +2 -0
- package/agents/components/feed/post.md +2 -0
- package/agents/components/form-field/form-field.md +3 -1
- package/agents/components/form-field/input.md +2 -0
- package/agents/components/form-field/input.spec.json +2 -1
- package/agents/components/form-field/search.md +2 -0
- package/agents/components/form-field/search.spec.json +2 -1
- package/agents/components/form-field/select.md +2 -0
- package/agents/components/form-field/textarea.md +2 -0
- package/agents/components/form-field/textarea.spec.json +2 -1
- package/agents/components/header/header.md +2 -0
- package/agents/components/header/main.md +2 -0
- package/agents/components/header/sub.md +2 -0
- package/agents/components/list/accordion.md +2 -0
- package/agents/components/list/entry.md +2 -0
- package/agents/components/list/entry.spec.json +2 -1
- package/agents/components/list/list.md +3 -1
- package/agents/components/list/radio.md +2 -0
- package/agents/components/list/standard.md +2 -0
- package/agents/components/list/standard.spec.json +2 -1
- package/agents/components/metadata/compact.md +13 -7
- package/agents/components/metadata/compact.spec.json +19 -6
- package/agents/components/metadata/metadata.family.json +3 -3
- package/agents/components/metadata/metadata.md +4 -2
- package/agents/components/metadata/standard.md +24 -0
- package/agents/components/nav-card/nav-card.md +2 -0
- package/agents/components/nav-list/nav-list.md +2 -0
- package/agents/components/navigation-bar/main.md +2 -0
- package/agents/components/navigation-bar/navigation-bar.md +2 -0
- package/agents/components/navigation-bar/search.md +2 -0
- package/agents/components/navigation-bar/sub.md +2 -0
- package/agents/components/page-shell/page-shell.md +2 -0
- package/agents/components/pagination/pagination.family.json +26 -0
- package/agents/components/pagination/pagination.md +40 -0
- package/agents/components/pagination/pagination.spec.json +54 -0
- package/agents/components/profile-header/profile-header.md +2 -0
- package/agents/components/progress/progress.md +2 -0
- package/agents/components/side-sheet/side-sheet.md +2 -0
- package/agents/components/skeleton/skeleton.md +2 -0
- package/agents/components/status-tag/status-tag.md +2 -0
- package/agents/components/suggestion-list/suggestion-list.md +2 -0
- package/agents/components/switch/switch.md +2 -0
- package/agents/components/tab-bar/tab-bar.md +2 -0
- package/agents/components/tabs/rounded.md +2 -0
- package/agents/components/tabs/segmented.md +2 -0
- package/agents/components/tabs/tabs.md +2 -0
- package/agents/components/tabs/underline.md +2 -0
- package/agents/components/thumbnail/thumbnail.md +2 -0
- package/agents/components/toast/toast.md +2 -0
- package/agents/components/tooltip/tooltip.md +2 -0
- package/agents/compose.md +3 -3
- package/agents/manifest.json +1 -0
- package/agents/patterns/README.md +2 -0
- package/agents/patterns/actions.md +2 -0
- package/agents/patterns/browsing.md +2 -0
- package/agents/patterns/communications.md +2 -0
- package/agents/patterns/layout.md +2 -0
- package/agents/patterns/modals.md +2 -0
- package/agents/patterns/visual.md +2 -0
- package/agents/usage.json +15 -3
- package/dist/index.cjs +95 -39
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +28 -1
- package/dist/index.d.ts +28 -1
- package/dist/index.js +94 -40
- package/dist/index.js.map +1 -1
- package/dist/styles.css +183 -41
- package/package.json +2 -3
- package/agents/reconstruct.md +0 -55
- package/agents/scoped-adoption.md +0 -111
package/agents/anti-patterns.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# anti-patterns.md — common Lovable / agent failure modes
|
|
2
2
|
|
|
3
|
-
A catalogue of the wrong-shaped output Chorus consumers most often produce, paired with the right-shaped fix. Each entry names the rule, the broken snippet, and the corrected one. **Read at least once before composing a new screen** — most agent violations come from these
|
|
3
|
+
A catalogue of the wrong-shaped output Chorus consumers most often produce, paired with the right-shaped fix. Each entry names the rule, the broken snippet, and the corrected one. **Read at least once before composing a new screen** — most agent violations come from these 18 recurring shapes. Pair with [`compose.md`](compose.md) and [`tokens.usage.json`](tokens.usage.json).
|
|
4
4
|
|
|
5
5
|
When in doubt: if your output matches a "❌ wrong" snippet below, discard and regenerate.
|
|
6
6
|
|
|
@@ -383,7 +383,7 @@ The cognitive trap: *"No Chorus family fits a hint card / inline annotation / sm
|
|
|
383
383
|
|
|
384
384
|
**Off-scale = pick the next ladder rung, NOT halfway.** 13px is forbidden; 12 or 14 are tokens. 6px gap is forbidden; 4 or 8 are tokens. If no token feels right, that's a Chorus gap report ("`spacing.inline.xs` reads too tight, `inline.sm` too loose for this slot — proposing a new rung") — not a license to invent. See the full raw → token map in [compose.md § When you go custom](compose.md).
|
|
385
385
|
|
|
386
|
-
Side note: this entry catches the values. Whether you should have gone custom at all is a separate question — the visual-reuse table in [LOVABLE.md §C](LOVABLE.md) covers
|
|
386
|
+
Side note: this entry catches the values. Whether you should have gone custom at all is a separate question — the visual-reuse table in [LOVABLE.md §C](LOVABLE.md) covers the `"open"` families that you can borrow on visual-fit grounds (`<Feed>` as a generic article-card surface, `<Section>` as any labelled block). Reach for that first; #14 only fires after you've genuinely exhausted the LEGO ladder.
|
|
387
387
|
|
|
388
388
|
---
|
|
389
389
|
|
package/agents/catalog.md
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# Catalog — intent → component
|
|
2
2
|
|
|
3
|
+
> 🇰🇷 한국어: [`i18n/ko/schema/catalog.md`](../i18n/ko/schema/catalog.md)
|
|
4
|
+
|
|
3
5
|
Reverse index from natural-language intent to family + sub-component. Read *before* opening any spec to narrow candidates. Authoritative shape lives in `schema/components/<family>/<sub>.spec.json`; this file is a routing layer.
|
|
4
6
|
|
|
5
7
|
> **How to import / use a family → `usage.json`.** This catalog names families as `family / sub`. The React access pattern differs per family: a named import (`profile-header` → `import { ProfileHeader }`), a `variant=` prop (`button / fab` → `<Button variant="fab">` — there is **no** `<Fab>` export), a separate export (`carousel / post` → `PostCarousel`), or a data array (`list / entry` → `<List items={[…]}>` — there is **no** `<ListItem>`). `usage.json` is the authoritative, build-time **parity-checked** map of every family to its exact usage. **A failed `import { X }` for one component never means the catalog is unreliable or a family is "missing at runtime" — look it up in `usage.json` and use the access it specifies. Never fall back to assembling a family from token primitives.**
|
|
@@ -8,7 +10,7 @@ Reverse index from natural-language intent to family + sub-component. Read *befo
|
|
|
8
10
|
|
|
9
11
|
Each family declares `visualReuse: "open" | "locked"` in its `<family>.family.json`. The catalog respects that flag.
|
|
10
12
|
|
|
11
|
-
- **Open (default,
|
|
13
|
+
- **Open (default, 28 families)** — `avatar-rail`, `badge`, `banner`, `bubble`, `button`, `carousel`, `chip`, `directory-list`, `divider`, `feed`, `header`, `list`, `metadata`, `nav-card`, `nav-list`, `navigation-bar`, `page-shell`, `pagination`, `profile-header`, `progress`, `side-sheet`, `skeleton`, `status-tag`, `suggestion-list`, `switch`, `tab-bar`, `tabs`, `thumbnail`. The intent table is the *first* suggestion, but the agent MAY reach for these on visual-fit grounds even when the brief's intent does not match the row verbatim — e.g. `<Feed>` as a generic article-card surface, `<Banner>` as a tonal aside outside a literal "notice", `<Carousel>` as any labelled editorial block. Anatomy invariants (slot grammar, token bindings, intrinsic geometry) still apply.
|
|
12
14
|
- **Locked (5 families)** — `dialog`, `bottom-sheet`, `toast`, `tooltip`, `form-field`. MUST only be used when the brief's intent matches a row. Their contract IS the interaction — focus trap, auto-dismiss, ARIA live region, form semantics, hover/focus trigger. Borrowing the visual shape without the role breaks behavior. Marked *(locked)* below.
|
|
13
15
|
|
|
14
16
|
When in doubt: open families are recipes, locked families are rules.
|
|
@@ -41,8 +43,9 @@ When in doubt: open families are recipes, locked families are rules.
|
|
|
41
43
|
| reversible state (mute/unmute, follow) | `button / toggle` |
|
|
42
44
|
| dense action inside a toolbar | `button / toolbar` |
|
|
43
45
|
| destructive primary commit | `button / standard` with `flavor: "destructive"` (inside a Dialog) |
|
|
46
|
+
| grouped buttons / docked bottom action bar | `button / group` → `<ButtonGroup>` (named export) |
|
|
44
47
|
|
|
45
|
-
**Disambiguate**: FAB = the **single** canonical commit per screen. Destructive primary commits go in `dialog`/`bottom-sheet`, not on a FAB.
|
|
48
|
+
**Disambiguate**: FAB = the **single** canonical commit per screen. Destructive primary commits go in `dialog`/`bottom-sheet`, not on a FAB. **Two or more Buttons that belong together** (Cancel + Save, or a bottom-pinned action bar) go in `<ButtonGroup>` — a **named export**, not `<Button variant="group">`. Use `variant="docked"` for the bottom action bar (full-bleed surface + 16px inset + 8px gap + upward `sys.elevation.sheet` shadow + optional `label`); never hand-roll the wrapper div or give it a top border. Like the other bars it renders in flow — `page-shell` owns the pinning.
|
|
46
49
|
|
|
47
50
|
## Structure & layout
|
|
48
51
|
|
|
@@ -152,8 +155,9 @@ Each row resolves to a typed React component — `<FormField variant="search" pl
|
|
|
152
155
|
| instant-commit on/off toggle | `switch` |
|
|
153
156
|
| inline status mark next to a row label | `status-tag` |
|
|
154
157
|
| always-on annotation pill anchored to a UI element | `bubble` |
|
|
158
|
+
| dot position indicator under a snap pager | `pagination` |
|
|
155
159
|
|
|
156
|
-
**Disambiguate**: `switch` = single binary on/off that commits the moment it flips (notifications, privacy). For multi-option pickers use `list / radio`; for actions needing confirmation use `button` + `dialog`; for "selected" facet state in a chip row use `chip / filter`. `status-tag` = SMALL (16-rung, 10px text) decorative status pill inline next to a row label — "pending", "rejected", "draft". For a 32-rung interactive chip use `chip / tag`; for a numeric count attached to an icon/thumbnail use `badge / update`; for a role / title mark beside a user's nickname (Channel owner / Verified / PRO) use `badge / role` — identity, where `status-tag` is workflow state. `bubble` = a labelled pill with a tail that points at a specific element to flag attention *at rest* (new-feature nudge, keyword/campaign promo next to a top-bar icon or search bar) — vs `badge` (numeric count / dot, no tail) and `status-tag` (status mark on a row label).
|
|
160
|
+
**Disambiguate**: `switch` = single binary on/off that commits the moment it flips (notifications, privacy). For multi-option pickers use `list / radio`; for actions needing confirmation use `button` + `dialog`; for "selected" facet state in a chip row use `chip / filter`. `status-tag` = SMALL (16-rung, 10px text) decorative status pill inline next to a row label — "pending", "rejected", "draft". For a 32-rung interactive chip use `chip / tag`; for a numeric count attached to an icon/thumbnail use `badge / update`; for a role / title mark beside a user's nickname (Channel owner / Verified / PRO) use `badge / role` — identity, where `status-tag` is workflow state. `bubble` = a labelled pill with a tail that points at a specific element to flag attention *at rest* (new-feature nudge, keyword/campaign promo next to a top-bar icon or search bar) — vs `badge` (numeric count / dot, no tail) and `status-tag` (status mark on a row label). `pagination` = decorative dot position row under a one-page-at-a-time pager (carousel, gallery) — non-interactive (`aria-hidden`), the host owns the active index; for tap-to-switch sections use `tabs`, for task-completion ratio use `progress`.
|
|
157
161
|
|
|
158
162
|
## Loading & Placeholder
|
|
159
163
|
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# Avatar rail
|
|
2
2
|
|
|
3
|
+
> 🇰🇷 한국어: [`i18n/ko/schema/components/avatar-rail/avatar-rail.md`](../../../i18n/ko/schema/components/avatar-rail/avatar-rail.md)
|
|
4
|
+
|
|
3
5
|
A horizontal strip of channel entry points — each item routes to a channel or company page. Composes a [Thumbnail](../thumbnail/thumbnail.md) (optional `updateDot`) and a single-line label; an optional trailing action lives at the end.
|
|
4
6
|
|
|
5
7
|
**Reach for this when** the row is a label-only nav strip of subscribed or saved entities — *Subscribed channels*, *Saved companies*, *My topics*. **Skip when** rows carry follower counts or descriptions (use [DirectoryList](../directory-list/directory-list.md) / [SuggestionList](../suggestion-list/suggestion-list.md)), the surface is fixed-width profile cards ([Profile carousel](../carousel/profile.md)), or rows commit in place rather than route ([Toggle Button](../button/toggle.md) cluster).
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# Badge
|
|
2
2
|
|
|
3
|
+
> 🇰🇷 한국어: [`i18n/ko/schema/components/badge/badge.md`](../../../i18n/ko/schema/components/badge/badge.md)
|
|
4
|
+
|
|
3
5
|
A small, non-interactive mark anchored to a host label — badge-shaped annotation for two roles. **Update** is the brand-tone activity indicator: a numeric count pill or a labelless corner dot flagging unread / new activity (3 unread, `99+`, a dot on a [Thumbnail](../thumbnail/thumbnail.md)). **Role** is the tonal identity pill: a pale `primaryContainer` mark naming the user's role or title — Channel owner (채널장), Verified (현직자) — riding the bare nickname at the end of their metadata's meta row. The two share the anchored anatomy, the `radius.full` corner, and the never-interactive contract; they diverge on tone (brand vs primary-container) and on what they say about the host (*what's new* vs *who this is*). Both are reached through the single `Badge` export — `variant="update"` (default) / `variant="role"`.
|
|
4
6
|
|
|
5
7
|
**Layout inset.** `inline` — slot atom. A Badge has no page-rail responsibility; the host places it (Thumbnail corner, List row label, [Metadata](../metadata/metadata.md) trailing edge). Never a sibling of `full-bleed` page rows.
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# Role
|
|
2
2
|
|
|
3
|
+
> 🇰🇷 한국어: [`i18n/ko/schema/components/badge/role.md`](../../../i18n/ko/schema/components/badge/role.md)
|
|
4
|
+
|
|
3
5
|
A tonal identity pill naming a user's role or title — anchored at the trailing edge of their metadata. Pale `primaryContainer` fill with a deep `onPrimaryContainer` label: informational, quiet, and clearly distinct from the [Update](./update.md) sub's brand-tone alert. Always rides beside a user identity — canonically the bare nickname at the end of the metadata row's second line, **exactly one badge per nickname** — never interactive. Two appearances: `default` (tonal primaryContainer) and `inverse` (near-black inverseSurface pair, reserved for the paid-expert **PRO** mark).
|
|
4
6
|
|
|
5
7
|
**Reach for this when** the mark says *who the person is* — Channel owner (채널장), Verified (현직자), Moderator. **Skip when** the mark signals workflow state — pending / approved / rejected belong to [Status tag](../status-tag/status-tag.md) — or new-activity on a host — that's [Update](./update.md).
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# Update
|
|
2
2
|
|
|
3
|
+
> 🇰🇷 한국어: [`i18n/ko/schema/components/badge/update.md`](../../../i18n/ko/schema/components/badge/update.md)
|
|
4
|
+
|
|
3
5
|
A small brand-tone indicator attached to a host label — flagging *new or unread activity*. Two types share the brand fill and `radius.full` corner: **Numeric** (a labelled count pill) and **Dot** (a labelless update dot used by [Thumbnail](../thumbnail/thumbnail.md)). Always anchored to a host; never interactive.
|
|
4
6
|
|
|
5
7
|
**Reach for Numeric when** the count itself carries meaning — 3 unread, 12 mentions, `99+` notifications. **Reach for Dot when** the presence of activity is the whole signal — a corner flag without a magnitude. **Skip Update** when the mark names *who the person is* rather than *what is new* — use [Role](./role.md) — or when it is descriptive metadata — use [Tag](../chip/tag.md).
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
# Banner
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
> 🇰🇷 한국어: [`i18n/ko/schema/components/banner/banner.md`](../../../i18n/ko/schema/components/banner/banner.md)
|
|
4
|
+
|
|
5
|
+
An in-body explanation block — a tinted card sitting within the reading flow with an optional heading line, a short paragraph, and an optional follow-through link. Four axes: **appearance** (`default` / `accent` / `destructive`), **outline** (`outlined` / none), **leading slot** (`icon` / `thumbnail` / none), **trailing slot** (`trailingIcon` / none).
|
|
4
6
|
|
|
5
7
|
**Reach for this when** a passage needs a brief aside the reader can scan or skip. **Skip when** the message demands a decision ([Dialog](../dialog/dialog.md) / [Bottom sheet](../bottom-sheet/bottom-sheet.md)) or confirms a recent user action ([Toast](../toast/toast.md)).
|
|
6
8
|
|
|
@@ -77,6 +79,63 @@ import { Banner, Thumbnail } from '@teamblind-chorus/ui';
|
|
|
77
79
|
</Banner>
|
|
78
80
|
```
|
|
79
81
|
|
|
82
|
+
### Outlined
|
|
83
|
+
|
|
84
|
+
An optional `sys.borderWidth.hairline` (1) inset stroke toned to the appearance's color family and kept deliberately faint, so the outline reads as a soft edge of the same tint rather than a frame — the subtle gray hairline (`sys.color.outlineVariant`) on `default`'s gray-tinted scrim, `primary` at 40% on `accent`'s blue-tinted container, `error` at 40% on `destructive`. Painted as an inset box-shadow, never a real border, so toggling it cannot change the banner's footprint. Reach for it when the tinted fill alone doesn't separate the banner from its host surface.
|
|
85
|
+
|
|
86
|
+
```preview
|
|
87
|
+
banner/outlined
|
|
88
|
+
---
|
|
89
|
+
import { Banner } from '@teamblind-chorus/ui';
|
|
90
|
+
|
|
91
|
+
// vertical 8 between sibling banners is the parent column's job (safe zone)
|
|
92
|
+
<div style={{ display: 'flex', flexDirection: 'column', gap: 'var(--sys-layout-stack-xs)' }}>
|
|
93
|
+
<Banner appearance="default" outlined>
|
|
94
|
+
Stay active in the community to level up and unlock more of what the app offers.
|
|
95
|
+
</Banner>
|
|
96
|
+
<Banner appearance="accent" outlined>
|
|
97
|
+
Stay active in the community to level up and unlock more of what the app offers.
|
|
98
|
+
</Banner>
|
|
99
|
+
</div>
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
### With title
|
|
103
|
+
|
|
104
|
+
An optional heading line above the body — `label.md` (14 / Semibold) in the container's foreground, separated from the body by `sys.layout.stack.2xs` (4) so the pair reads as one passage. Reach for it when the aside needs a scannable lead-in; omit it for single-thought asides where the body carries itself.
|
|
105
|
+
|
|
106
|
+
```preview
|
|
107
|
+
banner/with-title
|
|
108
|
+
---
|
|
109
|
+
import { Banner } from '@teamblind-chorus/ui';
|
|
110
|
+
|
|
111
|
+
<Banner
|
|
112
|
+
appearance="default"
|
|
113
|
+
title="Level up faster"
|
|
114
|
+
action={{ label: 'How levels work', href: '#level' }}
|
|
115
|
+
>
|
|
116
|
+
Stay active in the community to level up and unlock more of what the app offers.
|
|
117
|
+
</Banner>
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
### With trailing icon
|
|
121
|
+
|
|
122
|
+
A 16 × 16 (`sys.icon.md`) glyph at the trailing edge, vertically centred against the whole block and painted in `currentColor`. Reach for it when the banner leads somewhere — a forward affordance such as `ForwardCircleFillIcon` signals the aside opens a destination.
|
|
123
|
+
|
|
124
|
+
```preview
|
|
125
|
+
banner/with-trailing-icon
|
|
126
|
+
---
|
|
127
|
+
import { Banner } from '@teamblind-chorus/ui';
|
|
128
|
+
import { ForwardCircleFillIcon } from '@teamblind-chorus/ui/icons';
|
|
129
|
+
|
|
130
|
+
<Banner
|
|
131
|
+
appearance="default"
|
|
132
|
+
title="Level up faster"
|
|
133
|
+
trailingIcon={<ForwardCircleFillIcon size={16} />}
|
|
134
|
+
>
|
|
135
|
+
Stay active in the community to level up and unlock more of what the app offers.
|
|
136
|
+
</Banner>
|
|
137
|
+
```
|
|
138
|
+
|
|
80
139
|
### With icon
|
|
81
140
|
|
|
82
141
|
A 16 × 16 (`sys.icon.md`) glyph at the leading edge, painted in `currentColor`. The slot is sized to the body's first-line height so the glyph centres on the first line — multi-line bodies keep the icon anchored to the first-line cap, not the block centre. Reach for it when the aside leads with a meaning-bearing glyph rather than a brand image.
|
|
@@ -100,31 +159,35 @@ import { StarIcon } from '@teamblind-chorus/ui/icons';
|
|
|
100
159
|
|
|
101
160
|
Two appearances on the *emphasis* axis (plus `destructive` for errors). Banner carries no disabled state; only the optional action link follows the link state contract.
|
|
102
161
|
|
|
103
|
-
| Appearance | Container fill | Body / action color | When to use |
|
|
104
|
-
|
|
105
|
-
| `default` | `sys.color.scrimSubtle` (translucent inverse-tone scrim — ~8% black light / ~8% white dark) | body in `sys.color.onSurface`, action steps to `sys.color.primary` | Supplementary asides the reader can pass over without missing the main flow. |
|
|
106
|
-
| `accent` | `sys.color.primaryContainer` | body in `onPrimaryContainer`, action inherits | Asides worth pulling the eye toward — new-feature explainers, capability nudges. |
|
|
107
|
-
| `destructive` | `sys.color.errorContainer` | body in `onErrorContainer`, action inherits | Blocking errors or rejections — failed approvals, outages, billing. |
|
|
162
|
+
| Appearance | Container fill | Body / action color | Outline (when `outlined`) | When to use |
|
|
163
|
+
|---------------|---------------------------------------------------------------------------------------------|--------------------------------------------------------------------|----------------------------|------------------------------------------------------------------------------|
|
|
164
|
+
| `default` | `sys.color.scrimSubtle` (translucent inverse-tone scrim — ~8% black light / ~8% white dark) | body in `sys.color.onSurface`, action steps to `sys.color.primary` | `sys.color.outlineVariant` (subtle gray) | Supplementary asides the reader can pass over without missing the main flow. |
|
|
165
|
+
| `accent` | `sys.color.primaryContainer` | body in `onPrimaryContainer`, action inherits | `sys.color.primary` at 40% (soft blue) | Asides worth pulling the eye toward — new-feature explainers, capability nudges. |
|
|
166
|
+
| `destructive` | `sys.color.errorContainer` | body in `onErrorContainer`, action inherits | `sys.color.error` at 40% | Blocking errors or rejections — failed approvals, outages, billing. |
|
|
108
167
|
|
|
109
168
|
## Slots
|
|
110
169
|
|
|
111
170
|
- **container** — tinted block. Horizontal flex with `align-items: flex-start`; 12px inset, 8px sibling gap, 8px corner radius.
|
|
112
171
|
- **icon** *(optional)* — 16 × 16 glyph. Slot height equals the `body.sm` line box so the glyph centres on the body's first line. Paints in `currentColor`.
|
|
113
172
|
- **thumbnail** *(optional)* — leading [Thumbnail](../thumbnail/thumbnail.md). Takes precedence over `icon`; footprint and corner come from Thumbnail.
|
|
114
|
-
- **content** — vertical column holding body and optional action; 8px stack gap; fills remaining inline space.
|
|
173
|
+
- **content** — vertical column holding optional title, body, and optional action; 8px stack gap (4px title↔body); fills remaining inline space.
|
|
174
|
+
- **title** *(optional)* — heading line above the body. `label.md` / Semibold / inherits container foreground. 4px (`sys.layout.stack.2xs`) gap to the body.
|
|
115
175
|
- **body** — explanation copy. `body.sm` / Regular / inherits container foreground. Required.
|
|
116
176
|
- **action** *(optional)* — follow-through link below the body. `label.md` / Semibold / underlined.
|
|
177
|
+
- **trailingIcon** *(optional)* — 16 × 16 glyph at the trailing edge, vertically centred against the container. Paints in `currentColor`.
|
|
117
178
|
|
|
118
179
|
## Anatomy
|
|
119
180
|
|
|
120
181
|
| Slot | Token bindings |
|
|
121
182
|
|-----------|----------------|
|
|
122
|
-
| container | Fill + foreground per appearance, `sys.radius.md` (8), `sys.layout.container.sm` (12) padding, `sys.layout.stack.xs` (8) sibling gap, `align-items: flex-start` |
|
|
183
|
+
| container | Fill + foreground per appearance, `sys.radius.md` (8), `sys.layout.container.sm` (12) padding, `sys.layout.stack.xs` (8) sibling gap, `align-items: flex-start`. With `outlined`: `sys.borderWidth.hairline` (1) inset stroke in the appearance's outline color |
|
|
123
184
|
| icon | `sys.icon.md` (16 × 16) glyph inside a slot whose height equals the body's first-line box (`calc(sys.typo.body.sm.size * sys.typo.body.sm.line)`); `color: currentColor` |
|
|
124
185
|
| thumbnail | Delegated to [Thumbnail](../thumbnail/thumbnail.md); footprint-preserving (`flex: 0 0 auto`) |
|
|
125
|
-
| content | Flex column, `flex: 1 1 auto`, `sys.layout.stack.xs` (8) body↔action gap |
|
|
186
|
+
| content | Flex column, `flex: 1 1 auto`, `sys.layout.stack.xs` (8) body↔action gap, `sys.layout.stack.2xs` (4) title↔body gap |
|
|
187
|
+
| title | `sys.typo.label.md` (14 / Semibold 600), color inherits |
|
|
126
188
|
| body | `sys.typo.body.sm` (14 / Regular), color inherits |
|
|
127
189
|
| action | `sys.typo.label.md` (14 / Semibold), underlined. Steps to `sys.color.primary` in `default`; inherits in `accent` / `destructive`. |
|
|
190
|
+
| trailingIcon | `sys.icon.md` (16 × 16) glyph, `align-self: center` against the container, `color: currentColor` |
|
|
128
191
|
|
|
129
192
|
## States
|
|
130
193
|
|
|
@@ -15,11 +15,27 @@
|
|
|
15
15
|
],
|
|
16
16
|
"default": "default"
|
|
17
17
|
},
|
|
18
|
+
"outlined": {
|
|
19
|
+
"type": "boolean",
|
|
20
|
+
"optional": true,
|
|
21
|
+
"default": false,
|
|
22
|
+
"description": "Paints a `sys.borderWidth.hairline` (1) inset stroke around the container, toned to the appearance's color family and kept deliberately faint so it reads as a soft edge of the same tint, not a frame — the subtle gray hairline (`sys.color.outlineVariant`) on `default`'s gray-tinted fill, `primary` at 40% (`color-mix(sys.color.primary, 40%)`) on `accent`'s blue-tinted fill, `error` at 40% on `destructive`. Rendered as an inset box-shadow, never a real border, so toggling it cannot change the banner's footprint (see DESIGN.md → Border & Stroke). Reach for it when the tinted fill alone doesn't separate the banner from its host surface."
|
|
23
|
+
},
|
|
24
|
+
"title": {
|
|
25
|
+
"type": "node",
|
|
26
|
+
"optional": true,
|
|
27
|
+
"description": "Optional heading line above the body. label.md (14 / Semibold 600) in the container's foreground, separated from the body by `sys.layout.stack.2xs` (4). Reach for it when the aside needs a scannable lead-in; omit for single-thought asides where the body carries itself."
|
|
28
|
+
},
|
|
18
29
|
"icon": {
|
|
19
30
|
"type": "node",
|
|
20
31
|
"optional": true,
|
|
21
32
|
"description": "A 16 × 16 (`sys.icon.md`) glyph at the container's leading edge. Inherits the banner's foreground (`currentColor`) so the mark reads as part of the body copy. The slot occupies the body.sm line-box height so the glyph centers on the **first line** of the body — multi-line bodies keep the icon anchored to the first-line cap, not the block center. Ignored when `thumbnail` is also passed."
|
|
22
33
|
},
|
|
34
|
+
"trailingIcon": {
|
|
35
|
+
"type": "node",
|
|
36
|
+
"optional": true,
|
|
37
|
+
"description": "A 16 × 16 (`sys.icon.md`) glyph at the container's trailing edge, vertically centered against the whole block (`align-self: center`). Paints in `currentColor`. Reach for it when the banner leads somewhere — a forward affordance such as `ForwardCircleFillIcon` signaling the whole aside opens a destination."
|
|
38
|
+
},
|
|
23
39
|
"thumbnail": {
|
|
24
40
|
"type": "node",
|
|
25
41
|
"optional": true,
|
|
@@ -58,9 +74,16 @@
|
|
|
58
74
|
},
|
|
59
75
|
"content": {
|
|
60
76
|
"required": true,
|
|
61
|
-
"description": "Vertical column holding body and (optional) action. flex: 1 1 auto.",
|
|
77
|
+
"description": "Vertical column holding (optional) title, body, and (optional) action. flex: 1 1 auto.",
|
|
62
78
|
"intrinsic": true
|
|
63
79
|
},
|
|
80
|
+
"title": {
|
|
81
|
+
"required": false,
|
|
82
|
+
"description": "Optional heading line above the body. label.md (14 / Semibold) / inherits container's foreground. Separated from the body by `sys.layout.stack.2xs` (4) — tighter than the body↔action gap so title and body read as one passage.",
|
|
83
|
+
"accepts": [
|
|
84
|
+
"text"
|
|
85
|
+
]
|
|
86
|
+
},
|
|
64
87
|
"body": {
|
|
65
88
|
"required": true,
|
|
66
89
|
"description": "Explanation copy. body.sm / Regular / inherits container's foreground. Single paragraph; wraps freely.",
|
|
@@ -74,6 +97,13 @@
|
|
|
74
97
|
"accepts": [
|
|
75
98
|
"button"
|
|
76
99
|
]
|
|
100
|
+
},
|
|
101
|
+
"trailingIcon": {
|
|
102
|
+
"required": false,
|
|
103
|
+
"description": "16 × 16 (`sys.icon.md`) glyph slot at the trailing edge. Vertically centered against the container (`align-self: center`), paints in `currentColor`. Typically a forward affordance (e.g. `ForwardCircleFillIcon`).",
|
|
104
|
+
"accepts": [
|
|
105
|
+
"icon"
|
|
106
|
+
]
|
|
77
107
|
}
|
|
78
108
|
},
|
|
79
109
|
"sizing": {
|
|
@@ -84,8 +114,13 @@
|
|
|
84
114
|
"iconSlotHeight": "calc(sys.typo.body.sm.size * sys.typo.body.sm.line)",
|
|
85
115
|
"iconColor": "currentColor",
|
|
86
116
|
"contentStackGap": "sys.layout.stack.xs",
|
|
117
|
+
"titleTypo": "sys.typo.label.md",
|
|
118
|
+
"titleGap": "sys.layout.stack.2xs",
|
|
87
119
|
"bodyTypo": "sys.typo.body.sm",
|
|
88
|
-
"actionTypo": "sys.typo.label.md"
|
|
120
|
+
"actionTypo": "sys.typo.label.md",
|
|
121
|
+
"trailingIconSize": "sys.icon.md",
|
|
122
|
+
"trailingIconColor": "currentColor",
|
|
123
|
+
"outlineWidth": "sys.borderWidth.hairline"
|
|
89
124
|
},
|
|
90
125
|
"safeZone": {
|
|
91
126
|
"inline": {
|
|
@@ -106,18 +141,21 @@
|
|
|
106
141
|
"background": "sys.color.scrimSubtle",
|
|
107
142
|
"foreground": "sys.color.onSurface",
|
|
108
143
|
"actionColor": "sys.color.primary",
|
|
144
|
+
"outlineColor": "sys.color.outlineVariant",
|
|
109
145
|
"note": "Body sits in `onSurface`; the action link steps to primary so it carries the only chromatic emphasis. Background is the translucent inverse-tone scrim (`sys.color.scrimSubtle` — ~8% black light / ~8% white dark) so the banner stays harmonious on any underlying surface — body, raised card, BottomSheet, Dialog — by tinting one step darker (light mode) or lighter (dark mode) instead of pinning to a fixed neutral step that can collide with the surface ladder. Same scrim used by Chip / Tag default, Progress track, StatusTag neutral, and Skeleton."
|
|
110
146
|
},
|
|
111
147
|
"accent": {
|
|
112
148
|
"background": "sys.color.primaryContainer",
|
|
113
149
|
"foreground": "sys.color.onPrimaryContainer",
|
|
114
150
|
"actionColor": "inherit",
|
|
151
|
+
"outlineColor": "color-mix(sys.color.primary, 40%)",
|
|
115
152
|
"note": "Both body and action paint in the primary family so the whole banner reads as one highlighted block. Reach for `accent` when the aside should pull more attention — feature explainers, capability nudges."
|
|
116
153
|
},
|
|
117
154
|
"destructive": {
|
|
118
155
|
"background": "sys.color.errorContainer",
|
|
119
156
|
"foreground": "sys.color.onErrorContainer",
|
|
120
157
|
"actionColor": "inherit",
|
|
158
|
+
"outlineColor": "color-mix(sys.color.error, 40%)",
|
|
121
159
|
"note": "Body and action paint in the error family so the whole banner reads as one warning block. Reach for `destructive` when the aside is a blocking error or rejection — failed approvals, integration outages, billing problems. Use sparingly — every destructive banner on a screen competes with the others for the user's alarm budget."
|
|
122
160
|
}
|
|
123
161
|
},
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# Bottom sheet
|
|
2
2
|
|
|
3
|
+
> 🇰🇷 한국어: [`i18n/ko/schema/components/bottom-sheet/bottom-sheet.md`](../../../i18n/ko/schema/components/bottom-sheet/bottom-sheet.md)
|
|
4
|
+
|
|
3
5
|
An edge-anchored interruption — a panel that rises from the bottom of the viewport, sits over a scrim, and holds richer content than a Dialog can.
|
|
4
6
|
|
|
5
7
|
**Reach for this when** you want to steer the user toward a preferred action *without severing the flow* — present enough to focus attention, light enough that dismissing returns them where they were. **Skip when** the decision must be committed before the flow can continue (use [Dialog](../dialog/dialog.md)).
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# Bubble
|
|
2
2
|
|
|
3
|
+
> 🇰🇷 한국어: [`i18n/ko/schema/components/bubble/bubble.md`](../../../i18n/ko/schema/components/bubble/bubble.md)
|
|
4
|
+
|
|
3
5
|
A small persistent annotation pill with a tail pointing at an anchor — a chat-icon "new messages" flag, a search-bar campaign nudge, a feature-flag callout. Sibling of [Tooltip](../tooltip/tooltip.md): Tooltip is transient and overlays neighbours on hover; Bubble stays in view at the resting state, sits lower in visual priority (no elevation, smaller padding, single line), and never occludes the surrounding chrome.
|
|
4
6
|
|
|
5
7
|
**Reach for this when** the annotation must remain readable as part of the resting UI. **Skip when** the hint is invoked on demand (use [Tooltip](../tooltip/tooltip.md)), carries a decision or blocking meaning (use [Banner](../banner/banner.md) / [Dialog](../dialog/dialog.md)), or is just a numeric count beside an icon (use [Badge](../badge/badge.md)).
|
|
@@ -19,10 +19,11 @@
|
|
|
19
19
|
"visualReuse": "open",
|
|
20
20
|
"layoutInset": "inline",
|
|
21
21
|
"usage": {
|
|
22
|
-
"note": "All button
|
|
22
|
+
"note": "All button ROLES are the single `Button` export selected by the `variant` prop — there is NO `<Fab>`, `<IconButton>`, `<ToggleButton>`, or `<Toolbar>` export. An `import { Fab }` failure does NOT mean the family is missing; reach for `<Button variant=\"fab\">`. The one separate named export is `ButtonGroup` (the `group` sub) — a layout wrapper for two or more Buttons, NOT a Button variant.",
|
|
23
23
|
"subs": {
|
|
24
24
|
"fab": { "variant": "fab", "example": "<Button variant=\"fab\" appearance=\"primary\" icon={<EditIcon />}>글쓰기</Button>" },
|
|
25
|
-
"icon": { "variant": "icon", "example": "<Button variant=\"icon\" aria-label=\"…\" icon={<MoreIcon />} />" }
|
|
25
|
+
"icon": { "variant": "icon", "example": "<Button variant=\"icon\" aria-label=\"…\" icon={<MoreIcon />} />" },
|
|
26
|
+
"group": { "import": "ButtonGroup", "example": "<ButtonGroup variant=\"docked\" label={<>선택한 직군: <strong>SW개발</strong></>}><Button appearance=\"outlined\" size=\"large\">연봉 정보</Button><Button appearance=\"primary\" size=\"large\">추천 채용공고</Button></ButtonGroup>", "note": "Named export — NOT a Button variant (there is no `<Button variant=\"group\">`). Lays out adjacent Buttons; `variant=\"docked\"` is the bottom-pinned action bar (surface + 16px inset + 8px gap + upward `sys.elevation.sheet` shadow + optional `label`). Renders in flow — PageShell owns the pinning." }
|
|
26
27
|
}
|
|
27
28
|
},
|
|
28
29
|
"spec": "button.md",
|
|
@@ -33,6 +34,11 @@
|
|
|
33
34
|
"md": "button.md",
|
|
34
35
|
"default": true
|
|
35
36
|
},
|
|
37
|
+
{
|
|
38
|
+
"slug": "group",
|
|
39
|
+
"spec": "group.spec.json",
|
|
40
|
+
"md": "standard.md"
|
|
41
|
+
},
|
|
36
42
|
{
|
|
37
43
|
"slug": "fab",
|
|
38
44
|
"spec": "fab.spec.json",
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# Button
|
|
2
2
|
|
|
3
|
+
> 🇰🇷 한국어: [`i18n/ko/schema/components/button/button.md`](../../../i18n/ko/schema/components/button/button.md)
|
|
4
|
+
|
|
3
5
|
The action-surface family. **Standard Button** is the default inline filled / outlined / tertiary control; the rest specialise by **shape · context · footprint**: floating canvas commit (FAB), glyph-only capsule (Icon Button), chromeless link-shaped commit (Text Button), dense capsule for toolbars and tabs (Toolbar Button), reversible commit at the Toolbar footprint (Toggle Button), option-toggle with leading checkbox (Check Button). Per-sub intent lives on each sub's page.
|
|
4
6
|
|
|
5
7
|
**Layout inset.** `inline` — slot atom. No page-rail responsibility; the surrounding container places it. Lives inside another component's slot (List row trailing, Section header trailing, NavigationBar trailing, BottomSheet action stack) or inside a layout `<div>` that already pays the page gutter. The FAB sub is the one exception — pinned to the page viewport, not the row rail.
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# Check
|
|
2
2
|
|
|
3
|
+
> 🇰🇷 한국어: [`i18n/ko/schema/components/button/check.md`](../../../i18n/ko/schema/components/button/check.md)
|
|
4
|
+
|
|
3
5
|
Option-toggle commit — a [Text Button](./text.md) with a required leading checkbox glyph that flips outline → fill on `checked`, plus an optional middle icon. Two sizes (`medium` / `small`) where the checkbox footprint is the differentiator.
|
|
4
6
|
|
|
5
7
|
**Reach for this when** an option is committed alongside the surface's main action — *Use this perk?*, *Apply offer*, *Keep me signed in*. **Skip when** the row is a form-bound checkbox input (out-of-system), the commit is a one-shot action ([Text Button](./text.md)), or the row needs a radio's single-select contract ([Radio list](../list/radio.md)).
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# FAB
|
|
2
2
|
|
|
3
|
+
> 🇰🇷 한국어: [`i18n/ko/schema/components/button/fab.md`](../../../i18n/ko/schema/components/button/fab.md)
|
|
4
|
+
|
|
3
5
|
Floating action button — surface-elevated commit anchored to the canvas, reachable while content scrolls. Single fixed rung with a pill geometry (`sys.radius.full`) and floating elevation (`sys.elevation.floating`); label and icon both optional, at least one present.
|
|
4
6
|
|
|
5
7
|
**Reach for this when** the canvas needs a single headline action that survives scroll — Compose, Add, Create. **Skip when** the action lives inline with content (use [Standard Button](./standard.md)) or the row is a dense toolbar ([Toolbar Button](./toolbar.md)).
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "../../spec.schema.json",
|
|
3
|
+
"name": "ButtonGroup",
|
|
4
|
+
"family": "button",
|
|
5
|
+
"subcomponent": "group",
|
|
6
|
+
"description": "Lays out two or more Buttons as one composition so consumers never hand-roll the wrapper div. A NAMED export (`import { ButtonGroup }`) — NOT a Button variant. Three forms share the family's 8px (`sys.layout.inline.md`) gap: inline horizontal (default), inline vertical (`orientation=\"vertical\"`), and docked (`variant=\"docked\"`) — a footer bar pinned to the bottom of the app. The docked form adds full-bleed `sys.color.surface`, a 16px (`sys.layout.container.md`) inset, an upward `sys.elevation.sheet` shadow (no top stroke), and an optional `label` above the row.",
|
|
7
|
+
"element": "div",
|
|
8
|
+
"props": {
|
|
9
|
+
"variant": {
|
|
10
|
+
"type": "enum",
|
|
11
|
+
"values": [
|
|
12
|
+
"inline",
|
|
13
|
+
"docked"
|
|
14
|
+
],
|
|
15
|
+
"default": "inline"
|
|
16
|
+
},
|
|
17
|
+
"orientation": {
|
|
18
|
+
"type": "enum",
|
|
19
|
+
"values": [
|
|
20
|
+
"horizontal",
|
|
21
|
+
"vertical"
|
|
22
|
+
],
|
|
23
|
+
"default": "horizontal",
|
|
24
|
+
"note": "Applies to the `inline` variant. `docked` is always a single horizontal row."
|
|
25
|
+
},
|
|
26
|
+
"label": {
|
|
27
|
+
"type": "node",
|
|
28
|
+
"optional": true,
|
|
29
|
+
"note": "Optional caption above the row. Rendered in `sys.typo.body.md` (16px) / `sys.color.onSurfaceVariant`, centered. An inline <strong> reads as the emphasized value in the full-strength on-surface tone."
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
"slots": {
|
|
33
|
+
"children": {
|
|
34
|
+
"required": true,
|
|
35
|
+
"description": "The Buttons to group. In the `docked` variant they split the row equally (each `flex: 1`), so no per-Button `fullWidth` is needed.",
|
|
36
|
+
"accepts": [
|
|
37
|
+
"button"
|
|
38
|
+
]
|
|
39
|
+
},
|
|
40
|
+
"label": {
|
|
41
|
+
"required": false,
|
|
42
|
+
"description": "Optional caption above the row (`label` prop). `sys.typo.body.md` (16px), `sys.color.onSurfaceVariant`, centered, 16px (`sys.layout.stack.md`) above the row.",
|
|
43
|
+
"accepts": [
|
|
44
|
+
"text"
|
|
45
|
+
]
|
|
46
|
+
}
|
|
47
|
+
},
|
|
48
|
+
"anatomy": {
|
|
49
|
+
"gap": "sys.layout.inline.md (8) — between Buttons, both axes",
|
|
50
|
+
"labelStackGap": "sys.layout.stack.md (16) — label → row",
|
|
51
|
+
"docked": {
|
|
52
|
+
"background": "sys.color.surface",
|
|
53
|
+
"padding": "sys.layout.container.md (16) — all four sides",
|
|
54
|
+
"elevation": "sys.elevation.sheet — upward shadow, the same one Bottom Sheet / Side Sheet cast",
|
|
55
|
+
"border": null,
|
|
56
|
+
"split": "each Button flex: 1 1 0 — they share the row equally"
|
|
57
|
+
}
|
|
58
|
+
},
|
|
59
|
+
"forbidden": [
|
|
60
|
+
"raw wrapper <div> with hand-rolled flex/gap instead of <ButtonGroup> — the gap, surface, and shadow are the component's contract",
|
|
61
|
+
"`variant=\"docked\"` self-pinned with position: sticky/fixed — like the other bars it renders in flow; PageShell owns the pinning",
|
|
62
|
+
"top `border` / hairline on the docked bar — separation from the scrolling body is the upward `sys.elevation.sheet` shadow, not a stroke",
|
|
63
|
+
"reaching for ButtonGroup as `<Button variant=\"group\">` — it is its own named export, not a Button variant"
|
|
64
|
+
]
|
|
65
|
+
}
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# Icon
|
|
2
2
|
|
|
3
|
+
> 🇰🇷 한국어: [`i18n/ko/schema/components/button/icon.md`](../../../i18n/ko/schema/components/button/icon.md)
|
|
4
|
+
|
|
3
5
|
The icon-only commit surface — circular transparent target carrying a single glyph. Two rungs: `large` (40 × 40 / 24-glyph) for page chrome, `medium` (32 × 32 / 16-glyph) for inside-control density.
|
|
4
6
|
|
|
5
7
|
**Reach for this when** the control's identity is the glyph and the action is reversible — [Navigation bar](../navigation-bar/navigation-bar.md) search/chat, [Dialog](../dialog/dialog.md) dismiss, feed-row "⋯". **Skip when** the action is destructive or one-shot — use standard [Button](./button.md) with a visible verb.
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# Standard
|
|
2
2
|
|
|
3
|
+
> 🇰🇷 한국어: [`i18n/ko/schema/components/button/standard.md`](../../../i18n/ko/schema/components/button/standard.md)
|
|
4
|
+
|
|
3
5
|
The default inline action surface — a labelled commit (form submit, dialog confirm, row action). Two axes: **size** (`large` / `medium` / `small`), **appearance** (`primary` / `secondary` / `outlined` / `tertiary`).
|
|
4
6
|
|
|
5
7
|
**Reach for this when** you need a labelled commit inline with content — Save, Continue, Confirm, Cancel. **Skip when** the commit must float above scrolling content ([FAB](./fab.md)), the rung is body-text-sized ([Text Button](./text.md)), or the row is a dense toolbar ([Toolbar Button](./toolbar.md)).
|
|
@@ -103,36 +105,60 @@ import { Button } from '@teamblind-chorus/ui';
|
|
|
103
105
|
|
|
104
106
|
### Group
|
|
105
107
|
|
|
106
|
-
|
|
108
|
+
Compose adjacent Buttons with **`ButtonGroup`** instead of a hand-rolled wrapper — it owns the family's **8px** gap (`sys.layout.inline.md`). Horizontal (default): outlined left, primary right. Vertical (`orientation="vertical"`): primary top, secondary below.
|
|
107
109
|
|
|
108
110
|
```preview
|
|
109
111
|
button/standard/group
|
|
110
112
|
---
|
|
111
|
-
import { Button } from '@teamblind-chorus/ui';
|
|
113
|
+
import { Button, ButtonGroup } from '@teamblind-chorus/ui';
|
|
112
114
|
|
|
113
|
-
<
|
|
114
|
-
<Button appearance="outlined" size="large">
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
<Button appearance="primary" size="large">
|
|
118
|
-
Confirm
|
|
119
|
-
</Button>
|
|
120
|
-
</div>
|
|
115
|
+
<ButtonGroup aria-label="Group example">
|
|
116
|
+
<Button appearance="outlined" size="large">See more</Button>
|
|
117
|
+
<Button appearance="primary" size="large">Confirm</Button>
|
|
118
|
+
</ButtonGroup>
|
|
121
119
|
```
|
|
122
120
|
|
|
123
121
|
```preview
|
|
124
122
|
button/standard/group-vertical
|
|
125
123
|
---
|
|
126
|
-
import { Button } from '@teamblind-chorus/ui';
|
|
124
|
+
import { Button, ButtonGroup } from '@teamblind-chorus/ui';
|
|
125
|
+
|
|
126
|
+
<ButtonGroup orientation="vertical" aria-label="Group example">
|
|
127
|
+
<Button appearance="primary" size="large" fullWidth>Save</Button>
|
|
128
|
+
<Button appearance="secondary" size="large" fullWidth>Cancel</Button>
|
|
129
|
+
</ButtonGroup>
|
|
130
|
+
```
|
|
127
131
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
132
|
+
### Docked action bar
|
|
133
|
+
|
|
134
|
+
`ButtonGroup` with **`variant="docked"`** — two **Large** Buttons combined into a footer bar pinned to the bottom of the app, the screen-level commit row for a flow (a picker, a form, a detail view). Full-bleed `sys.color.surface` with a **16px** inset (`sys.layout.container.md`); the two Buttons split the row equally at the family's 8px gap (`sys.layout.inline.md`). No top stroke — instead an upward `sys.elevation.sheet` shadow (the same one [Bottom Sheet](../bottom-sheet/bottom-sheet.md) / [Side Sheet](../side-sheet/side-sheet.md) cast) lifts it off the scrolling body, so content passing behind reads as a separate region. Supplementary **outlined** left, **primary** commit right. Renders in flow — like the other bars it must **not** self-pin (`position: sticky`/`fixed`); [Page Shell](../page-shell/page-shell.md) owns the pinning.
|
|
135
|
+
|
|
136
|
+
```preview
|
|
137
|
+
button/standard/docked-bar
|
|
138
|
+
---
|
|
139
|
+
import { Button, ButtonGroup } from '@teamblind-chorus/ui';
|
|
140
|
+
|
|
141
|
+
<ButtonGroup variant="docked" aria-label="Page actions">
|
|
142
|
+
<Button appearance="outlined" size="large">Salary info</Button>
|
|
143
|
+
<Button appearance="primary" size="large">View jobs</Button>
|
|
144
|
+
</ButtonGroup>
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
An **optional text label** sits above the row — pass it via the `label` prop. It's a caption echoing what the Buttons act on (the current selection, a running total), rendered in `sys.typo.body.md` (**16px**) / `sys.color.onSurfaceVariant`, centered, separated from the row by `sys.layout.stack.md` (16px); an inline `<strong>` reads as the emphasized value in the full-strength on-surface tone.
|
|
148
|
+
|
|
149
|
+
```preview
|
|
150
|
+
button/standard/docked-bar-labeled
|
|
151
|
+
---
|
|
152
|
+
import { Button, ButtonGroup } from '@teamblind-chorus/ui';
|
|
153
|
+
|
|
154
|
+
<ButtonGroup
|
|
155
|
+
variant="docked"
|
|
156
|
+
aria-label="Page actions"
|
|
157
|
+
label={<>Selected role: <strong>SW Engineer</strong></>}
|
|
158
|
+
>
|
|
159
|
+
<Button appearance="outlined" size="large">Salary info</Button>
|
|
160
|
+
<Button appearance="primary" size="large">View jobs</Button>
|
|
161
|
+
</ButtonGroup>
|
|
136
162
|
```
|
|
137
163
|
|
|
138
164
|
### Truncation
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# Text
|
|
2
2
|
|
|
3
|
+
> 🇰🇷 한국어: [`i18n/ko/schema/components/button/text.md`](../../../i18n/ko/schema/components/button/text.md)
|
|
4
|
+
|
|
3
5
|
The link-shaped commit surface — reads as text at rest, paints a button-like hover overlay and focus ring on interaction. Two axes: **appearance** (`default` / `accent` / `onPrimary` / `inverse`), **size** (`medium` / `small` / `xsmall`).
|
|
4
6
|
|
|
5
7
|
**Reach for this when** the action is inline next to typographic content and commits — *Skip*, *Edit*, *Resend*, a section's trailing *See all*. **Skip when** the affordance navigates — use [Text link](../../DESIGN.md#text-links).
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# Toggle
|
|
2
2
|
|
|
3
|
+
> 🇰🇷 한국어: [`i18n/ko/schema/components/button/toggle.md`](../../../i18n/ko/schema/components/button/toggle.md)
|
|
4
|
+
|
|
3
5
|
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
6
|
|
|
5
7
|
**Reach for this when** you need a reversible commit that persists across views — *Follow / Following*, *Subscribe / Subscribed*, *Join / Joined*. **Skip when** the action is one-shot ([Standard Button](./standard.md)), the row is a dense toolbar ([Toolbar Button](./toolbar.md)), or the toggle belongs to a filter set ([Filter Chip](../chip/filter.md)).
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# Toolbar
|
|
2
2
|
|
|
3
|
+
> 🇰🇷 한국어: [`i18n/ko/schema/components/button/toolbar.md`](../../../i18n/ko/schema/components/button/toolbar.md)
|
|
4
|
+
|
|
3
5
|
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
6
|
|
|
5
7
|
**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)).
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# Carousel
|
|
2
2
|
|
|
3
|
+
> 🇰🇷 한국어: [`i18n/ko/schema/components/carousel/carousel.md`](../../../i18n/ko/schema/components/carousel/carousel.md)
|
|
4
|
+
|
|
3
5
|
Page-region wrapper for editorial collections — a labelled block with a leading heading and an optional trailing `See all` link, hosting a horizontal swipeable rail underneath. Carousel owns the chrome (surface, padding, header anatomy); each sub owns only its pager + cards.
|
|
4
6
|
|
|
5
7
|
- **[Post](./post.md)** — a swipeable rail of compact post cards. Surfaces a curated set of popular posts or gives paid / verified accounts priority placement inside the feed column.
|