@teamblind-chorus/ui 1.0.1 โ 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 +1 -2
- package/agents/reconstruct.md +0 -55
- package/agents/scoped-adoption.md +0 -111
|
@@ -3,13 +3,18 @@
|
|
|
3
3
|
"name": "Metadata",
|
|
4
4
|
"family": "metadata",
|
|
5
5
|
"subcomponent": "compact",
|
|
6
|
-
"description": "One-line
|
|
6
|
+
"description": "One-line channel-detail attribution โ the slimmed [Standard](standard.md) head that keeps the leading 32-rung [Thumbnail](../thumbnail/thumbnail.md) but drops the primary name line, follow toggle, and subtitle (the info that is unnecessary once the channel context is already established). A leading avatar plus the identity meta-link row, with the posting time relocated to the line's trailing edge: avatar ยท company name ยท nickname (bare, no @ prefix, optional single role badge) ยท timestamp. The identity items keep the standard meta-row grammar (independent `<a>` links, middot separators, badge outside the link); the timestamp is plain text in `sys.color.outline` so the line reads identity-first, time-last. `layoutInset: inline` โ atom-shaped, pays no padding of its own; the host row owns the gutter / divider / click target.",
|
|
7
7
|
"element": "div",
|
|
8
8
|
"props": {
|
|
9
9
|
"variant": {
|
|
10
10
|
"type": "literal",
|
|
11
11
|
"value": "compact"
|
|
12
12
|
},
|
|
13
|
+
"avatar": {
|
|
14
|
+
"type": "object",
|
|
15
|
+
"optional": true,
|
|
16
|
+
"description": "Leading [Thumbnail](../thumbnail/thumbnail.md) at `size={32}` โ same prop and rung as the [Standard](standard.md) head. Forwards every Thumbnail prop verbatim (`{ src, alt }`). When omitted, the Thumbnail renders its image-area fallback over `surfaceContainerHigh`."
|
|
17
|
+
},
|
|
13
18
|
"meta": {
|
|
14
19
|
"type": "node",
|
|
15
20
|
"required": true,
|
|
@@ -18,15 +23,23 @@
|
|
|
18
23
|
"timestamp": {
|
|
19
24
|
"type": "string",
|
|
20
25
|
"required": true,
|
|
21
|
-
"description": "Posting time at the line's trailing edge โ plain text (never a link),
|
|
26
|
+
"description": "Posting time at the line's trailing edge โ plain text (never a link), in `label.sm` / `sys.color.outline` so it recedes behind the identity links. NOT preceded by a middot: the time is separated from the identity cluster by an `inline.md` (8) gap so it reads as a distinct trailing element rather than another identity item (mirrors the Standard head's nameโtime treatment). Required: the timestamp is what distinguishes a compact attribution from a bare identity row."
|
|
22
27
|
}
|
|
23
28
|
},
|
|
24
29
|
"slots": {
|
|
25
30
|
"container": {
|
|
26
31
|
"required": true,
|
|
27
|
-
"description": "Outer row โ
|
|
32
|
+
"description": "Outer row โ flex row, `align-items: center`, hosting the leading avatar and the single text line. `min-width: 0` so the line can truncate inside a flexed host row.",
|
|
28
33
|
"intrinsic": true
|
|
29
34
|
},
|
|
35
|
+
"avatar": {
|
|
36
|
+
"required": false,
|
|
37
|
+
"description": "Leading [Thumbnail](../thumbnail/thumbnail.md) at `size={32}` โ identical to the Standard head. Vertically centered against the one-line meta cluster.",
|
|
38
|
+
"accepts": [
|
|
39
|
+
"thumbnail"
|
|
40
|
+
],
|
|
41
|
+
"rendersAs": "thumbnail:32"
|
|
42
|
+
},
|
|
30
43
|
"meta": {
|
|
31
44
|
"required": true,
|
|
32
45
|
"description": "Identity meta-link row โ company name then nickname, each its own `<a>`; siblings separate by middot; `flex-wrap: nowrap` so the cluster stays a single text line. Hover paints the underline on the link alone โ the middot stays unstyled.",
|
|
@@ -34,7 +47,7 @@
|
|
|
34
47
|
},
|
|
35
48
|
"timestamp": {
|
|
36
49
|
"required": true,
|
|
37
|
-
"description": "Trailing plain-text posting time after the
|
|
50
|
+
"description": "Trailing plain-text posting time after the identity cluster โ no leading middot, separated by an `inline.md` (8) gap. `sys.typo.label.sm` / `sys.color.outline` โ one tonal step further than the identity links.",
|
|
38
51
|
"accepts": [
|
|
39
52
|
"text"
|
|
40
53
|
]
|
|
@@ -54,13 +67,13 @@
|
|
|
54
67
|
"note": "Compact Metadata itself has no lifecycle states โ each identity link carries its own state contract; the timestamp is inert."
|
|
55
68
|
},
|
|
56
69
|
"behavior": {
|
|
57
|
-
"dotHeight": "Every middot separator (between identity items
|
|
70
|
+
"dotHeight": "Every middot separator (between identity items) uses `line-height: 1` so its line-box matches its font-size โ same family-wide rule as the standard sub. The timestamp carries no leading middot โ it is separated from the identity cluster by an `inline.md` gap.",
|
|
58
71
|
"linkAffordances": "Identity items render as independent `<a>` elements โ taps land on the link, not the host row. The timestamp is plain text and never intercepts the row's own tap target.",
|
|
59
72
|
"singleLine": "The cluster is one text line โ the meta row never wraps. When the host row is narrower than the cluster, the identity links truncate; the timestamp keeps its full width.",
|
|
60
73
|
"metaBadge": "An object identity item's `badge` node renders after the item's `<a>` at the middots' 4px (`inline.sm`) gap โ the mark annotates the nickname without becoming link content."
|
|
61
74
|
},
|
|
62
75
|
"forbidden": [
|
|
63
|
-
"
|
|
76
|
+
"entity-name primary line, follow toggle, subtitle, or trailing slot painted on the compact form โ those belong to the [Standard](standard.md) head; compact keeps only the leading avatar plus the one-line identity / timestamp",
|
|
64
77
|
"timestamp omitted or rendered as a link โ the plain trailing timestamp anchors the line and is what makes it an attribution",
|
|
65
78
|
"timestamp placed before the identity items โ the canonical order is company ยท nickname ยท timestamp, time last",
|
|
66
79
|
"nickname displayed with an @ prefix โ nicknames render bare, family-wide",
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"$schema": "../../family.schema.json",
|
|
3
3
|
"family": "metadata",
|
|
4
4
|
"name": "Metadata",
|
|
5
|
-
"description": "Author / brand attribution cluster shared by [Feed Post](../feed/post.md) and [Feed Ad](../feed/ad.md). A leading 32-rung [Thumbnail](../thumbnail/thumbnail.md), a primary line (entity name + optional inline timestamp + optional follow toggle), and an optional secondary line (a plain `subtitle` text โ canonical 'Sponsored' for ads โ or an array of independently-linked `meta` items: location, job function, and โ canonically last โ the user's nickname, displayed bare with no @ prefix). Optional trailing slot hosts a row-level affordance like the dismiss ร button on ads. The one-line [Compact](compact.md) sub drops the primary line
|
|
5
|
+
"description": "Author / brand attribution cluster shared by [Feed Post](../feed/post.md) and [Feed Ad](../feed/ad.md). A leading 32-rung [Thumbnail](../thumbnail/thumbnail.md), a primary line (entity name + optional inline timestamp + optional follow toggle), and an optional secondary line (a plain `subtitle` text โ canonical 'Sponsored' for ads โ or an array of independently-linked `meta` items: location, job function, and โ canonically last โ the user's nickname, displayed bare with no @ prefix). Optional trailing slot hosts a row-level affordance like the dismiss ร button on ads. The one-line [Compact](compact.md) sub keeps the leading 32-rung avatar but drops the primary name line, follow toggle, and subtitle โ channel-detail attribution: avatar ยท company name ยท nickname (optional role badge) ยท trailing timestamp. The middot separator's line-box is constrained to its font-size so the dot never inflates the surrounding text line.",
|
|
6
6
|
"useCases": [
|
|
7
7
|
"feed post author attribution",
|
|
8
8
|
"feed ad brand attribution",
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
"layoutInset": "inline",
|
|
14
14
|
"wrapperGuidance": "Atom โ pays no inline padding of its own. Sits inside whichever Feed sub-component composes it (Post / Ad card head); the host pays surrounding rhythm. Do NOT wrap Metadata in a per-row padding-inline div.",
|
|
15
15
|
"usage": {
|
|
16
|
-
"note": "Both shapes are the single `Metadata` export โ the one-line
|
|
16
|
+
"note": "Both shapes are the single `Metadata` export โ the one-line channel-detail shape is `variant=\"compact\"`, NOT a separate `<MetadataCompact>`. Leading thumbnail is the `avatar` prop (NOT `thumbnail`) on both shapes; the secondary line is either a `subtitle` string or a `meta` string array โ not children.",
|
|
17
17
|
"subs": {
|
|
18
18
|
"standard": {
|
|
19
19
|
"import": "Metadata",
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
},
|
|
22
22
|
"compact": {
|
|
23
23
|
"variant": "compact",
|
|
24
|
-
"example": "<Metadata variant=\"compact\" meta={[\"Maple Mill Bakery\", \"ryestarter\"]} timestamp=\"35m\" />"
|
|
24
|
+
"example": "<Metadata variant=\"compact\" avatar={{ src, alt }} meta={[\"Maple Mill Bakery\", \"ryestarter\"]} timestamp=\"35m\" />"
|
|
25
25
|
}
|
|
26
26
|
}
|
|
27
27
|
},
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
# Metadata
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
> ๐ฐ๐ท ํ๊ตญ์ด: [`i18n/ko/schema/components/metadata/metadata.md`](../../../i18n/ko/schema/components/metadata/metadata.md)
|
|
4
|
+
|
|
5
|
+
Author / brand attribution cluster. The family covers two shapes riding the same `label.sm` text grammar: **[Standard](./standard.md)** โ the avatar + two-line head shared by [Feed Post](../feed/post.md) and [Feed Ad](../feed/ad.md) (entity name + optional timestamp / follow on top, subtitle or meta-link row below); **[Compact](./compact.md)** โ the one-line channel-detail attribution (leading 32-rung avatar ยท company name ยท nickname ยท timestamp), the slimmed head with the name line, follow, and subtitle dropped. Per-sub intent lives on each sub's page.
|
|
4
6
|
|
|
5
7
|
**Layout inset.** `inline` โ Metadata ships no padding of its own. Sits inside whichever host composes it (Feed Post / Ad card head, comment row); the host pays surrounding rhythm, divider, and tap target.
|
|
6
8
|
|
|
@@ -23,4 +25,4 @@ Every identity item (entity name, meta items) is its own `<a>` โ taps land on
|
|
|
23
25
|
## Sub-components
|
|
24
26
|
|
|
25
27
|
- **[Standard](./standard.md)** *(default)* โ Avatar + two-line attribution head for Feed Post / Feed Ad. Primary line (name + timestamp + follow) over an optional secondary line (subtitle or meta-link row); optional trailing affordance.
|
|
26
|
-
- **[Compact](./compact.md)** โ One-line
|
|
28
|
+
- **[Compact](./compact.md)** โ One-line channel-detail attribution: leading 32-rung avatar ยท company name ยท nickname (optional role badge) ยท trailing plain timestamp. The slimmed Standard head with the primary name line, follow, and subtitle dropped. Reached as `variant="compact"` on the same `Metadata` export.
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# Standard
|
|
2
2
|
|
|
3
|
+
> ๐ฐ๐ท ํ๊ตญ์ด: [`i18n/ko/schema/components/metadata/standard.md`](../../../i18n/ko/schema/components/metadata/standard.md)
|
|
4
|
+
|
|
3
5
|
The default attribution head of the [Metadata](./metadata.md) family, shared by [Feed Post](../feed/post.md) and [Feed Ad](../feed/ad.md). A leading 32-rung [Thumbnail](../thumbnail/thumbnail.md), a primary line (entity name + optional inline timestamp + optional follow toggle), and an optional secondary line (a plain `subtitle` text โ canonical 'Sponsored' for ads โ or an array of independently-linked `meta` items: location, job function, and โ canonically last โ the user's nickname, displayed bare with no @ prefix). Optional trailing slot hosts a row-level affordance like the dismiss ร button on ads.
|
|
4
6
|
|
|
5
7
|
**Reach for this when** composing the head of a Feed Post or Feed Ad โ the cluster owns the avatar + identity + meta. **Skip when** you need a one-line comment / reply attribution โ company name ยท nickname ยท timestamp, no avatar (use [Compact](./compact.md)) โ a generic entity directory row with up to three text lines and a single trailing commit (use [list/entry](../list/entry.md)), a labelled-region heading (use [Header](../header/header.md)), or a profile / channel page hero (use [Profile header](../profile-header/profile-header.md)).
|
|
@@ -67,6 +69,28 @@ import { XIcon } from '@teamblind-chorus/ui/icons';
|
|
|
67
69
|
/>
|
|
68
70
|
```
|
|
69
71
|
|
|
72
|
+
### With role badge
|
|
73
|
+
|
|
74
|
+
A meta item in object form carries `badge` โ a single presentational mark rendered after the item's link, outside the `<a>`, at the middots' 4px gap. Canonical fill: a role [Badge](../badge/badge.md) on the trailing nickname. At most one badge rides the nickname, never a stack.
|
|
75
|
+
|
|
76
|
+
```preview
|
|
77
|
+
metadata/standard/badge
|
|
78
|
+
---
|
|
79
|
+
import { Metadata, Badge } from '@teamblind-chorus/ui';
|
|
80
|
+
|
|
81
|
+
<Metadata
|
|
82
|
+
avatar={{ src: '/placeholder.png', alt: 'Sourdough Bakers' }}
|
|
83
|
+
name="Sourdough Bakers"
|
|
84
|
+
nameHref="#"
|
|
85
|
+
timestamp="2h"
|
|
86
|
+
meta={[
|
|
87
|
+
'Brooklyn, NY',
|
|
88
|
+
'Home baker',
|
|
89
|
+
{ label: 'crustcrumb', href: '#', badge: <Badge variant="role">Verified</Badge> },
|
|
90
|
+
]}
|
|
91
|
+
/>
|
|
92
|
+
```
|
|
93
|
+
|
|
70
94
|
## Slots
|
|
71
95
|
|
|
72
96
|
- **container** โ outer flex row. `align-items: center`, `sys.layout.inline.md` (8px) gap between avatar, text column, and trailing slot.
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# Nav card
|
|
2
2
|
|
|
3
|
+
> ๐ฐ๐ท ํ๊ตญ์ด: [`i18n/ko/schema/components/nav-card/nav-card.md`](../../../i18n/ko/schema/components/nav-card/nav-card.md)
|
|
4
|
+
|
|
3
5
|
A bounded single-row card โ outlined rounded surface with a label, optional supporting line, and optional trailing affordance. Two variants pick the trailing shape: `default` ships no trailing icon (bare labelled tile), `nav` auto-renders the right-pointing chevron (explicit drill-in). The whole card is the tap target.
|
|
4
6
|
|
|
5
7
|
**Reach for this when** one row needs to read as its own discrete affordance โ a labelled scope tile (`default`), a standalone settings drill-in (`nav`), a picker trigger, or a channel / sub-brand entry card. **Skip when** rows stack into a vertical column (use a [List](../list/list.md) drill-in row โ `nav: true`), the action is a commit (use [Button](../button/button.md)), or the surface is purely informational (use [Banner](../banner/banner.md)).
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# Nav list
|
|
2
2
|
|
|
3
|
+
> ๐ฐ๐ท ํ๊ตญ์ด: [`i18n/ko/schema/components/nav-list/nav-list.md`](../../../i18n/ko/schema/components/nav-list/nav-list.md)
|
|
4
|
+
|
|
3
5
|
A vertical label-only nav list โ labelled block where each row carries a label (and an optional supporting line) plus a trailing chevron Icon Button, and routes via `href` / `onClick`. Bundles a [Header](../header/header.md) over a label-only [List](../list/entry.md) `variant="entry"` so the section title and the route group come as one composition. Anatomy is intentionally label-only โ `thumbnail` is omitted on every row, the leading column collapses, and the trailing rail carries a default [Icon Button](../button/icon.md) with a right-pointing chevron.
|
|
4
6
|
|
|
5
7
|
**Reach for this when** the screen shows a category index, settings menu, or any "pick a sub-page" set where each row is purely a route target. **Skip when** the rows need a leading thumbnail (use [DirectoryList](../directory-list/directory-list.md)) or the rows commit in place rather than route (use [List](../list/standard.md) `variant="standard"`).
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# Main
|
|
2
2
|
|
|
3
|
+
> ๐ฐ๐ท ํ๊ตญ์ด: [`i18n/ko/schema/components/navigation-bar/main.md`](../../../i18n/ko/schema/components/navigation-bar/main.md)
|
|
4
|
+
|
|
3
5
|
The landing-screen top bar โ anchored to a tab root (feed, inbox, profile). A leading menu glyph plus left-aligned page name sit at the start; **up to four** trailing icon actions (conventionally search, chat, profile) sit at the end. Title carries the system's largest page-level rung (`typo.heading.lg`, 24/Semibold). The same row also serves a **drill-in (content-detail) screen** โ pass `onBack` instead of `onMenuClick` and the leading glyph swaps to a back chevron, with the trailing cluster carrying up to four actions (see [Use cases](#use-cases)).
|
|
4
6
|
|
|
5
7
|
**Reach for this when** the screen is a tab root and needs the menu drawer plus a small set of global affordances, **or** when it's a content-detail drill-in (a post / article reached from a feed) needing back navigation plus a share-and-save cluster. **Skip when** you need a centred title with a single action (use [Sub](./sub.md)) or are on a dedicated search page (use [Search](./search.md)).
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# Navigation bar
|
|
2
2
|
|
|
3
|
+
> ๐ฐ๐ท ํ๊ตญ์ด: [`i18n/ko/schema/components/navigation-bar/navigation-bar.md`](../../../i18n/ko/schema/components/navigation-bar/navigation-bar.md)
|
|
4
|
+
|
|
3
5
|
The top app bar โ a horizontal strip pinned to the top of a screen that names the screen and exposes its highest-priority actions. Three sub-flavors share this contract: **Main** (landing-screen bar), **Sub** (drill-in bar with centred title), and **Search** (search-page bar with a bare-text input). All three sit at 16px inline / 8px block padding and delegate icon slots to [Icon Button](../button/icon.md).
|
|
4
6
|
|
|
5
7
|
**Layout inset.** `full-bleed` โ sits flush at the top of the page shell. The bar owns its own `16px inline / 8px block` padding via `layout.container.*`; do **not** wrap it in another `padding-inline` / `px-*` / `style={{ padding: โฆ }}` div. The bar is pinned chrome, not a `<main>` child โ sits *outside* the `<main>` that pays `sys.layout.page.*`. See [`AGENTS.md` ยง Composition rules](../../../AGENTS.md#composition-rules).
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# Search
|
|
2
2
|
|
|
3
|
+
> ๐ฐ๐ท ํ๊ตญ์ด: [`i18n/ko/schema/components/navigation-bar/search.md`](../../../i18n/ko/schema/components/navigation-bar/search.md)
|
|
4
|
+
|
|
3
5
|
The search top bar โ anchored to a dedicated search page reached from a [Main](./main.md) or [Sub](./sub.md) bar's search trigger. Owns the entire search affordance: leading back-arrow Icon Button, single bare-text input filling the row, conditional trailing clear (*ร*). Drops the centred title โ the input *is* the focus.
|
|
4
6
|
|
|
5
7
|
**Reach for this when** the entire screen exists to capture a query and show results. **Skip when** search is one affordance among several on a tab root (use [Main](./main.md) with a search trailing action) or when the field lives in-line with content (use the [search](../form-field/search.md) form-field).
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# Sub
|
|
2
2
|
|
|
3
|
+
> ๐ฐ๐ท ํ๊ตญ์ด: [`i18n/ko/schema/components/navigation-bar/sub.md`](../../../i18n/ko/schema/components/navigation-bar/sub.md)
|
|
4
|
+
|
|
3
5
|
The drill-in top bar โ anchored to every screen one step inside a flow. Centred page name, flanked by two independent side slots โ each optionally a [Text Button](../button/text.md), an [Icon Button](../button/icon.md), or a [Toolbar Button](../button/toolbar.md) / text link, chosen per side. The title itself is optional too: omit it when the side actions say everything (composer bars). Title drops from Main's `typo.heading.lg` to `typo.heading.sm` (16/Semibold).
|
|
4
6
|
|
|
5
7
|
**Reach for this when** the screen is one step inside a flow and needs a back affordance plus a single primary action. **Skip when** you're on a tab root (use [Main](./main.md)) or a dedicated search page (use [Search](./search.md)).
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# PageShell
|
|
2
2
|
|
|
3
|
+
> ๐ฐ๐ท ํ๊ตญ์ด: [`i18n/ko/schema/components/page-shell/page-shell.md`](../../../i18n/ko/schema/components/page-shell/page-shell.md)
|
|
4
|
+
|
|
3
5
|
The app scaffold that **pins** `NavigationBar` (top) and `TabBar` (bottom) while only the body scrolls.
|
|
4
6
|
|
|
5
7
|
## Why it exists
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "../../family.schema.json",
|
|
3
|
+
"family": "pagination",
|
|
4
|
+
"name": "Pagination",
|
|
5
|
+
"description": "Decorative dot-position indicator for one-page-at-a-time pagers. An inline element (`span` / inline-flex) sized to its dots โ one 6px dot per page; the active dot paints `sys.color.onSurface`, the rest paint `sys.color.outlineVariant`. Presentational only (`aria-hidden`) โ the host pager owns horizontal placement (the carousels center it), scroll position, active-index tracking, and keyboard reach; tapping a dot does not navigate. Renders nothing below two pages. Single appearance, single rung. Single-spec family.",
|
|
6
|
+
"useCases": [
|
|
7
|
+
"carousel page position (PostCarousel / ProfileCarousel)",
|
|
8
|
+
"swipeable media gallery position",
|
|
9
|
+
"onboarding pager position"
|
|
10
|
+
],
|
|
11
|
+
"visualReuse": "open",
|
|
12
|
+
"layoutInset": "inline",
|
|
13
|
+
"spec": "pagination.md",
|
|
14
|
+
"usage": {
|
|
15
|
+
"note": "Decorative only โ the root is `aria-hidden` and dots are not buttons. An inline element with intrinsic width: the host owns horizontal placement (e.g. centering under its pager) as well as the active index (e.g. via IntersectionObserver on its snap targets), and MUST keep later pages keyboard-reachable on its own. Renders nothing when `count` < 2.",
|
|
16
|
+
"example": "<Pagination count={5} activeIndex={2} />"
|
|
17
|
+
},
|
|
18
|
+
"subcomponents": [
|
|
19
|
+
{
|
|
20
|
+
"slug": "pagination",
|
|
21
|
+
"spec": "pagination.spec.json",
|
|
22
|
+
"md": "pagination.md",
|
|
23
|
+
"default": true
|
|
24
|
+
}
|
|
25
|
+
]
|
|
26
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# Pagination
|
|
2
|
+
|
|
3
|
+
> ๐ฐ๐ท ํ๊ตญ์ด: [`i18n/ko/schema/components/pagination/pagination.md`](../../../i18n/ko/schema/components/pagination/pagination.md)
|
|
4
|
+
|
|
5
|
+
Decorative dot-position indicator for one-page-at-a-time pagers โ an inline row of 6px dots, one per page, the active dot painted `sys.color.onSurface` and the rest `sys.color.outlineVariant`.
|
|
6
|
+
|
|
7
|
+
**Reach for this when** a horizontally-snapping pager needs to show where the user is โ carousel cards ([PostCarousel](../carousel/post.md), [ProfileCarousel](../carousel/profile.md)), a swipeable media gallery, an onboarding pager. **Skip when** the position is a task-completion ratio (use [Progress](../progress/progress.md)), the pages need direct tap-to-jump navigation (dots here are non-interactive โ use [Tabs](../tabs/tabs.md)), or the pager has only one page (the component renders nothing below two).
|
|
8
|
+
|
|
9
|
+
**Layout inset.** `inline` โ an inline element (`span`, `display: inline-flex`) exactly as wide as its dots, with no padding or fill of its own. It never stretches or centers itself; the host owns horizontal placement (the carousels center it via `align-self: center`) and the gap between its track and the dot row (`sys.layout.stack.md`).
|
|
10
|
+
|
|
11
|
+
## Default
|
|
12
|
+
|
|
13
|
+
Five pages with the third active. Dots are 6 ร 6, fully rounded, `sys.layout.inline.sm` apart; the row is `aria-hidden` and renders at its intrinsic width โ where it sits is the host's call.
|
|
14
|
+
|
|
15
|
+
```preview
|
|
16
|
+
pagination/default
|
|
17
|
+
---
|
|
18
|
+
import { Pagination } from '@teamblind-chorus/ui';
|
|
19
|
+
|
|
20
|
+
<Pagination count={5} activeIndex={2} />
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Slots
|
|
24
|
+
|
|
25
|
+
- **dot** *(decorative, one per page)* โ 6 ร 6 (`ref.space.75`), `sys.radius.full`. Active paints `sys.color.onSurface`; inactive paints `sys.color.outlineVariant`.
|
|
26
|
+
|
|
27
|
+
## Anatomy
|
|
28
|
+
|
|
29
|
+
| Slot | Token bindings |
|
|
30
|
+
|------|----------------|
|
|
31
|
+
| row | inline element โ `display: inline-flex`, intrinsic width, `sys.layout.inline.sm` gap, `aria-hidden`; horizontal placement belongs to the host |
|
|
32
|
+
| dot | 6 ร 6 (`ref.space.75`), `sys.radius.full`; active `sys.color.onSurface`, inactive `sys.color.outlineVariant` |
|
|
33
|
+
|
|
34
|
+
## Behavior
|
|
35
|
+
|
|
36
|
+
- **Inline element.** A `span` with `display: inline-flex` and intrinsic width โ the component paints no layout opinion beyond its own dot row. Centering under a pager (or any other placement) is a host binding: the carousels apply `align-self: center` on their own selector.
|
|
37
|
+
- **Decorative.** The root carries `aria-hidden="true"`. Dots are not buttons; tapping one does not scroll. Because of this, the host MUST keep later pages reachable another way โ focusable page contents in natural tab order, scroll-into-view on focus (see the carousel specs' `keyboardReach` rule).
|
|
38
|
+
- **Host owns the index.** Pagination never observes scroll itself. The host derives `activeIndex` โ typically an IntersectionObserver at a ~60% threshold on its scroll-snap targets โ and the component paints it, clamped to `0..count-1`.
|
|
39
|
+
- **Minimum two pages.** Renders nothing when `count` < 2; the host needs no conditional of its own.
|
|
40
|
+
- **No emphasis axis.** A single appearance โ active is `onSurface`, never a chromatic accent. Position is chrome, not a call to action.
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "../../spec.schema.json",
|
|
3
|
+
"name": "Pagination",
|
|
4
|
+
"family": "pagination",
|
|
5
|
+
"description": "Decorative dot-position indicator for one-page-at-a-time pagers. An inline element (`span`, `display: inline-flex`) sized to its dots โ it never stretches or centers itself; the host owns horizontal placement (the carousels center it via `align-self: center`). Renders one 6px (`ref.space.75`) fully-rounded dot per page in a `sys.layout.inline.sm` row; the active dot paints `sys.color.onSurface`, the rest paint `sys.color.outlineVariant`. The row is presentational only (`aria-hidden`): the host pager (PostCarousel, ProfileCarousel, a media gallery) tracks the active index โ typically via IntersectionObserver on its snap targets โ and passes it down; tapping a dot does not navigate. Renders nothing when `count` < 2 โ a one-page pager has no position to indicate.",
|
|
6
|
+
"element": "span",
|
|
7
|
+
"props": {
|
|
8
|
+
"count": {
|
|
9
|
+
"type": "number",
|
|
10
|
+
"default": 0,
|
|
11
|
+
"description": "Total number of pages โ one dot renders per page. Below 2 the component renders nothing."
|
|
12
|
+
},
|
|
13
|
+
"activeIndex": {
|
|
14
|
+
"type": "number",
|
|
15
|
+
"default": 0,
|
|
16
|
+
"description": "Zero-based index of the active page. Clamped to `0..count-1`. Owned by the host pager (e.g. updated from an IntersectionObserver on its scroll-snap targets)."
|
|
17
|
+
}
|
|
18
|
+
},
|
|
19
|
+
"slots": {
|
|
20
|
+
"dot": {
|
|
21
|
+
"required": true,
|
|
22
|
+
"description": "One per page. 6 ร 6 (`ref.space.75`), `sys.radius.full`. Active paints `sys.color.onSurface`; inactive paints `sys.color.outlineVariant`.",
|
|
23
|
+
"intrinsic": true
|
|
24
|
+
}
|
|
25
|
+
},
|
|
26
|
+
"sizing": {
|
|
27
|
+
"display": "inline-flex",
|
|
28
|
+
"displayNote": "Intrinsic width โ the row is exactly as wide as its dots plus gaps. No `width`, no stretch, no self-centering; horizontal placement belongs to the host (the carousels apply `align-self: center` on their own selector).",
|
|
29
|
+
"dotSize": "ref.space.75",
|
|
30
|
+
"dotGap": "sys.layout.inline.sm",
|
|
31
|
+
"dotRadius": "sys.radius.full"
|
|
32
|
+
},
|
|
33
|
+
"appearance": {
|
|
34
|
+
"activeDot": "sys.color.onSurface",
|
|
35
|
+
"inactiveDot": "sys.color.outlineVariant"
|
|
36
|
+
},
|
|
37
|
+
"states": {
|
|
38
|
+
"default": { "note": "Pagination carries no lifecycle states โ the active index is its visual state, and the dots are non-interactive." }
|
|
39
|
+
},
|
|
40
|
+
"behavior": {
|
|
41
|
+
"inlineElement": "An inline element โ `span` with `display: inline-flex` and intrinsic width. The component never paints layout opinion beyond its own dot row; centering under a pager (or any other placement) is a host binding, not a Pagination one.",
|
|
42
|
+
"decorative": "The root carries `aria-hidden='true'`. Dots reflect the host pager's position only; they are not buttons and tapping one does not scroll. The host MUST keep later pages reachable another way (natural tab order + scroll-into-view on focus) โ see the carousel specs' `keyboardReach` rule.",
|
|
43
|
+
"hostOwnsIndex": "Pagination never observes scroll itself. The host derives `activeIndex` (typically IntersectionObserver at a ~60% threshold on its snap targets) and re-renders; the component just paints it, clamped to `0..count-1`.",
|
|
44
|
+
"minCount": "Renders nothing when `count` < 2. The host does not need its own conditional around the component."
|
|
45
|
+
},
|
|
46
|
+
"forbidden": [
|
|
47
|
+
"Dots rendered as interactive controls (button / link, tap-to-navigate) โ that needs a focusable, labelled tab-like contract this family deliberately does not carry; if navigation chrome is required, use a different component",
|
|
48
|
+
"Active dot painted with a chromatic primary / accent / brand tone โ the indicator is position chrome, not emphasis; active is `sys.color.onSurface`, full stop",
|
|
49
|
+
"Pagination used as a step / completion indicator for a linear task โ that role is `progress`",
|
|
50
|
+
"Numeric page labels, counters, or arrows composed into the row โ the family is the dot row only; numbered pagination is a different pattern",
|
|
51
|
+
"Rendering the row for a single page (`count` < 2) by bypassing the built-in guard โ a one-page pager has no position to indicate",
|
|
52
|
+
"Row stretched to the host column (`width: 100%` / block display) to center the dots from inside โ the component is an intrinsic-width inline element; centering is the host's binding"
|
|
53
|
+
]
|
|
54
|
+
}
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# Profile header
|
|
2
2
|
|
|
3
|
+
> ๐ฐ๐ท ํ๊ตญ์ด: [`i18n/ko/schema/components/profile-header/profile-header.md`](../../../i18n/ko/schema/components/profile-header/profile-header.md)
|
|
4
|
+
|
|
3
5
|
Identity block at the top of a profile detail screen โ a full-bleed cover band, an overlapping circular [Thumbnail](../thumbnail/thumbnail.md) avatar, an entity name, a visibility + follower meta row, and a trailing follow [Toggle Button](../button/toggle.md). Same `profile` contract as [Profile carousel](../carousel/profile.md) (channel topic, user, company channel) โ the carousel surfaces a fixed-shape card in a curated rail; the header is the page-level identity rung the rail's `See all` lands on.
|
|
4
6
|
|
|
5
7
|
**Reach for this when** a profile detail route opens on a followable entity that needs a cover, an avatar, a single page-level heading, and a follow affordance. **Skip when** the screen is a feed list (use [Navigation bar/home](../navigation-bar/main.md) + [Feed](../feed/feed.md)), a settings or account drill-in (use [Navigation bar/page](../navigation-bar/sub.md) + [Nav card](../nav-card/nav-card.md)), or a curated profile rail (use [Carousel](../carousel/carousel.md) + [Profile carousel](../carousel/profile.md)).
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# Progress
|
|
2
2
|
|
|
3
|
+
> ๐ฐ๐ท ํ๊ตญ์ด: [`i18n/ko/schema/components/progress/progress.md`](../../../i18n/ko/schema/components/progress/progress.md)
|
|
4
|
+
|
|
3
5
|
A single visual rung โ 8px tall, fully rounded โ that previews how far a long-running task has advanced. Determinate only: a filled indicator parks at the value's ratio. No emphasis axis: track paints with `sys.color.scrimSubtle` (the translucent inverse-tone scrim โ ~8% black light, ~8% white dark); indicator paints in `inverseSurface` so the filled segment contrasts against the track regardless of theme.
|
|
4
6
|
|
|
5
7
|
**Reach for this when** a screen holds a task long enough that the user would otherwise wonder if anything is happening โ file uploads, onboarding step counters, background syncs, account migrations. **Skip when** the task resolves under 300ms, the wait is purely opaque (use [Skeleton](../skeleton/skeleton.md) for content placeholders, busy spinners for short opaque waits), or the metric is primary content rather than chrome (use a chart).
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# SideSheet
|
|
2
2
|
|
|
3
|
+
> ๐ฐ๐ท ํ๊ตญ์ด: [`i18n/ko/schema/components/side-sheet/side-sheet.md`](../../../i18n/ko/schema/components/side-sheet/side-sheet.md)
|
|
4
|
+
|
|
3
5
|
Off-canvas content column anchored to the leading or trailing edge of the viewport. Pairs with [BottomSheet](../bottom-sheet/bottom-sheet.md) as the Sheet family's other anchor โ BottomSheet for committed-sheet flows, SideSheet for off-canvas navigation columns, settings panes, channel directories, filter rails.
|
|
4
6
|
|
|
5
7
|
**Reach for this when** an off-canvas column belongs next to the current page โ a navigation drawer, a channel / topic / saved-item directory, a filter rail, a settings pane. **Skip when** the surface is a committed-sheet flow (use [BottomSheet](../bottom-sheet/bottom-sheet.md)), a confirmation prompt (use [Dialog](../dialog/dialog.md)), a labelled in-flow block (use [Section](../section/section.md)), or a permanent app-shell navigation (use [TabBar](../tab-bar/tab-bar.md) / [NavigationBar](../navigation-bar/navigation-bar.md)).
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# Skeleton
|
|
2
2
|
|
|
3
|
+
> ๐ฐ๐ท ํ๊ตญ์ด: [`i18n/ko/schema/components/skeleton/skeleton.md`](../../../i18n/ko/schema/components/skeleton/skeleton.md)
|
|
4
|
+
|
|
3
5
|
A tonal placeholder block that previews where real content will render. Paints with a translucent `scrimSubtle` overlay (~8% black in light mode, ~8% white in dark) and a slow opacity pulse โ visible on every host surface tier without colliding with a fixed neutral step. Three shapes โ `text` (default 16-line block), `block` (image / card body), `circle` (avatar). Compose multiple Skeletons inside `SkeletonGroup` to mirror the loading content's rhythm.
|
|
4
6
|
|
|
5
7
|
**Reach for this when** a list row, feed post, card cover, or avatar is being fetched and the host would otherwise paint empty. **Skip when** the wait is sub-300ms, the data is unavailable rather than loading (use an empty-state illustration), or the loading scope is the whole screen (use a centered progress indicator at page level).
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# Status tag
|
|
2
2
|
|
|
3
|
+
> ๐ฐ๐ท ํ๊ตญ์ด: [`i18n/ko/schema/components/status-tag/status-tag.md`](../../../i18n/ko/schema/components/status-tag/status-tag.md)
|
|
4
|
+
|
|
3
5
|
A small inline status pill โ a tonal mark sized for the trailing edge of a row label. Two **appearances**: `neutral` (quiet informational default) and `error` (rejection / blocked). Decorative โ never interactive.
|
|
4
6
|
|
|
5
7
|
**Reach for this when** a row label needs a short state annotation inline โ "pending" next to a user channel, "rejected" next to a failed approval, "draft" next to an in-progress post. **Skip when** the state is the row's primary content (use [List/standard](../list/standard.md) with `supportingText`), when the mark must be tappable (host row owns the click target), inside a chip row (use [chip/filter](../chip/filter.md) or [chip/tag](../chip/tag.md)), or when the mark names *who the person is* rather than a workflow state โ ์ฑ๋์ฅ / ํ์ง์ belong to [badge/role](../badge/role.md).
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# Suggestion list
|
|
2
2
|
|
|
3
|
+
> ๐ฐ๐ท ํ๊ตญ์ด: [`i18n/ko/schema/components/suggestion-list/suggestion-list.md`](../../../i18n/ko/schema/components/suggestion-list/suggestion-list.md)
|
|
4
|
+
|
|
3
5
|
A vertically-stacked block of follow-able recommendations rendered as a swipeable pager. One page shows three [list/entry](../list/entry.md)-shaped rows at the `xlarge` rung (56px [Thumbnail](../thumbnail/thumbnail.md), label + stacked `secondary` followers + `description`, trailing [Toggle Button](../button/toggle.md)); the next page peeks at the trailing edge to invite the swipe. Anatomy is entity-agnostic โ channels, people, companies, topics share one shape.
|
|
4
6
|
|
|
5
7
|
**Reach for this when** a follow-able set is best surfaced as a peek of three with horizontal paging โ recommended channels, people you may know, suggested companies. **Skip when** the full set should scan vertically with no pager (use [DirectoryList](../directory-list/directory-list.md)), the rail is a label-only nav strip ([AvatarRail](../avatar-rail/avatar-rail.md)), or the surface needs fixed-width profile cards ([Profile carousel](../carousel/profile.md)).
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# Switch
|
|
2
2
|
|
|
3
|
+
> ๐ฐ๐ท ํ๊ตญ์ด: [`i18n/ko/schema/components/switch/switch.md`](../../../i18n/ko/schema/components/switch/switch.md)
|
|
4
|
+
|
|
3
5
|
A binary active/inactive control โ a pill-shaped track with a circular thumb that translates between ends. **Inactive** reads as a `scrimSubtle` track with an `outlineVariant` hairline and a fixed-white thumb; **active** paints the track in `primary` so the contract reads chromatically without an inline label.
|
|
4
6
|
|
|
5
7
|
**Reach for this when** a setting commits the moment it changes โ notifications, privacy toggles, *show in feed*, instant-commit list trailing. **Skip when** the commit needs confirmation (use [Button](../button/button.md) + [Dialog](../dialog/dialog.md)), when the user picks one of several options ([List/radio](../list/radio.md), [Tabs](../tabs/tabs.md)), or when destructive โ Switch carries no undo.
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# Tab bar
|
|
2
2
|
|
|
3
|
+
> ๐ฐ๐ท ํ๊ตญ์ด: [`i18n/ko/schema/components/tab-bar/tab-bar.md`](../../../i18n/ko/schema/components/tab-bar/tab-bar.md)
|
|
4
|
+
|
|
3
5
|
The bottom tab bar โ a horizontal strip pinned to the bottom of the app exposing top-level destinations (Home / Company / Explore / Jobs / Notifications) in one tap. Each item stacks a 24px glyph above a 10/Regular label; active items show the filled companion glyph at `onSurface`, inactive render the outline at `onSurfaceVariant`. An item may opt into `appearance="primary"` to render a tile-shaped commit affordance โ the conventional **Create** entry at the trailing end.
|
|
4
6
|
|
|
5
7
|
**Reach for this when** you need persistent top-level navigation on a mobile-shaped viewport. **Skip when** the destinations belong inside a single surface โ use [Tabs](../tabs/underline.md) โ or when the chrome must float โ use [FAB](../button/fab.md).
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# Rounded
|
|
2
2
|
|
|
3
|
+
> ๐ฐ๐ท ํ๊ตญ์ด: [`i18n/ko/schema/components/tabs/rounded.md`](../../../i18n/ko/schema/components/tabs/rounded.md)
|
|
4
|
+
|
|
3
5
|
Rounded-rectangle tab row โ each tab a self-contained chip with a required leading icon and label. Shares chrome with [Segmented](./segmented.md) and [Filter chip](../chip/filter.md) verbatim; the single divergence is corner radius, which steps from `sys.radius.full` (capsule) to `sys.radius.md` (8) โ reads as a soft rounded rectangle, not a pill.
|
|
4
6
|
|
|
5
7
|
**Reach for this when** the row is a sort/filter switcher inside content. **Skip when** the row anchors content sections โ use [Underline](./underline.md) โ or when it's an in-place mode change โ use [Segmented](./segmented.md).
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# Segmented
|
|
2
2
|
|
|
3
|
+
> ๐ฐ๐ท ํ๊ตญ์ด: [`i18n/ko/schema/components/tabs/segmented.md`](../../../i18n/ko/schema/components/tabs/segmented.md)
|
|
4
|
+
|
|
3
5
|
The inline view-mode toggle โ a row of mutually-exclusive selectors for in-place mode changes (List โ Grid, Day โ Week โ Month).
|
|
4
6
|
|
|
5
7
|
**Reach for this when** the segments swap the *same* content's view or range in place โ List โ Grid, Day โ Week โ Month. **Skip when** segments switch between *different* panels ([Underline](./underline.md)), multiple values can co-select ([Filter chip](../chip/filter.md)), or the rung is a single-select picker over a long list ([Radio list](../list/radio.md)).
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# Tabs
|
|
2
2
|
|
|
3
|
+
> ๐ฐ๐ท ํ๊ตญ์ด: [`i18n/ko/schema/components/tabs/tabs.md`](../../../i18n/ko/schema/components/tabs/tabs.md)
|
|
4
|
+
|
|
3
5
|
A horizontal row of mutually-exclusive selections โ exactly one tab is active, and selecting one swaps the surrounding panel. Sub-flavors: **Underline** is the canonical content-section switcher; **Segmented** is the inline view-mode toggle whose chrome delegates to [Filter chip](../chip/filter.md).
|
|
4
6
|
|
|
5
7
|
**Layout inset.** `full-bleed` โ sits as a direct child of the page shell (or any surface that pays the gutter) and stretches edge-to-edge inside it. The tabs rail pays its own internal padding via `layout.container.*`; do **not** wrap it in another `padding-inline` / `px-*` / `style={{ padding: โฆ }}` div, or the page rail double-pays. Inside a bounded surface (Card / Dialog / BottomSheet / Sheet), apply the negative-margin opt-out โ see [`AGENTS.md` ยง Composition rules](../../../AGENTS.md#composition-rules).
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# Underline
|
|
2
2
|
|
|
3
|
+
> ๐ฐ๐ท ํ๊ตญ์ด: [`i18n/ko/schema/components/tabs/underline.md`](../../../i18n/ko/schema/components/tabs/underline.md)
|
|
4
|
+
|
|
3
5
|
The canonical content-section switcher โ a row of tabs anchored above a panel, with a 2px `onSurface` indicator that slides along the active tab's bottom edge.
|
|
4
6
|
|
|
5
7
|
**Reach for this when** moving between peer panels of the same surface. **Skip when** the row is an in-place mode change โ use [Segmented](./segmented.md) instead.
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# Thumbnail
|
|
2
2
|
|
|
3
|
+
> ๐ฐ๐ท ํ๊ตญ์ด: [`i18n/ko/schema/components/thumbnail/thumbnail.md`](../../../i18n/ko/schema/components/thumbnail/thumbnail.md)
|
|
4
|
+
|
|
3
5
|
A circular image โ the unit that identifies a channel, a feed author, or any small-rung image. Two optional badges ride without changing footprint: an **update dot** top-right and a **logo badge** bottom-right. A pure visual primitive โ no label of its own.
|
|
4
6
|
|
|
5
7
|
**Reach for this when** the host needs to identify an entity at a glance โ channel avatar in a list row, feed author block, AvatarRail item, NavigationBar leading. **Skip when** the surface is a labelled icon glyph (use [Icon Button](../button/icon.md)) or a sub-brand identity at fixed inline scale ([NavCard](../nav-card/nav-card.md)'s logo slot).
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# Toast
|
|
2
2
|
|
|
3
|
+
> ๐ฐ๐ท ํ๊ตญ์ด: [`i18n/ko/schema/components/toast/toast.md`](../../../i18n/ko/schema/components/toast/toast.md)
|
|
4
|
+
|
|
3
5
|
A transient confirmation strip that floats above the page after a user action lands โ saved, copied, sent, retried. Inverse-toned by default so the message contrasts with any underlying page tier; content-driven width up to a 400 cap.
|
|
4
6
|
|
|
5
7
|
**Reach for this when** you need to confirm a system outcome the user just triggered โ saved, copied, sent, retried. **Skip when** the message is contextual to the content itself (use [Banner](../banner/banner.md)), or the surface is a committed confirmation prompt (use [Dialog](../dialog/dialog.md)).
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# Tooltip
|
|
2
2
|
|
|
3
|
+
> ๐ฐ๐ท ํ๊ตญ์ด: [`i18n/ko/schema/components/tooltip/tooltip.md`](../../../i18n/ko/schema/components/tooltip/tooltip.md)
|
|
4
|
+
|
|
3
5
|
A trigger-anchored explanation bubble โ a small contrast-toned surface with a caret that points at the host. Reach for it to surface a label or short hint that does not fit on the trigger ("Manage" on an icon button, a coach-mark). Prefer [Banner](../banner/banner.md) when the message belongs inline in the reading flow, and [Toast](../toast/toast.md) when it confirms a recent user action.
|
|
4
6
|
|
|
5
7
|
**Layout inset.** `bounded-surface` โ popover shell anchored to a trigger element. Owns its outer padding and trigger-relative placement; not subject to the page shell's `layout.page.*` gutter. Mount via a portal at the document root and position relative to the trigger. See [`AGENTS.md` ยง Composition rules](../../../AGENTS.md#composition-rules).
|
package/agents/compose.md
CHANGED
|
@@ -17,7 +17,7 @@ The recipes below answer the five compositional situations every product surface
|
|
|
17
17
|
| `sys.layout.page.lg` (24โ40px) | Marketing / editorial / landing. |
|
|
18
18
|
| `sys.layout.page.xl` (40โ64px) | Showcase heroes only. |
|
|
19
19
|
|
|
20
|
-
**Paid once at the page shell.** All **
|
|
20
|
+
**Paid once at the page shell.** All **thirteen full-bleed families** (`family.json#layoutInset` is authoritative) inherit it โ `avatar-rail`, `carousel`, `directory-list`, `divider`, `feed`, `header`, `list`, `nav-list`, `navigation-bar`, `profile-header`, `suggestion-list`, `tab-bar`, `tabs` โ plus the `feed-ad` (feed sub) and `accordion` (list sub) that inherit from their parent. Never re-pay `padding-inline` on the child. `chip` is `inline`, but a chip *group* (filter rail) is rail-responsible โ place it like a full-bleed child (no padding wrapper). Inline cards (`banner`, `nav-card`) are NOT in this list โ they don't claim the page rail themselves; the host (page shell at the top level, or another container when wrapped) pays their horizontal inset for them. See LOVABLE.md ยงA.4.
|
|
21
21
|
|
|
22
22
|
### Surface interior padding
|
|
23
23
|
|
|
@@ -142,7 +142,7 @@ DESIGN.md rules condensed to a single line each. Read as **immediate-reject** tr
|
|
|
142
142
|
4. **Surface tier โค 2 levels per screen.** `surface` plus one `surface*Container` rung is the cap. A third nested surface tone reads muddy โ promote one to a different family (Banner, Card, Section header) instead of layering.
|
|
143
143
|
5. **Chip / pill / avatar radius is always `radius.full`.** A 4px-rounded "chip" is a card; pick one component. Likewise a 999-rounded "card" reads as a chip.
|
|
144
144
|
6. **Banner role decides the fill.** Informational โ `sys.color.primaryContainer`. Promotional โ `sys.color.surfaceContainerLow` (with optional brand accent on the leading icon, *not* the background). Error notice โ `sys.color.errorContainer`. **`brandContainer` is reserved for promotional tinted strips, not default banners.**
|
|
145
|
-
7. **Page inset is paid once at the page shell.** Every `full-bleed` family (
|
|
145
|
+
7. **Page inset is paid once at the page shell.** Every `full-bleed` family (AvatarRail, Carousel, DirectoryList, Divider, Feed โ plus its FeedAd sub โ Header (both `<Header>` and `<SubHeader>`), List โ plus its accordion sub โ NavList, NavigationBar, ProfileHeader, SuggestionList, TabBar, Tabs) stretches edge-to-edge and pays its own row inset internally โ never wrap one in a padded container or pass it `padding-inline`, or it double-pays the rail and lands at a different margin than its siblings. `Banner` / `nav-card` are **inline** (host owns the inset), not full-bleed; `Chip` is **inline** too, but a chip *group* is rail-responsible โ place it like a full-bleed child. See `family.json#layoutInset` โ the authoritative per-family value.
|
|
146
146
|
8. **Nesting tightens, never widens.** Parent `container.md` โ child `container.sm` โ grandchild `container.xs`. Inverting reads as compression, not hierarchy.
|
|
147
147
|
9. **Spec slot grammar is closed.** If a slot is not declared in `spec.json#slots`, it does not exist. Do not synthesize new slots, do not pass `className` / `style` overrides.
|
|
148
148
|
10. **FAB count โค 1 per screen.** Create is the single canonical commit โ additional FABs dilute the affordance.
|
|
@@ -193,7 +193,7 @@ If a step has no good match โ that's a **Chorus gap**, not a license to invent
|
|
|
193
193
|
|
|
194
194
|
## When you go custom (no Chorus family fits)
|
|
195
195
|
|
|
196
|
-
The LEGO ladder didn't surface a fit and you've genuinely exhausted visual-reuse on the
|
|
196
|
+
The LEGO ladder didn't surface a fit and you've genuinely exhausted visual-reuse on the `"open"` families (count owned by LOVABLE ยงC / `family.json#visualReuse`) โ you're building a custom primitive (small hint card, inline annotation, narrow aside). **Component went flexible; tokens did not.** With no Chorus spec to deny you, every literal value you type is either a token resolution or a violation.
|
|
197
197
|
|
|
198
198
|
The drift shape this section guards: a custom div where `background` is a token (because color rules are well-internalized) but `gap`, `padding`, `fontSize`, `lineHeight`, `borderRadius` are raw px (because the "off-scale px" rule lives in prose, not in concrete examples). Five literals, five violations โ *"I used the color token"* is not a partial pass.
|
|
199
199
|
|
package/agents/manifest.json
CHANGED
|
@@ -71,6 +71,7 @@
|
|
|
71
71
|
{ "family": "nav-list", "root": "components/nav-list/nav-list.family.json" },
|
|
72
72
|
{ "family": "navigation-bar", "root": "components/navigation-bar/navigation-bar.family.json" },
|
|
73
73
|
{ "family": "page-shell", "root": "components/page-shell/page-shell.family.json" },
|
|
74
|
+
{ "family": "pagination", "root": "components/pagination/pagination.family.json" },
|
|
74
75
|
{ "family": "profile-header", "root": "components/profile-header/profile-header.family.json" },
|
|
75
76
|
{ "family": "progress", "root": "components/progress/progress.family.json" },
|
|
76
77
|
{ "family": "carousel", "root": "components/carousel/carousel.family.json" },
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# Patterns
|
|
2
2
|
|
|
3
|
+
> ๐ฐ๐ท ํ๊ตญ์ด: [`i18n/ko/patterns/README.md`](../i18n/ko/patterns/README.md)
|
|
4
|
+
|
|
3
5
|
Canonical visual references for Chorus. Each pattern pairs an **image board** (`<slug>.png`) with a spec note (`<slug>.md`) covering intent, layout, tokens, and components. Boards are grouped by *what the surface does* โ each `.png` is a montage of representative screens so an agent can learn a category's recurring patterns and visuals at a glance.
|
|
4
6
|
|
|
5
7
|
Schemas say *what is allowed*; patterns say *what good looks like*.
|
|
@@ -4,6 +4,8 @@ image: ./actions.png
|
|
|
4
4
|
status: canonical
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
+
> ๐ฐ๐ท ํ๊ตญ์ด: [`i18n/ko/patterns/actions.md`](../i18n/ko/patterns/actions.md)
|
|
8
|
+
|
|
7
9
|
## Intent
|
|
8
10
|
|
|
9
11
|
Task-completion surfaces โ the user supplies input and commits. Verification forms, keyword/post registration, service-agreement checklists, comment composition, job filter settings, and the home content-creation shortcuts. Each surface is keyboard-first and terminates in **one** high-emphasis primary commit (a full-width or trailing blue `button / standard`), with neutral escapes parked around it. Reach here for the anatomy of "fill something in, then commit it".
|
|
@@ -4,6 +4,8 @@ image: ./browsing.png
|
|
|
4
4
|
status: canonical
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
+
> ๐ฐ๐ท ํ๊ตญ์ด: [`i18n/ko/patterns/browsing.md`](../i18n/ko/patterns/browsing.md)
|
|
8
|
+
|
|
7
9
|
## Intent
|
|
8
10
|
|
|
9
11
|
Entity discovery and navigation โ channel drawers, all/followed channel lists, Explore trending rails, search empty/suggestion states, and the company feed. Lists of entities (channels, companies, posts) are the spine; the per-row commit is **Follow / Unfollow** (`button / toggle`), and the **whole row is the nav click target**. Reach here for "browse a set of things and follow/open one".
|
|
@@ -4,6 +4,8 @@ image: ./communications.png
|
|
|
4
4
|
status: canonical
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
+
> ๐ฐ๐ท ํ๊ตญ์ด: [`i18n/ko/patterns/communications.md`](../i18n/ko/patterns/communications.md)
|
|
8
|
+
|
|
7
9
|
## Intent
|
|
8
10
|
|
|
9
11
|
Authored content and engagement โ posts, embedded polls, gated previews, comment threads, the channel picker for posting, and offer/poll compose. `feed` is the spine for content streams; `list / radio` carries poll options; compose surfaces commit via a Cancel/Post action bar. Reach here for "read, react to, or author a post or comment".
|