@teamblind-chorus/ui 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +112 -0
- package/agents/AGENTS.md +143 -0
- package/agents/DESIGN.md +1311 -0
- package/agents/LOVABLE.md +472 -0
- package/agents/anti-patterns.md +533 -0
- package/agents/catalog.md +232 -0
- package/agents/components/avatar-rail/avatar-rail.family.json +46 -0
- package/agents/components/avatar-rail/avatar-rail.md +103 -0
- package/agents/components/avatar-rail/avatar-rail.spec.json +160 -0
- package/agents/components/badge/badge.family.json +45 -0
- package/agents/components/badge/badge.md +10 -0
- package/agents/components/badge/role.md +100 -0
- package/agents/components/badge/role.spec.json +75 -0
- package/agents/components/badge/update.md +132 -0
- package/agents/components/badge/update.spec.json +114 -0
- package/agents/components/banner/banner.family.json +28 -0
- package/agents/components/banner/banner.md +136 -0
- package/agents/components/banner/banner.spec.json +136 -0
- package/agents/components/bottom-sheet/bottom-sheet.family.json +29 -0
- package/agents/components/bottom-sheet/bottom-sheet.md +176 -0
- package/agents/components/bottom-sheet/bottom-sheet.spec.json +168 -0
- package/agents/components/bubble/bubble.family.json +29 -0
- package/agents/components/bubble/bubble.md +134 -0
- package/agents/components/bubble/bubble.spec.json +91 -0
- package/agents/components/button/button.family.json +76 -0
- package/agents/components/button/button.md +31 -0
- package/agents/components/button/check.md +138 -0
- package/agents/components/button/check.spec.json +161 -0
- package/agents/components/button/fab.md +161 -0
- package/agents/components/button/fab.spec.json +106 -0
- package/agents/components/button/icon.md +141 -0
- package/agents/components/button/icon.spec.json +164 -0
- package/agents/components/button/standard.md +219 -0
- package/agents/components/button/standard.spec.json +205 -0
- package/agents/components/button/text.md +186 -0
- package/agents/components/button/text.spec.json +215 -0
- package/agents/components/button/toggle.md +108 -0
- package/agents/components/button/toggle.spec.json +124 -0
- package/agents/components/button/toolbar.md +189 -0
- package/agents/components/button/toolbar.spec.json +109 -0
- package/agents/components/carousel/carousel.family.json +41 -0
- package/agents/components/carousel/carousel.md +40 -0
- package/agents/components/carousel/post.md +148 -0
- package/agents/components/carousel/post.spec.json +229 -0
- package/agents/components/carousel/profile.md +184 -0
- package/agents/components/carousel/profile.spec.json +219 -0
- package/agents/components/chip/chip.family.json +37 -0
- package/agents/components/chip/chip.md +10 -0
- package/agents/components/chip/filter.md +212 -0
- package/agents/components/chip/filter.spec.json +124 -0
- package/agents/components/chip/tag.md +137 -0
- package/agents/components/chip/tag.spec.json +104 -0
- package/agents/components/dialog/dialog.family.json +29 -0
- package/agents/components/dialog/dialog.md +113 -0
- package/agents/components/dialog/dialog.spec.json +156 -0
- package/agents/components/directory-list/directory-list.family.json +46 -0
- package/agents/components/directory-list/directory-list.md +87 -0
- package/agents/components/directory-list/directory-list.spec.json +104 -0
- package/agents/components/divider/divider.family.json +28 -0
- package/agents/components/divider/divider.md +78 -0
- package/agents/components/divider/divider.spec.json +51 -0
- package/agents/components/feed/ad.md +108 -0
- package/agents/components/feed/ad.spec.json +187 -0
- package/agents/components/feed/feed.family.json +48 -0
- package/agents/components/feed/feed.md +30 -0
- package/agents/components/feed/post.md +240 -0
- package/agents/components/feed/post.spec.json +361 -0
- package/agents/components/form-field/form-field.family.json +50 -0
- package/agents/components/form-field/form-field.md +11 -0
- package/agents/components/form-field/input.md +198 -0
- package/agents/components/form-field/input.spec.json +202 -0
- package/agents/components/form-field/search.md +81 -0
- package/agents/components/form-field/search.spec.json +135 -0
- package/agents/components/form-field/select.md +101 -0
- package/agents/components/form-field/select.spec.json +194 -0
- package/agents/components/form-field/textarea.md +89 -0
- package/agents/components/form-field/textarea.spec.json +176 -0
- package/agents/components/header/header.family.json +43 -0
- package/agents/components/header/header.md +18 -0
- package/agents/components/header/main.md +101 -0
- package/agents/components/header/main.spec.json +117 -0
- package/agents/components/header/sub.md +129 -0
- package/agents/components/header/sub.spec.json +81 -0
- package/agents/components/list/accordion.md +183 -0
- package/agents/components/list/accordion.spec.json +201 -0
- package/agents/components/list/entry.md +280 -0
- package/agents/components/list/entry.spec.json +237 -0
- package/agents/components/list/list.family.json +75 -0
- package/agents/components/list/list.md +24 -0
- package/agents/components/list/radio.md +144 -0
- package/agents/components/list/radio.spec.json +186 -0
- package/agents/components/list/standard.md +262 -0
- package/agents/components/list/standard.spec.json +221 -0
- package/agents/components/metadata/compact.md +69 -0
- package/agents/components/metadata/compact.spec.json +69 -0
- package/agents/components/metadata/metadata.family.json +42 -0
- package/agents/components/metadata/metadata.md +26 -0
- package/agents/components/metadata/standard.md +104 -0
- package/agents/components/metadata/standard.spec.json +152 -0
- package/agents/components/nav-card/nav-card.family.json +29 -0
- package/agents/components/nav-card/nav-card.md +179 -0
- package/agents/components/nav-card/nav-card.spec.json +161 -0
- package/agents/components/nav-list/nav-list.family.json +46 -0
- package/agents/components/nav-list/nav-list.md +91 -0
- package/agents/components/nav-list/nav-list.spec.json +107 -0
- package/agents/components/navigation-bar/main.md +201 -0
- package/agents/components/navigation-bar/main.spec.json +109 -0
- package/agents/components/navigation-bar/navigation-bar.family.json +44 -0
- package/agents/components/navigation-bar/navigation-bar.md +21 -0
- package/agents/components/navigation-bar/search.md +96 -0
- package/agents/components/navigation-bar/search.spec.json +142 -0
- package/agents/components/navigation-bar/sub.md +174 -0
- package/agents/components/navigation-bar/sub.spec.json +123 -0
- package/agents/components/page-shell/page-shell.family.json +22 -0
- package/agents/components/page-shell/page-shell.md +51 -0
- package/agents/components/profile-header/profile-header.family.json +29 -0
- package/agents/components/profile-header/profile-header.md +149 -0
- package/agents/components/profile-header/profile-header.spec.json +200 -0
- package/agents/components/progress/progress.family.json +27 -0
- package/agents/components/progress/progress.md +38 -0
- package/agents/components/progress/progress.spec.json +67 -0
- package/agents/components/side-sheet/side-sheet.family.json +30 -0
- package/agents/components/side-sheet/side-sheet.md +154 -0
- package/agents/components/side-sheet/side-sheet.spec.json +109 -0
- package/agents/components/skeleton/skeleton.family.json +28 -0
- package/agents/components/skeleton/skeleton.md +123 -0
- package/agents/components/skeleton/skeleton.spec.json +73 -0
- package/agents/components/status-tag/status-tag.family.json +26 -0
- package/agents/components/status-tag/status-tag.md +114 -0
- package/agents/components/status-tag/status-tag.spec.json +69 -0
- package/agents/components/suggestion-list/suggestion-list.family.json +46 -0
- package/agents/components/suggestion-list/suggestion-list.md +91 -0
- package/agents/components/suggestion-list/suggestion-list.spec.json +178 -0
- package/agents/components/switch/switch.family.json +27 -0
- package/agents/components/switch/switch.md +114 -0
- package/agents/components/switch/switch.spec.json +123 -0
- package/agents/components/tab-bar/tab-bar.family.json +27 -0
- package/agents/components/tab-bar/tab-bar.md +178 -0
- package/agents/components/tab-bar/tab-bar.spec.json +184 -0
- package/agents/components/tabs/rounded.md +150 -0
- package/agents/components/tabs/rounded.spec.json +140 -0
- package/agents/components/tabs/segmented.md +114 -0
- package/agents/components/tabs/segmented.spec.json +100 -0
- package/agents/components/tabs/tabs.family.json +59 -0
- package/agents/components/tabs/tabs.md +18 -0
- package/agents/components/tabs/underline.md +147 -0
- package/agents/components/tabs/underline.spec.json +139 -0
- package/agents/components/thumbnail/thumbnail.family.json +28 -0
- package/agents/components/thumbnail/thumbnail.md +152 -0
- package/agents/components/thumbnail/thumbnail.spec.json +172 -0
- package/agents/components/toast/toast.family.json +28 -0
- package/agents/components/toast/toast.md +133 -0
- package/agents/components/toast/toast.spec.json +89 -0
- package/agents/components/tooltip/tooltip.family.json +29 -0
- package/agents/components/tooltip/tooltip.md +139 -0
- package/agents/components/tooltip/tooltip.spec.json +110 -0
- package/agents/compose.md +240 -0
- package/agents/icons.json +831 -0
- package/agents/images.md +66 -0
- package/agents/manifest.json +87 -0
- package/agents/patterns/README.md +59 -0
- package/agents/patterns/actions.md +50 -0
- package/agents/patterns/browsing.md +52 -0
- package/agents/patterns/communications.md +56 -0
- package/agents/patterns/layout.md +72 -0
- package/agents/patterns/modals.md +50 -0
- package/agents/patterns/visual.md +55 -0
- package/agents/reconstruct.md +55 -0
- package/agents/scoped-adoption.md +111 -0
- package/agents/tokens.usage.json +1657 -0
- package/agents/usage.json +422 -0
- package/dist/icons/index.cjs +1332 -0
- package/dist/icons/index.cjs.map +1 -0
- package/dist/icons/index.d.cts +228 -0
- package/dist/icons/index.d.ts +228 -0
- package/dist/icons/index.js +1114 -0
- package/dist/icons/index.js.map +1 -0
- package/dist/index.cjs +5905 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +896 -0
- package/dist/index.d.ts +896 -0
- package/dist/index.js +5847 -0
- package/dist/index.js.map +1 -0
- package/dist/styles.css +5765 -0
- package/eslint/README.md +79 -0
- package/eslint/index.js +78 -0
- package/eslint/rules.js +472 -0
- package/eslint/test.mjs +135 -0
- package/package.json +96 -0
- package/placeholder.png +0 -0
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
# compose.md — composition cheatsheet
|
|
2
|
+
|
|
3
|
+
A 1-page lookup for the design-token decisions every screen runs into. Skim **before composing JSX**; pair with [`tokens.usage.json`](tokens.usage.json) (which token for which slot) and [`DESIGN.md`](DESIGN.md) (deep rationale). When this file and DESIGN.md disagree, DESIGN.md wins.
|
|
4
|
+
|
|
5
|
+
The recipes below answer the five compositional situations every product surface runs into. Not new tokens — every line resolves to a step in the standard `sys.*` ladder.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Spacing recipes
|
|
10
|
+
|
|
11
|
+
### Page shell horizontal gutter
|
|
12
|
+
|
|
13
|
+
| Pick | When |
|
|
14
|
+
| --- | --- |
|
|
15
|
+
| **`sys.layout.page.md`** (16px) | Default for every ordinary app route (feed, settings, compose, detail). |
|
|
16
|
+
| `sys.layout.page.sm` (8px) | Dashboards / admin tables / dense multi-pane. |
|
|
17
|
+
| `sys.layout.page.lg` (24→40px) | Marketing / editorial / landing. |
|
|
18
|
+
| `sys.layout.page.xl` (40→64px) | Showcase heroes only. |
|
|
19
|
+
|
|
20
|
+
**Paid once at the page shell.** All **eleven full-bleed families** inherit it — `navigation-bar`, `profile-header`, `tab-bar`, `tabs`, `section`, `feed`, `list`, `accordion` (list sub), `suggestion-list`, `avatar-rail`, `chip` (group). Never re-pay `padding-inline` on the child. 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
|
+
|
|
22
|
+
### Surface interior padding
|
|
23
|
+
|
|
24
|
+
| Pick | When |
|
|
25
|
+
| --- | --- |
|
|
26
|
+
| **`sys.layout.container.md`** (16px) | Default — card, list-row, sheet content, section horizontal padding. |
|
|
27
|
+
| `sys.layout.container.sm` (12px) | Button / input-field padding. Also the one-rung step-down for a child nested inside a `container.md` parent. |
|
|
28
|
+
| `sys.layout.container.xs` (8px) | Chip body, segmented-control items, dense list rows. |
|
|
29
|
+
| `sys.layout.container.lg` (24→32px) | Dialog body, feature-card callouts, primary dialog interiors. |
|
|
30
|
+
| `sys.layout.container.xl/2xl/3xl` | Hero / marketing only. |
|
|
31
|
+
|
|
32
|
+
**Nesting rule.** Parent at `container.md` → child at `container.sm` → grandchild at `container.xs`. Same direction across the tree — never invert, never skip rungs. A 16px parent with a 4px grandchild reads as compression, not hierarchy.
|
|
33
|
+
|
|
34
|
+
### Vertical sibling rhythm (`gap` between stacked siblings)
|
|
35
|
+
|
|
36
|
+
| Pick | When |
|
|
37
|
+
| --- | --- |
|
|
38
|
+
| **`sys.layout.stack.md`** (16px) | Default — paragraph↔paragraph, card↔card, item↔item within one section. |
|
|
39
|
+
| `sys.layout.stack.xs` (8px) | One tightly-bound group (bullet rows, metadata lines, cluster of fields about the same entity). Also section↔section separator when paddings alone don't separate them. |
|
|
40
|
+
| `sys.layout.stack.2xs` (4px) | Visually bonded pairs only — label↔input, title↔subtitle, caption↔parent text. Not for general content. |
|
|
41
|
+
| `sys.layout.stack.sm` (12px) | Form field↔field gap. |
|
|
42
|
+
| `sys.layout.stack.lg` (24→32px) | Distinct content groups within a section — heading block↔body block, form group↔submit cluster. |
|
|
43
|
+
| `sys.layout.stack.xl` (32→40px) | Page-section break — strong content break, still one scroll region. |
|
|
44
|
+
|
|
45
|
+
**Apply on the shared parent via `gap`** (`flex-direction: column; gap: var(--sys-layout-stack-md)`). Never `margin-top` per child.
|
|
46
|
+
|
|
47
|
+
### Horizontal sibling rhythm
|
|
48
|
+
|
|
49
|
+
| Pick | When |
|
|
50
|
+
| --- | --- |
|
|
51
|
+
| **`sys.layout.inline.md`** (12→16px) | Default — button group, inline action cluster, icon button row. |
|
|
52
|
+
| `sys.layout.inline.sm` (8px) | Chip↔chip in a filter row, dense action cluster. |
|
|
53
|
+
| `sys.layout.inline.xs` (4px) | Glyph↔label inside a tight control. |
|
|
54
|
+
| `sys.layout.inline.lg` (12→16px) | Spacious inline pair, header trailing-action cluster. |
|
|
55
|
+
| `sys.layout.inline.xl/2xl` | Toolbar cluster / marketing pair. |
|
|
56
|
+
|
|
57
|
+
---
|
|
58
|
+
|
|
59
|
+
## Color quartet picker
|
|
60
|
+
|
|
61
|
+
Color tokens come in **four-token quartets** — `<role>` and `<role>Container` fills, each paired with `on<Role>` and `on<Role>Container` foregrounds. **Never split the pair.**
|
|
62
|
+
|
|
63
|
+
| Surface intent | Fill | Foreground |
|
|
64
|
+
| --- | --- | --- |
|
|
65
|
+
| **Page / card / list row / feed item** | `sys.color.surface` | `sys.color.onSurface` (primary text) + `sys.color.onSurfaceVariant` (meta text) |
|
|
66
|
+
| **Primary commit / link / active selection** | `sys.color.primary` | `sys.color.onPrimary` |
|
|
67
|
+
| **Soft primary tint (info banner, filter chip selected)** | `sys.color.primaryContainer` | `sys.color.onPrimaryContainer` |
|
|
68
|
+
| **Editorial / promotional / FAB** | `sys.color.brand` | `sys.color.onBrand` |
|
|
69
|
+
| **Soft brand tint (promotional callout)** | `sys.color.brandContainer` | `sys.color.onBrandContainer` |
|
|
70
|
+
| **Success state / positive metric** | `sys.color.success` | `sys.color.onSuccess` |
|
|
71
|
+
| **Destructive commit / error state** | `sys.color.error` | `sys.color.onError` |
|
|
72
|
+
| **Search input bar fill** | `sys.color.surfaceContainerLow` | `sys.color.onSurface` |
|
|
73
|
+
| **Banner / cover band / image-area underlay** | `sys.color.surfaceContainerHigh` | `sys.color.onSurface` |
|
|
74
|
+
| **Toast** | `sys.color.inverseSurface` | `sys.color.inverseOnSurface` |
|
|
75
|
+
| **Dialog / BottomSheet scrim** | `sys.color.scrim` | n/a (decorative) |
|
|
76
|
+
| **Card outline / list-row divider** | n/a (stroke) | `sys.color.outlineVariant` |
|
|
77
|
+
| **Form-field active stroke / high-emphasis divider** | n/a (stroke) | `sys.color.outline` |
|
|
78
|
+
|
|
79
|
+
---
|
|
80
|
+
|
|
81
|
+
## Type ramp picker — by surface intent
|
|
82
|
+
|
|
83
|
+
| Surface | Ramp |
|
|
84
|
+
| --- | --- |
|
|
85
|
+
| **Page-level title** (Home navigation-bar title, top-region heading) | `sys.typo.heading.lg` (24→32px) |
|
|
86
|
+
| **Section / card title** (Section label, Feed.title, Job card title) | `sys.typo.heading.md` (20px) |
|
|
87
|
+
| **Sub-section heading inside a card** | `sys.typo.heading.sm` (16px) |
|
|
88
|
+
| **Single-topic body** (article, post detail, long-form description) | `sys.typo.body.md` (16px) |
|
|
89
|
+
| **Mixed-group body** (settings page, compact feed item descriptions, card listing several short blocks) | `sys.typo.body.sm` (14px) |
|
|
90
|
+
| **Primary button label / tab label** | `sys.typo.label.lg` (16px) |
|
|
91
|
+
| **List-row primary label / chip label** | `sys.typo.label.md` (14px) |
|
|
92
|
+
| **Meta line / supporting label / counter** | `sys.typo.label.sm` or `sys.typo.label.sm` (12px) |
|
|
93
|
+
| **Article / figure caption / footnote** | `sys.typo.label.sm` (12px) |
|
|
94
|
+
|
|
95
|
+
**Body-size rule of thumb**: single-topic page → `body.md`. Second peer text group joins → drop to `body.sm`.
|
|
96
|
+
|
|
97
|
+
## Type ramp picker — by component slot
|
|
98
|
+
|
|
99
|
+
When composing a specific component, this table is more specific than the intent table above — pick the exact slot rather than reasoning about intent.
|
|
100
|
+
|
|
101
|
+
| Component slot | Ramp | Weight |
|
|
102
|
+
| --- | --- | --- |
|
|
103
|
+
| `navigation-bar/main.title` (page-level wordmark) | `sys.typo.heading.lg` | 600 Semibold |
|
|
104
|
+
| `navigation-bar/sub.title` (centered title in drill-in) | `sys.typo.heading.md` | 600 Semibold |
|
|
105
|
+
| `section.label` (Section header) | `sys.typo.label.lg` | 600 Semibold |
|
|
106
|
+
| `section.headerAction` (See all link) | `sys.typo.label.md` | 500 Medium |
|
|
107
|
+
| `feed/post.title` (post headline) | `sys.typo.heading.md` | 600 Semibold |
|
|
108
|
+
| `feed/post.body` (post body preview, 2-line clamp) | `sys.typo.body.md` | 400 Regular |
|
|
109
|
+
| `feed/post.meta` (channel · time) | `sys.typo.label.sm` | 400 Regular |
|
|
110
|
+
| `feed/post.author.name` | `sys.typo.label.md` | 600 Semibold |
|
|
111
|
+
| `feed/post.engagement.count` (likes / comments / views) | `sys.typo.label.sm` | 400 Regular |
|
|
112
|
+
| `list/standard.primary` (row primary label) | `sys.typo.body.md` | 400 Regular |
|
|
113
|
+
| `list/standard.supporting` (row supporting text) | `sys.typo.body.sm` | 400 Regular |
|
|
114
|
+
| `button/standard.label` | `sys.typo.label.lg` | 600 Semibold |
|
|
115
|
+
| `button/text.label` | `sys.typo.label.md` | 500 Medium |
|
|
116
|
+
| `chip.label` (filter / tag) | `sys.typo.label.md` | 500 Medium |
|
|
117
|
+
| `tab/underline.label` (top-tab label) | `sys.typo.label.md` | 600 Semibold (active) / 500 Medium (rest) |
|
|
118
|
+
| `tab-bar.item.label` (bottom-tab label) | `sys.typo.label.sm` | 500 Medium |
|
|
119
|
+
| `form-field.label` | `sys.typo.label.md` | 500 Medium |
|
|
120
|
+
| `form-field.input.value` | `sys.typo.body.md` | 400 Regular |
|
|
121
|
+
| `form-field.helperText` | `sys.typo.label.sm` | 400 Regular |
|
|
122
|
+
| `banner.title` | `sys.typo.label.lg` | 600 Semibold |
|
|
123
|
+
| `banner.body` | `sys.typo.body.sm` | 400 Regular |
|
|
124
|
+
| `toast.body` | `sys.typo.label.md` | 500 Medium |
|
|
125
|
+
| `dialog.title` | `sys.typo.heading.sm` | 600 Semibold |
|
|
126
|
+
| `dialog.body` | `sys.typo.body.md` | 400 Regular |
|
|
127
|
+
| `badge.label` (numeric / text) | `sys.typo.label.sm` | 600 Semibold |
|
|
128
|
+
| `suggestion-list.row.name` | `sys.typo.label.md` | 600 Semibold |
|
|
129
|
+
| `suggestion-list.row.followers` | `sys.typo.label.sm` | 400 Regular |
|
|
130
|
+
|
|
131
|
+
**Avoid the under-12px trap.** Agents often default to 11-13px for "compact" copy — that breaks Korean / CJK hierarchy. When unsure, take the next-larger rung. The smallest rung for *visible* copy is 12px (`label.sm` / `label.sm`); below is reserved for legal / aux.
|
|
132
|
+
|
|
133
|
+
---
|
|
134
|
+
|
|
135
|
+
## Composition guard rails (hard one-liners)
|
|
136
|
+
|
|
137
|
+
DESIGN.md rules condensed to a single line each. Read as **immediate-reject** triggers when reviewing your own output.
|
|
138
|
+
|
|
139
|
+
1. **Brand red is an accent marker, never a surface.** No `navigation-bar` chrome paints brand. No banner background paints brand (use `primaryContainer` for info, `surfaceContainerLow` / `secondaryContainer` for promotional). Brand instances per screen ≤ 3 — canonically: Create tab item (1), feed active-like (≤2), optional promotional banner accent (1). See [`tokens.usage.json#sys.color.brand`](tokens.usage.json).
|
|
140
|
+
2. **Card outlines: `outlineVariant` hairline as inset shadow** (or `::after` overlay when the card hosts a full-bleed child). **Never `border:` on a card.** A `border` reflows the box; an inset shadow / overlay does not. The reflow is the bug.
|
|
141
|
+
3. **List rows: only `outlineVariant` divider between rows.** No per-row `border`. The list owns the seam, the row owns the click target.
|
|
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
|
+
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
|
+
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 (Header — both `<Header>` and `<SubHeader>` — List, Feed, FeedAd, Carousel, AvatarRail, Divider, DirectoryList, NavList, SuggestionList, ProfileHeader, Chip group, NavigationBar, 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. See `family.json#layoutInset` — the authoritative per-family value.
|
|
146
|
+
8. **Nesting tightens, never widens.** Parent `container.md` → child `container.sm` → grandchild `container.xs`. Inverting reads as compression, not hierarchy.
|
|
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
|
+
10. **FAB count ≤ 1 per screen.** Create is the single canonical commit — additional FABs dilute the affordance.
|
|
149
|
+
|
|
150
|
+
---
|
|
151
|
+
|
|
152
|
+
## Radius picker
|
|
153
|
+
|
|
154
|
+
| Shape | Pick |
|
|
155
|
+
| --- | --- |
|
|
156
|
+
| **Card / banner / dialog body / Feed item / Section card** | `sys.radius.md` (8px) |
|
|
157
|
+
| **BottomSheet top corners / toast pill / large dialog** | `sys.radius.lg` (12px) |
|
|
158
|
+
| **Poll option row / list radio / tag pill** | `sys.radius.sm` (4px) |
|
|
159
|
+
| **Chip outer / pill button / circular Thumbnail / avatar** | `sys.radius.full` |
|
|
160
|
+
| **Hero surface** | `sys.radius.xl` / `2xl` |
|
|
161
|
+
|
|
162
|
+
---
|
|
163
|
+
|
|
164
|
+
## Stroke + elevation picker
|
|
165
|
+
|
|
166
|
+
| Slot | Pick |
|
|
167
|
+
| --- | --- |
|
|
168
|
+
| **Card outline / list-row divider / form-field rest stroke** | `sys.borderWidth.hairline` (1px) × `sys.color.outlineVariant` |
|
|
169
|
+
| **Form-field active / focus ring outer** | `sys.borderWidth.thin` (2px) × `sys.color.outline` or `sys.color.focus` |
|
|
170
|
+
| **Cards at rest / hovered list rows** | `sys.elevation.raised` |
|
|
171
|
+
| **FAB / floating menu / dropdown** | `sys.elevation.floating` |
|
|
172
|
+
| **Dialog / modal / popover above scrim** | `sys.elevation.overlay` |
|
|
173
|
+
| **BottomSheet** | `sys.elevation.sheet` (shadow projects upward) |
|
|
174
|
+
|
|
175
|
+
---
|
|
176
|
+
|
|
177
|
+
## Quick decision flow when composing a new surface
|
|
178
|
+
|
|
179
|
+
1. **What lives on it?**
|
|
180
|
+
- Authored content (post body, article) → Feed / Section
|
|
181
|
+
- Menu / settings / picker rows → List
|
|
182
|
+
- Editorial collection → Section + carousel sub
|
|
183
|
+
- Page-level chrome → NavigationBar / TabBar
|
|
184
|
+
2. **What's the surface fill?** Pick from the [color quartet picker](#color-quartet-picker) by intent.
|
|
185
|
+
3. **What's the interior padding?** Default `container.md`; tighten one rung per level of nesting.
|
|
186
|
+
4. **What's the vertical rhythm to siblings?** Default `stack.md`; tighten to `stack.xs` for one-bound-group; widen to `stack.lg` for distinct groups.
|
|
187
|
+
5. **What's the corner radius?** Default `radius.md`; pill / chip → `radius.full`; large sheet → `radius.lg`.
|
|
188
|
+
6. **Edge stroke (if any)?** `borderWidth.hairline × outlineVariant`. If the surface hosts an opaque full-bleed child (cover image, hero), promote the outline to a `::after` overlay layer (DESIGN.md § Border & Stroke).
|
|
189
|
+
|
|
190
|
+
If a step has no good match — that's a **Chorus gap**, not a license to invent. Flag in one line and stop.
|
|
191
|
+
|
|
192
|
+
---
|
|
193
|
+
|
|
194
|
+
## When you go custom (no Chorus family fits)
|
|
195
|
+
|
|
196
|
+
The LEGO ladder didn't surface a fit and you've genuinely exhausted visual-reuse on the 13 `"open"` families — 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
|
+
|
|
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
|
+
|
|
200
|
+
### Raw → token map for custom surfaces
|
|
201
|
+
|
|
202
|
+
| You typed | Likely intent | Pick |
|
|
203
|
+
| --- | --- | --- |
|
|
204
|
+
| `fontSize: 13` | compact body / meta line | `className="sys-typo-body-md"` (16) or `sys-typo-label-md` (14). 13 is off-scale — pick the next rung. |
|
|
205
|
+
| `fontSize: 14` | strong label / row primary | `className="sys-typo-label-lg"` (16, 600 — strong) or `sys-typo-label-md` (14, 500 — quiet) |
|
|
206
|
+
| `fontSize: 11` or `10` | "tiny" caption | **Forbidden for visible copy.** Floor is `sys.typo.label.sm` (12) — see [anti-patterns.md #7](anti-patterns.md). |
|
|
207
|
+
| `fontWeight: 600` (set alone) | bold label | `className="sys-typo-*"` — every typo rung bundles its canonical weight. Don't set weight separately. |
|
|
208
|
+
| `lineHeight: 1.4` | "comfortable" line height | **Don't set it.** Typo tokens already carry line-height; setting it overrides the rung's contract. |
|
|
209
|
+
| `gap: 6` | tight inline cluster | `gap: var(--sys-layout-inline-xs)` (4) or `var(--sys-layout-inline-sm)` (8) — pick a rung, not halfway. |
|
|
210
|
+
| `gap: 10` | medium horizontal cluster | `var(--sys-layout-inline-sm)` (8) or `var(--sys-layout-inline-md)` (12) |
|
|
211
|
+
| `gap: 6` (vertical between siblings) | tight vertical stack | `gap: var(--sys-layout-stack-2xs)` (4) or `stack.xs` (8). Vertical uses `stack.*`, not `inline.*`. |
|
|
212
|
+
| `padding: "10px 12px"` | dense surface interior | `var(--sys-layout-container-xs) var(--sys-layout-container-sm)` (8 12). Two rung tokens on one line is fine. |
|
|
213
|
+
| `padding: 16` | default surface interior | `var(--sys-layout-container-md)` |
|
|
214
|
+
| `paddingInline: 16` (page level) | shell gutter | `var(--sys-layout-page-md)` — paid once at the shell, never on a full-bleed child. |
|
|
215
|
+
| `borderRadius: 6` | small radius | `var(--sys-radius-sm)` (4) or `var(--sys-radius-md)` (8) — no in-between. |
|
|
216
|
+
| `borderRadius: 10` | medium radius | `var(--sys-radius-md)` (8) or `var(--sys-radius-lg)` (12) |
|
|
217
|
+
| `borderRadius: 999` on a chip-shaped surface | pill | `var(--sys-radius-full)` |
|
|
218
|
+
| `border: "1px solid #..."` | hairline outline | `box-shadow: inset 0 0 0 var(--sys-borderWidth-hairline) var(--sys-color-outlineVariant)` (no-layout stroke — `border:` reflows the box; see [anti-patterns.md #2](anti-patterns.md)). |
|
|
219
|
+
| `background: "#fff"` / `"#FFF"` | page surface | `var(--sys-color-surface)` |
|
|
220
|
+
| `color: "#1A1A1A"` / `"#333"` | body text | `var(--sys-color-onSurface)` (primary) / `var(--sys-color-onSurfaceVariant)` (meta) |
|
|
221
|
+
|
|
222
|
+
> **Typography is a class, not a `font` shorthand.** A type rung is five properties (family + size + weight + line-height + tracking). There is **no `font: var(--sys-typo-*)` token** — and the CSS `font` shorthand can't carry letter-spacing anyway, so that declaration voids and the text falls back to a system font. Apply the bundled utility class instead: `className="sys-typo-body-md"` (roles `display`/`heading`/`body`/`label` × rungs `lg`/`md`/`sm`, plus the rung-less `sys-typo-caption`). If a class genuinely can't be attached, set all four `var(--sys-typo-<role>-<rung>-{size,weight,line,tracking})` vars — dropping one drifts the rung. Never set `lineHeight`/`fontWeight` separately alongside.
|
|
223
|
+
|
|
224
|
+
### The three authorized literal exceptions (per `DESIGN.md`)
|
|
225
|
+
|
|
226
|
+
Anything outside these is a violation, even on a custom surface:
|
|
227
|
+
|
|
228
|
+
1. **Intrinsic geometry** naming component anatomy — Thumbnail rung `48px`, Tooltip `min-height: 32px`, icon `16px` (slot contract, not layout).
|
|
229
|
+
2. **Computed compositions** combining tokens in `calc()` — `calc(48px + var(--sys-layout-inline-lg))` to anchor a divider to an avatar's trailing edge.
|
|
230
|
+
3. **Structural `0` / `100%` / `auto`** — no axis to resolve.
|
|
231
|
+
|
|
232
|
+
### What to do when no rung fits
|
|
233
|
+
|
|
234
|
+
A "Chorus gap" report — one line, stop. Example: *"Building `<HintCard>` — `sys.layout.inline.xs` (4) reads too tight for the leading icon ↔ body gap, `inline.sm` (8) too loose. Proposing a new `inline.2xs` (6) rung or accept the `inline.sm` reading. Not inlining `gap: 6`."* Then wait. **Never** inline as a workaround.
|
|
235
|
+
|
|
236
|
+
### Mental check before shipping a custom surface
|
|
237
|
+
|
|
238
|
+
> *"Scan every numeric literal in my style/className. For each: is it (a) a token call, (b) one of the three exceptions, or (c) a violation? If (c) — substitute the rung above, or file a Chorus gap. No fourth option."*
|
|
239
|
+
|
|
240
|
+
Pair with the [§E pre-flight custom-primitive checkbox](LOVABLE.md) before declaring done.
|