@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/dist/styles.css
CHANGED
|
@@ -222,6 +222,65 @@ textarea {
|
|
|
222
222
|
opacity: 0;
|
|
223
223
|
}
|
|
224
224
|
|
|
225
|
+
/* ------------------------------------------------------------
|
|
226
|
+
ButtonGroup — Buttons composed as one block. The row carries the
|
|
227
|
+
family's 8px (`sys.layout.inline.md`) gap; an optional label stacks
|
|
228
|
+
above it. The docked form adds the footer-bar chrome (surface fill,
|
|
229
|
+
16px inset, upward `sys.elevation.sheet` shadow). See ButtonGroup.jsx
|
|
230
|
+
and schema/components/button/standard.md.
|
|
231
|
+
------------------------------------------------------------ */
|
|
232
|
+
.chorus-button-group {
|
|
233
|
+
display: flex;
|
|
234
|
+
flex-direction: column;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
.chorus-button-group__row {
|
|
238
|
+
display: flex;
|
|
239
|
+
gap: var(--sys-layout-inline-md);
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
/* Vertical inline group — full-width Buttons stacked (primary over
|
|
243
|
+
secondary). Same 8px gap, now on the block axis. */
|
|
244
|
+
.chorus-button-group--vertical .chorus-button-group__row {
|
|
245
|
+
flex-direction: column;
|
|
246
|
+
align-items: stretch;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
/* Optional caption above the row — `sys.typo.body.md` (16px) in the muted
|
|
250
|
+
on-surface tone, centered, 16px (`sys.layout.stack.md`) above the row. An
|
|
251
|
+
inline <strong> reads as the emphasized value in the full-strength tone. */
|
|
252
|
+
.chorus-button-group__label {
|
|
253
|
+
text-align: center;
|
|
254
|
+
margin-bottom: var(--sys-layout-stack-md);
|
|
255
|
+
color: var(--sys-color-onSurfaceVariant);
|
|
256
|
+
font-size: var(--sys-typo-body-md-size);
|
|
257
|
+
font-weight: var(--sys-typo-body-md-weight);
|
|
258
|
+
line-height: var(--sys-typo-body-md-line);
|
|
259
|
+
letter-spacing: var(--sys-typo-body-md-tracking);
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
.chorus-button-group__label strong {
|
|
263
|
+
color: var(--sys-color-onSurface);
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
/* Docked footer bar — full-bleed surface pinned to the bottom of the app.
|
|
267
|
+
No top stroke; the upward `sys.elevation.sheet` shadow (same one
|
|
268
|
+
BottomSheet / SideSheet cast) lifts it off the scrolling body so content
|
|
269
|
+
passing behind reads as a separate region. Renders in flow — PageShell
|
|
270
|
+
owns the pin, so the bar must NOT set position: sticky/fixed itself. */
|
|
271
|
+
.chorus-button-group--docked {
|
|
272
|
+
background: var(--sys-color-surface);
|
|
273
|
+
padding: var(--sys-layout-container-md);
|
|
274
|
+
box-shadow: var(--sys-elevation-sheet);
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
/* In the docked bar the two Buttons split the row equally, so the consumer
|
|
278
|
+
drops in two `<Button size="large">` without wiring `fullWidth`. */
|
|
279
|
+
.chorus-button-group--docked .chorus-button-group__row > * {
|
|
280
|
+
flex: 1 1 0;
|
|
281
|
+
min-width: 0;
|
|
282
|
+
}
|
|
283
|
+
|
|
225
284
|
/* ============================================================
|
|
226
285
|
FAB
|
|
227
286
|
============================================================ */
|
|
@@ -1057,6 +1116,8 @@ a.chorus-metadata__name:focus-visible {
|
|
|
1057
1116
|
timestamp. Single text line — the row never wraps; identity links
|
|
1058
1117
|
truncate before the timestamp gives way. */
|
|
1059
1118
|
.chorus-metadata--compact .chorus-metadata__meta {
|
|
1119
|
+
flex: 1 1 auto;
|
|
1120
|
+
min-width: 0;
|
|
1060
1121
|
flex-wrap: nowrap;
|
|
1061
1122
|
white-space: nowrap;
|
|
1062
1123
|
}
|
|
@@ -1074,6 +1135,9 @@ a.chorus-metadata__name:focus-visible {
|
|
|
1074
1135
|
|
|
1075
1136
|
.chorus-metadata--compact .chorus-metadata__timestamp {
|
|
1076
1137
|
flex: 0 0 auto;
|
|
1138
|
+
/* No leading middot — pay the name↔time gap directly (matches the
|
|
1139
|
+
Standard head's 8px primary-line gap). */
|
|
1140
|
+
margin-inline-start: var(--sys-layout-inline-md);
|
|
1077
1141
|
}
|
|
1078
1142
|
|
|
1079
1143
|
/* ============================================================
|
|
@@ -1248,7 +1312,7 @@ a.chorus-metadata__name:focus-visible {
|
|
|
1248
1312
|
gap: var(--sys-layout-inline-sm);
|
|
1249
1313
|
}
|
|
1250
1314
|
.chorus-feed__poll-glyph { color: var(--sys-color-brand); display: inline-flex; }
|
|
1251
|
-
.chorus-feed__poll-label { color: var(--sys-color-brand); font-weight:
|
|
1315
|
+
.chorus-feed__poll-label { color: var(--sys-color-brand); font-weight: var(--ref-fontWeight-bold); }
|
|
1252
1316
|
|
|
1253
1317
|
/* Offer-evaluation tone — same chrome as the poll banner, but the
|
|
1254
1318
|
leading glyph and label paint in `sys.color.success` (resolves to
|
|
@@ -1266,7 +1330,7 @@ a.chorus-metadata__name:focus-visible {
|
|
|
1266
1330
|
background: var(--sys-color-outlineVariant);
|
|
1267
1331
|
}
|
|
1268
1332
|
.chorus-feed__poll-participants { color: var(--sys-color-onSurface); }
|
|
1269
|
-
.chorus-feed__poll-participants strong { font-weight:
|
|
1333
|
+
.chorus-feed__poll-participants strong { font-weight: var(--ref-fontWeight-bold); }
|
|
1270
1334
|
|
|
1271
1335
|
.chorus-feed__citation {
|
|
1272
1336
|
/* Citation is one bordered surface: the hero and the text column sit
|
|
@@ -1831,23 +1895,12 @@ a.chorus-metadata__name:focus-visible {
|
|
|
1831
1895
|
color: currentColor;
|
|
1832
1896
|
}
|
|
1833
1897
|
|
|
1834
|
-
|
|
1835
|
-
|
|
1836
|
-
|
|
1837
|
-
|
|
1838
|
-
|
|
1839
|
-
|
|
1840
|
-
|
|
1841
|
-
.chorus-post-carousel__dot {
|
|
1842
|
-
width: var(--ref-space-75);
|
|
1843
|
-
height: var(--ref-space-75);
|
|
1844
|
-
border-radius: var(--sys-radius-full);
|
|
1845
|
-
background: var(--sys-color-outlineVariant);
|
|
1846
|
-
display: block;
|
|
1847
|
-
}
|
|
1848
|
-
|
|
1849
|
-
.chorus-post-carousel__dot--active {
|
|
1850
|
-
background: var(--sys-color-onSurface);
|
|
1898
|
+
/* Pagination dots below the pager are the shared Pagination component
|
|
1899
|
+
(.chorus-pagination) — no carousel-local dot rules. The component is
|
|
1900
|
+
an intrinsic-width inline element, so the carousel (column flex,
|
|
1901
|
+
align-items stretch) centers it itself. */
|
|
1902
|
+
.chorus-post-carousel > .chorus-pagination {
|
|
1903
|
+
align-self: center;
|
|
1851
1904
|
}
|
|
1852
1905
|
|
|
1853
1906
|
/* ============================================================
|
|
@@ -2078,23 +2131,12 @@ a.chorus-metadata__name:focus-visible {
|
|
|
2078
2131
|
justify-content: center;
|
|
2079
2132
|
}
|
|
2080
2133
|
|
|
2081
|
-
|
|
2082
|
-
|
|
2083
|
-
|
|
2084
|
-
|
|
2085
|
-
|
|
2086
|
-
|
|
2087
|
-
|
|
2088
|
-
.chorus-profile-carousel__dot {
|
|
2089
|
-
width: var(--ref-space-75);
|
|
2090
|
-
height: var(--ref-space-75);
|
|
2091
|
-
border-radius: var(--sys-radius-full);
|
|
2092
|
-
background: var(--sys-color-outlineVariant);
|
|
2093
|
-
display: block;
|
|
2094
|
-
}
|
|
2095
|
-
|
|
2096
|
-
.chorus-profile-carousel__dot--active {
|
|
2097
|
-
background: var(--sys-color-onSurface);
|
|
2134
|
+
/* Pagination dots below the pager are the shared Pagination component
|
|
2135
|
+
(.chorus-pagination) — no carousel-local dot rules. The component is
|
|
2136
|
+
an intrinsic-width inline element, so the carousel (column flex,
|
|
2137
|
+
align-items stretch) centers it itself. */
|
|
2138
|
+
.chorus-profile-carousel > .chorus-pagination {
|
|
2139
|
+
align-self: center;
|
|
2098
2140
|
}
|
|
2099
2141
|
|
|
2100
2142
|
/* ============================================================
|
|
@@ -2437,11 +2479,20 @@ a.chorus-metadata__name:focus-visible {
|
|
|
2437
2479
|
pointer-events: none;
|
|
2438
2480
|
}
|
|
2439
2481
|
|
|
2440
|
-
|
|
2482
|
+
/* Nested-action scope — a trailing slot carrying an independent action
|
|
2483
|
+
(a Follow / mute / favorite button, an overflow control) is marked
|
|
2484
|
+
`data-nested-action` and already stops click/key propagation so it never
|
|
2485
|
+
commits the row's primary action. The row's hover / press overlay is
|
|
2486
|
+
suppressed while the pointer sits on that action so the large row does
|
|
2487
|
+
NOT read as hovered / pressed at the same time the small control does —
|
|
2488
|
+
the visual state boundary matches the event boundary. The decorative
|
|
2489
|
+
nav chevron carries no `data-nested-action`, so hovering it still lights
|
|
2490
|
+
the row (it IS the row's drill-in affordance). */
|
|
2491
|
+
.chorus-list__row:hover:not(:has([data-nested-action]:hover)) {
|
|
2441
2492
|
background: color-mix(in srgb, var(--sys-color-onSurface) calc(var(--sys-state-hover) * 100%), transparent);
|
|
2442
2493
|
}
|
|
2443
2494
|
|
|
2444
|
-
.chorus-list__row:active {
|
|
2495
|
+
.chorus-list__row:active:not(:has([data-nested-action]:active)) {
|
|
2445
2496
|
background: color-mix(in srgb, var(--sys-color-onSurface) calc(var(--sys-state-pressed) * 100%), transparent);
|
|
2446
2497
|
}
|
|
2447
2498
|
|
|
@@ -3241,6 +3292,7 @@ a.chorus-metadata__name:focus-visible {
|
|
|
3241
3292
|
.chorus-banner--accent {
|
|
3242
3293
|
background: var(--sys-color-primaryContainer);
|
|
3243
3294
|
color: var(--sys-color-onPrimaryContainer);
|
|
3295
|
+
--banner-outline-color: color-mix(in srgb, var(--sys-color-primary) 40%, transparent);
|
|
3244
3296
|
}
|
|
3245
3297
|
|
|
3246
3298
|
/* Destructive — error-tinted banner. Reach for it when the aside is a
|
|
@@ -3251,6 +3303,7 @@ a.chorus-metadata__name:focus-visible {
|
|
|
3251
3303
|
.chorus-banner--destructive {
|
|
3252
3304
|
background: var(--sys-color-errorContainer);
|
|
3253
3305
|
color: var(--sys-color-onErrorContainer);
|
|
3306
|
+
--banner-outline-color: color-mix(in srgb, var(--sys-color-error) 40%, transparent);
|
|
3254
3307
|
}
|
|
3255
3308
|
|
|
3256
3309
|
/* Default uses `sys.color.scrimSubtle` (~8% inverse-tone overlay —
|
|
@@ -3264,6 +3317,21 @@ a.chorus-metadata__name:focus-visible {
|
|
|
3264
3317
|
.chorus-banner--default {
|
|
3265
3318
|
background: var(--sys-color-scrimSubtle);
|
|
3266
3319
|
color: var(--sys-color-onSurface);
|
|
3320
|
+
--banner-outline-color: var(--sys-color-outlineVariant);
|
|
3321
|
+
}
|
|
3322
|
+
|
|
3323
|
+
/* Optional outline — a hairline inset stroke toned to the appearance's
|
|
3324
|
+
color family (each appearance block above sets
|
|
3325
|
+
`--banner-outline-color`), kept deliberately faint so the edge reads
|
|
3326
|
+
as a soft boundary of the same tint, not a frame: the subtle gray
|
|
3327
|
+
hairline (`sys.color.outlineVariant`) on default's gray-tinted
|
|
3328
|
+
scrim; `primary` / `error` at 40% over transparent (the Skeleton
|
|
3329
|
+
fill recipe) on accent / destructive. Painted as an inset
|
|
3330
|
+
box-shadow, never a `border`, so toggling it can't change the
|
|
3331
|
+
banner's footprint — same idiom as Chip (see DESIGN.md → Border &
|
|
3332
|
+
Stroke). */
|
|
3333
|
+
.chorus-banner--outlined {
|
|
3334
|
+
box-shadow: inset 0 0 0 var(--sys-borderWidth-hairline) var(--banner-outline-color);
|
|
3267
3335
|
}
|
|
3268
3336
|
|
|
3269
3337
|
/* Leading icon slot — a 16 × 16 glyph (`sys.icon.md`) that paints in
|
|
@@ -3306,6 +3374,23 @@ a.chorus-metadata__name:focus-visible {
|
|
|
3306
3374
|
gap: var(--sys-layout-stack-xs);
|
|
3307
3375
|
}
|
|
3308
3376
|
|
|
3377
|
+
/* Optional heading line above the body — label.md (14 / Semibold) in
|
|
3378
|
+
the container's foreground. Title↔body wants the tighter
|
|
3379
|
+
`sys.layout.stack.2xs` (4) so the pair reads as one passage, while
|
|
3380
|
+
the content column's gap stays `stack.xs` (8) for body↔action; the
|
|
3381
|
+
negative margin nets the column gap down to 4 for this pair only. */
|
|
3382
|
+
.chorus-banner__title {
|
|
3383
|
+
margin: 0;
|
|
3384
|
+
font-size: var(--sys-typo-label-md-size);
|
|
3385
|
+
line-height: var(--sys-typo-label-md-line);
|
|
3386
|
+
font-weight: var(--sys-typo-label-md-weight);
|
|
3387
|
+
color: inherit;
|
|
3388
|
+
}
|
|
3389
|
+
|
|
3390
|
+
.chorus-banner__title + .chorus-banner__body {
|
|
3391
|
+
margin-top: calc(var(--sys-layout-stack-2xs) - var(--sys-layout-stack-xs));
|
|
3392
|
+
}
|
|
3393
|
+
|
|
3309
3394
|
.chorus-banner__body {
|
|
3310
3395
|
margin: 0;
|
|
3311
3396
|
font-size: var(--sys-typo-body-sm-size);
|
|
@@ -3332,6 +3417,28 @@ a.chorus-metadata__name:focus-visible {
|
|
|
3332
3417
|
color: var(--sys-color-primary);
|
|
3333
3418
|
}
|
|
3334
3419
|
|
|
3420
|
+
/* Trailing icon slot — a 16 × 16 glyph (`sys.icon.md`) at the trailing
|
|
3421
|
+
edge, vertically centered against the whole block (`align-self:
|
|
3422
|
+
center` overrides the container's flex-start). Paints in the
|
|
3423
|
+
banner's foreground (`currentColor`); typically a forward affordance
|
|
3424
|
+
(e.g. ForwardCircleFillIcon) signaling the aside leads somewhere. */
|
|
3425
|
+
.chorus-banner__trailing-icon {
|
|
3426
|
+
flex: 0 0 var(--sys-icon-md);
|
|
3427
|
+
align-self: center;
|
|
3428
|
+
display: inline-flex;
|
|
3429
|
+
align-items: center;
|
|
3430
|
+
justify-content: center;
|
|
3431
|
+
color: currentColor;
|
|
3432
|
+
}
|
|
3433
|
+
|
|
3434
|
+
.chorus-banner__trailing-icon img,
|
|
3435
|
+
.chorus-banner__trailing-icon svg {
|
|
3436
|
+
width: var(--sys-icon-md);
|
|
3437
|
+
height: var(--sys-icon-md);
|
|
3438
|
+
display: block;
|
|
3439
|
+
color: currentColor;
|
|
3440
|
+
}
|
|
3441
|
+
|
|
3335
3442
|
/* ============================================================
|
|
3336
3443
|
Divider — section-break band between adjacent regions that
|
|
3337
3444
|
don't share an enclosing container. Single full-bleed block
|
|
@@ -3715,14 +3822,21 @@ a.chorus-metadata__name:focus-visible {
|
|
|
3715
3822
|
/* Hover / pressed re-tone the 1px stroke to `borderHover` (`outline`,
|
|
3716
3823
|
or `error` on the error appearance). `:active` also lights the
|
|
3717
3824
|
state-overlay `::before`. */
|
|
3718
|
-
|
|
3825
|
+
/* The trailing clear button is an independent nested action (it wipes the
|
|
3826
|
+
value, distinct from focusing / pressing the field) — marked
|
|
3827
|
+
`data-nested-action`. While the pointer hovers / presses it, the field's
|
|
3828
|
+
own hover stroke and pressed overlay are suppressed so the large field
|
|
3829
|
+
does NOT read as pressed at the same time the small "×" does. The Select
|
|
3830
|
+
chevron is NOT marked: it fires the same action as clicking the field
|
|
3831
|
+
(open), so it correctly lights the field. */
|
|
3719
3832
|
.chorus-field[data-force-state="hovered"],
|
|
3720
3833
|
.chorus-field[data-force-state="pressed"],
|
|
3721
|
-
.chorus-field:not(
|
|
3834
|
+
.chorus-field:hover:not(:has([data-nested-action]:hover)),
|
|
3835
|
+
.chorus-field:not(.is-disabled):active:not(:has([data-nested-action]:active)) {
|
|
3722
3836
|
box-shadow: inset 0 0 0 var(--field-border-width) var(--field-border-hover);
|
|
3723
3837
|
}
|
|
3724
3838
|
|
|
3725
|
-
.chorus-field:not(.is-disabled):active::before,
|
|
3839
|
+
.chorus-field:not(.is-disabled):active:not(:has([data-nested-action]:active))::before,
|
|
3726
3840
|
.chorus-field[data-force-state="pressed"]::before {
|
|
3727
3841
|
opacity: var(--field-overlay-pressed);
|
|
3728
3842
|
}
|
|
@@ -5225,6 +5339,34 @@ a.chorus-metadata__name:focus-visible {
|
|
|
5225
5339
|
transform: rotate(45deg);
|
|
5226
5340
|
}
|
|
5227
5341
|
|
|
5342
|
+
/* ============================================================
|
|
5343
|
+
Pagination — decorative dot-position indicator
|
|
5344
|
+
============================================================ */
|
|
5345
|
+
/* One 6px dot per page in an inline.sm row. An inline element —
|
|
5346
|
+
inline-flex, intrinsic width (no stretch, no self-centering); the
|
|
5347
|
+
host owns horizontal placement. Active dot paints `onSurface`, the
|
|
5348
|
+
rest `outlineVariant`. Non-interactive (`aria-hidden` on the root) —
|
|
5349
|
+
the host pager owns the active index and keyboard reach.
|
|
5350
|
+
See schema/components/pagination/pagination.md. */
|
|
5351
|
+
|
|
5352
|
+
.chorus-pagination {
|
|
5353
|
+
display: inline-flex;
|
|
5354
|
+
align-items: center;
|
|
5355
|
+
gap: var(--sys-layout-inline-sm);
|
|
5356
|
+
}
|
|
5357
|
+
|
|
5358
|
+
.chorus-pagination__dot {
|
|
5359
|
+
width: var(--ref-space-75);
|
|
5360
|
+
height: var(--ref-space-75);
|
|
5361
|
+
border-radius: var(--sys-radius-full);
|
|
5362
|
+
background: var(--sys-color-outlineVariant);
|
|
5363
|
+
display: block;
|
|
5364
|
+
}
|
|
5365
|
+
|
|
5366
|
+
.chorus-pagination__dot--active {
|
|
5367
|
+
background: var(--sys-color-onSurface);
|
|
5368
|
+
}
|
|
5369
|
+
|
|
5228
5370
|
/* ============================================================
|
|
5229
5371
|
Progress — linear progress bar (determinate)
|
|
5230
5372
|
============================================================ */
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@teamblind-chorus/ui",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"description": "Chorus React components. Ships prebuilt ESM + CJS bundles (`dist/`) and a single `styles.css`; import `@teamblind-chorus/tokens/tokens.css` + `@teamblind-chorus/ui/styles.css` once at the app entry. The contract every component honors lives in schema/components/<family>/<sub>.spec.json; see schema/manifest.json for the inventory.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Teamblind, Inc.",
|
|
@@ -54,7 +54,6 @@
|
|
|
54
54
|
"./agents/usage.json": "./agents/usage.json",
|
|
55
55
|
"./agents/DESIGN.md": "./agents/DESIGN.md",
|
|
56
56
|
"./agents/LOVABLE.md": "./agents/LOVABLE.md",
|
|
57
|
-
"./agents/reconstruct.md": "./agents/reconstruct.md",
|
|
58
57
|
"./agents/images.md": "./agents/images.md",
|
|
59
58
|
"./agents/patterns/": "./agents/patterns/",
|
|
60
59
|
"./agents/components/": "./agents/components/",
|
|
@@ -83,7 +82,7 @@
|
|
|
83
82
|
"react-dom": ">=18"
|
|
84
83
|
},
|
|
85
84
|
"dependencies": {
|
|
86
|
-
"@teamblind-chorus/tokens": "^1.0.
|
|
85
|
+
"@teamblind-chorus/tokens": "^1.0.1"
|
|
87
86
|
},
|
|
88
87
|
"publishConfig": {
|
|
89
88
|
"access": "public"
|
package/agents/reconstruct.md
DELETED
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
# Chorus reconstruction prompt
|
|
2
|
-
|
|
3
|
-
Paste the block below into a Chorus-aware agent (Lovable etc.) to rebuild ad-hoc,
|
|
4
|
-
agent-invented UI as pure Chorus. Strongest with `LOVABLE.md` loaded as the system
|
|
5
|
-
prompt — this block is a thin **driver**; the full rules live in `LOVABLE.md` and the
|
|
6
|
-
guides it tells the agent to read, so the prompt stays short (lower token cost).
|
|
7
|
-
|
|
8
|
-
---
|
|
9
|
-
|
|
10
|
-
```text
|
|
11
|
-
Chorus reconstruction pass — rebuild every ad-hoc component as pure Chorus.
|
|
12
|
-
|
|
13
|
-
GOAL: every hand-built component (raw div/button, Tailwind, shadcn, inline styles, hex)
|
|
14
|
-
becomes pure Chorus. No mixed renders — a screen is 100% Chorus or not done. The old UI
|
|
15
|
-
is the source of a migration to @teamblind-chorus/ui + @teamblind-chorus/tokens; don't preserve or
|
|
16
|
-
"match" it — the old style is the bug.
|
|
17
|
-
|
|
18
|
-
FIRST read node_modules/@teamblind-chorus/ui/agents/: catalog.md, manifest.json, compose.md,
|
|
19
|
-
anti-patterns.md. The component name is not the contract — bindings are in
|
|
20
|
-
components/<family>/<sub>.spec.json.
|
|
21
|
-
|
|
22
|
-
DO, in order:
|
|
23
|
-
1. INVENTORY every non-Chorus component in src/ (one line each: file → what it is).
|
|
24
|
-
2. MAP each intent via catalog.md to a family + sub. If none fits, climb the ladder
|
|
25
|
-
(recompose slots → LEGO-combine → new primitive with every value a var(--sys-*)),
|
|
26
|
-
then flag a one-line "Chorus gap". Never fall back to raw HTML/Tailwind/shadcn.
|
|
27
|
-
3. REBUILD with the real import from @teamblind-chorus/ui (icons from @teamblind-chorus/ui/icons),
|
|
28
|
-
honoring the spec; then delete the old component + its CSS.
|
|
29
|
-
|
|
30
|
-
NON-NEGOTIABLE (else discard + regenerate):
|
|
31
|
-
- Tokens only: no hex, no Tailwind colors, no off-scale px; card edge = inset shadow,
|
|
32
|
-
never border:.
|
|
33
|
-
- Typography = className="sys-typo-<role>-<rung>" (NO `font: var(--sys-typo-*)` token).
|
|
34
|
-
- Compound children: <Tabs> needs <Tab>; List/SuggestionList/AvatarRail take an items
|
|
35
|
-
array — never bare text.
|
|
36
|
-
- One gutter at the shell; full-bleed children (Tabs/Feed/List/Carousel/bars) never
|
|
37
|
-
wrapped in a padded div. Floating action = <Button variant="fab">, not a pinned
|
|
38
|
-
standard button. Fixed bars via .page-shell (height:100dvh + <main> overflow-y:auto).
|
|
39
|
-
|
|
40
|
-
VERIFY: eslint.config.js extends @teamblind-chorus/ui/eslint — fix every chorus/* error (never
|
|
41
|
-
suppress); run the §E pre-flight checklist + rail self-diagnostic (anti-patterns.md).
|
|
42
|
-
|
|
43
|
-
SCOPE (optional): "SCOPE: <area/route globs>" limits the pass to that boundary —
|
|
44
|
-
inventory, rebuild, and lint only inside it; outside files are reported, never edited
|
|
45
|
-
(boundary rules: agents/scoped-adoption.md).
|
|
46
|
-
|
|
47
|
-
DELIVER: a before→after table (component → Chorus family/sub, or "gap") and screens with
|
|
48
|
-
zero mixed renders. If scope is large, reconstruct the entry screen first, list the rest.
|
|
49
|
-
```
|
|
50
|
-
|
|
51
|
-
---
|
|
52
|
-
|
|
53
|
-
The ESLint preset (`@teamblind-chorus/ui/eslint`, wired in the VERIFY step) is the safety net:
|
|
54
|
-
even if the model drops a rule mid-session, `chorus/*` errors fail the build and its own
|
|
55
|
-
fix loop catches the drift.
|
|
@@ -1,111 +0,0 @@
|
|
|
1
|
-
# Scoped adoption — Chorus in a designated area of an existing app
|
|
2
|
-
|
|
3
|
-
Protocol for the third first-turn branch in `LOVABLE.md`: the user wants Chorus in a
|
|
4
|
-
**specific area** of an in-progress project — not a full migration (§D), not a blank
|
|
5
|
-
scaffold (greenfield). Triggers: the first message names a target area / route /
|
|
6
|
-
feature ("apply Chorus to the settings tab", "the community feed uses Chorus"), or
|
|
7
|
-
says to initialize and **stand by** for an area they'll designate next.
|
|
8
|
-
|
|
9
|
-
Everything in `LOVABLE.md` still applies *inside* the designated area. This file only
|
|
10
|
-
defines what changes **at and outside the boundary**.
|
|
11
|
-
|
|
12
|
-
## First-turn behaviour
|
|
13
|
-
|
|
14
|
-
1. Run §A.0 end-to-end (install, stylesheets, placeholder, lint preset — scoped, see
|
|
15
|
-
below) and post the readiness line.
|
|
16
|
-
2. Post a drift report **scoped to the designated area** (same shape as §D.1 — counts
|
|
17
|
-
+ worst offenders — but only for files the area renders). If no area is named yet,
|
|
18
|
-
skip the report and say so in one line.
|
|
19
|
-
3. **Stand by.** Do NOT reconstruct the entry screen, do NOT post an app-wide
|
|
20
|
-
migration plan, do NOT touch files outside the area. The §D "reconstruct the
|
|
21
|
-
representative screen immediately" step is **suspended** in this mode — the
|
|
22
|
-
reconstruction target is the designated area, on the user's brief.
|
|
23
|
-
|
|
24
|
-
## The boundary — declare it, then respect it
|
|
25
|
-
|
|
26
|
-
* **Declare the Chorus boundary at route / screen granularity** whenever possible.
|
|
27
|
-
State it explicitly in your first scoped reply: *"Chorus boundary: `/community/*`
|
|
28
|
-
(CommunityPage + children)."* Everything inside renders 100% Chorus; everything
|
|
29
|
-
outside stays untouched — visually and in code.
|
|
30
|
-
* **The zero-mixed-render rule applies per screen *inside* the boundary.** A screen
|
|
31
|
-
inside the boundary is 100% Chorus or not done. Screens outside the boundary are
|
|
32
|
-
out of scope — their drift is *reported, never edited*.
|
|
33
|
-
* **Sub-screen areas.** If the designated area is a region within a screen that also
|
|
34
|
-
has legacy UI (e.g. "only the comment section"), first propose widening the boundary
|
|
35
|
-
to the whole screen — that's the only way to honor zero-mixed-render literally. If
|
|
36
|
-
the user declines, the area's **container element is the declared boundary**: inside
|
|
37
|
-
it 100% Chorus, outside it untouched, and you state the exception in one line
|
|
38
|
-
("mixed render sanctioned at `<CommentSection>` per scoped boundary"). Never
|
|
39
|
-
restyle legacy siblings "for consistency".
|
|
40
|
-
* **§D.4 neighbor migration is capped at the boundary.** "Touched files AND immediate
|
|
41
|
-
visual neighbors" never drags in files outside the declared area. Out-of-boundary
|
|
42
|
-
drift goes on the next-PR shopping list (§D.5), nothing more.
|
|
43
|
-
* **Widening is the user's call.** They can grow the boundary ("now also the profile
|
|
44
|
-
page") or switch to full §D at any time. You may *suggest* widening when the
|
|
45
|
-
boundary forces an awkward seam; you never widen unilaterally.
|
|
46
|
-
|
|
47
|
-
## Embedded host shell — who pays the gutter
|
|
48
|
-
|
|
49
|
-
The §A.4 PageShell contract assumes Chorus owns the viewport. In scoped adoption it
|
|
50
|
-
usually doesn't:
|
|
51
|
-
|
|
52
|
-
* **Area = whole route/screen** → use `<PageShell>` normally inside that route.
|
|
53
|
-
* **Area = region embedded in a legacy frame** → do NOT install `<PageShell>` (a
|
|
54
|
-
100dvh flex column inside a host layout breaks the host's scroll). Instead, treat
|
|
55
|
-
the **legacy container as the shell**: it pays the horizontal gutter **once**, and
|
|
56
|
-
Chorus full-bleed children (`List`, `Feed`, `Carousel`, `Tabs`, …) are its direct
|
|
57
|
-
children with no extra `padding-inline` / `px-*` wrapper.
|
|
58
|
-
* If the legacy container already pays its own padding, that *is* the single
|
|
59
|
-
gutter — do not re-pay it on a Chorus wrapper.
|
|
60
|
-
* If it pays none, add it once at the area root:
|
|
61
|
-
`style={{ paddingInline: 'var(--sys-layout-page-md)' }}`.
|
|
62
|
-
* The rail self-diagnostic (LOVABLE.md § rail) still applies *within the area*: every
|
|
63
|
-
full-bleed child's left edge must align to the area's single gutter.
|
|
64
|
-
|
|
65
|
-
## ESLint preset — scope it to the area
|
|
66
|
-
|
|
67
|
-
`export default [ ...chorus ]` repo-wide would flood an unmigrated codebase with
|
|
68
|
-
`chorus/*` errors and push you toward either suppressing (forbidden) or migrating
|
|
69
|
-
everything (scope violation). Scope the preset with flat-config `files`:
|
|
70
|
-
|
|
71
|
-
```js
|
|
72
|
-
// eslint.config.js
|
|
73
|
-
import chorus from "@teamblind-chorus/ui/eslint";
|
|
74
|
-
export default [
|
|
75
|
-
...chorus.map((cfg) => ({
|
|
76
|
-
...cfg,
|
|
77
|
-
files: ["src/pages/community/**", "src/components/community/**"], // = the declared boundary
|
|
78
|
-
})),
|
|
79
|
-
];
|
|
80
|
-
```
|
|
81
|
-
|
|
82
|
-
* "Chorus lint green" (§E checklist) means green **within the scoped `files` globs**.
|
|
83
|
-
* When the user widens the boundary, widen the globs in the same change — the lint
|
|
84
|
-
scope and the declared boundary must never disagree.
|
|
85
|
-
* Never silence a `chorus/*` error inside the boundary; outside the boundary the
|
|
86
|
-
rules simply don't run.
|
|
87
|
-
|
|
88
|
-
## CSS coexistence — safe by construction
|
|
89
|
-
|
|
90
|
-
Importing `@teamblind-chorus/tokens/tokens.css` + `@teamblind-chorus/ui/styles.css` app-wide is
|
|
91
|
-
safe for the untouched legacy area. The only global effects are:
|
|
92
|
-
|
|
93
|
-
* a `button/input/select/textarea { font-family: inherit }` reset (form controls
|
|
94
|
-
inherit the body font — benign, usually invisible);
|
|
95
|
-
* `:root` custom properties (`--sys-*`, `--ref-*`, `--chorus-placeholder-image`) —
|
|
96
|
-
inert until something consumes them;
|
|
97
|
-
* `.chorus-*` / `.sys-typo-*` classes — inert on legacy markup.
|
|
98
|
-
|
|
99
|
-
The Pretendard `<link>` loads a font; it restyles nothing until a rule asks for it.
|
|
100
|
-
So: import the stylesheets once at app entry as §A.0 says — do NOT try to
|
|
101
|
-
conditionally load CSS per route, and do not warn the user about "global conflicts".
|
|
102
|
-
|
|
103
|
-
## Exit paths
|
|
104
|
-
|
|
105
|
-
* **Widen** — user names more areas; repeat the boundary declaration, widen the lint
|
|
106
|
-
globs, migrate the new area.
|
|
107
|
-
* **Graduate to §D** — user asks for the full conversion; run §D from step 1 with the
|
|
108
|
-
already-migrated areas as the reference target (skip §D.3's proactive
|
|
109
|
-
reconstruction — it exists).
|
|
110
|
-
* **Escape hatch (§D.7)** still works as documented: "just add the feature" demotes
|
|
111
|
-
everything to a drift note; new code stays pure Chorus.
|