feat: boundary-revision batch 9 — token surface + 11-widget catalog#20
feat: boundary-revision batch 9 — token surface + 11-widget catalog#20simonsangla merged 2 commits intomainfrom
Conversation
…ion build site - New cavekit-product-boundary.md: locks scope (R1 in-scope surface, R2 hard-wall out-of-scope, R3 frozen 11-widget catalog, R4 cross-cutting purity invariants) - New cavekit-widgets.md: backfills shipped widget-builder + previews and extends catalog from 8 to 11 (badge, pricing-card, testimonial added alphabetically), plus kpi-tile metric-variant requirement - Updated cavekit-schema.md: R1 grows from 4 to 9 color slots; new R8 shadow group; new R9 radius group; R4/R5/R7 updated to compose them; out-of-scope loses border-radius and shadow exclusions - Updated cavekit-overview.md: 7 domains, 53 requirements, new cross-refs - New context/plans/build-site-boundary-revision.md: 29 tasks (T-100..T-129) in 5 tiers, 78/78 NEW criteria covered, full Mermaid graph
Implements the boundary-revision delta T-100..T-129 from context/plans/build-site-boundary-revision.md. Schema (Tier 0+1): - ColorTokenSchema: 4 → 9 slots (added muted, hairline, inkSoft, surfaceInvert, onInvert) - ShadowTokenSchema: 4 named CSS box-shadow strings (primary, secondary, card, float) - RadiusTokenSchema: 5 numeric pixel values (pill, sm, md, lg, xl) with sm<=md<=lg<=xl ascending rule - ThemeConfigSchema, ThemeVariantPair: compose new groups + share-equality validation across light/dark variants - DEFAULT_THEME: 9 colors + 4 shadows + 5 radii (values from portfolio/DESIGN.md §2/§5/§6) - WIDGET_IDS: 8 → 11 (badge, pricing-card, testimonial inserted alphabetically), labels + selection schema + default selection updated - Persistence: legacy 4-color records load with patched defaults; legacy 8-widget selections load with 3 new keys patched to false (read back-compat — no schema-version bump required) Exports (Tier 2): every export function emits the new tokens - CSS / SCSS / Tailwind / Style Dictionary all gain color/shadow/radius emission rules; JSON / TS surface naturally via the schema-extended object; back-compat preserved (omitting widgets argument yields prior output exactly) Widget UI (Tier 2): - WidgetPreview: 4 new arms (badge, pricing-card, testimonial, kpi-tile metric variant); kpi-tile gains optional `variant?: 'tile' | 'metric'` prop; metric uses top-hairline + serif numeric + uppercase tracked label per portfolio/DESIGN.md §4.10/§6 - WidgetSelector: auto-grew to 11 toggles (no signature change) Editor (Tier 3): - Store: updateShadows + updateRadii actions (validated, undoable, partial) - ThemeEditor: 5 new color pickers, Shadows section (4 monospaced textareas), Radii section (5 number inputs); per-field error surface follows existing pattern Wiring (Tier 4): - themeToStyleVars emits all --color-* (9), --shadow-* (4), --radius-* (5) - App threads new store actions through to ThemeEditor Tests: - 220/220 pass (157 existing fixed + 63 new) - New: tests/schema/shadows.test.ts (positive, missing slots, invalid values, validator surface) - New: tests/schema/radii.test.ts (positive, missing slots, invalid values, ascending-order rule, validator surface) - New: tests/persistence/backCompat.test.ts (legacy 4-color theme load patches; legacy 8-widget selection load patches; round-trip preserves all groups) - New: tests/components/WidgetPreview.test.tsx (every catalog ID renders unique marker; kpi-tile metric variant produces distinct DOM) - Existing fixtures updated to spread DEFAULT_THEME so they remain schema-valid as the surface grew
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
There was a problem hiding this comment.
Code Review
This pull request implements Batch 9, establishing a Product Boundary to lock application scope and expanding the theme schema with shadow and radius token groups. The color palette grows to nine slots, and the widget catalog expands to eleven IDs with new renderings for badges, pricing cards, and testimonials. Export and persistence layers are updated to support these tokens and ensure backward compatibility for legacy records. Feedback recommends refactoring repetitive CSS variable generation and export logic into generic helper functions and implementing a more robust migration strategy for legacy data patching.
| function patchLegacyTheme(raw: unknown): unknown { | ||
| if (typeof raw !== 'object' || raw === null) return raw | ||
| const r = raw as Record<string, unknown> | ||
| const colors = (typeof r.colors === 'object' && r.colors !== null) | ||
| ? { ...DEFAULT_THEME.colors, ...(r.colors as Record<string, unknown>) } | ||
| : { ...DEFAULT_THEME.colors } | ||
| const shadows = (typeof r.shadows === 'object' && r.shadows !== null) | ||
| ? { ...DEFAULT_THEME.shadows, ...(r.shadows as Record<string, unknown>) } | ||
| : { ...DEFAULT_THEME.shadows } | ||
| const radii = (typeof r.radii === 'object' && r.radii !== null) | ||
| ? { ...DEFAULT_THEME.radii, ...(r.radii as Record<string, unknown>) } | ||
| : { ...DEFAULT_THEME.radii } | ||
| return { ...r, colors, shadows, radii } | ||
| } |
There was a problem hiding this comment.
| for (const k of Object.keys(config.colors) as Array<keyof typeof config.colors>) { | ||
| out[`--color-${kebab(String(k))}`] = config.colors[k] | ||
| } | ||
| for (const k of Object.keys(config.shadows) as Array<keyof typeof config.shadows>) { | ||
| out[`--shadow-${k}`] = config.shadows[k] | ||
| } | ||
| for (const k of Object.keys(config.radii) as Array<keyof typeof config.radii>) { | ||
| out[`--radius-${k}`] = `${config.radii[k]}px` | ||
| } |
| function colorVarLines(colors: ThemeConfig['colors']): string[] { | ||
| return (Object.keys(colors) as Array<keyof typeof colors>).map( | ||
| (k) => ` --color-${kebab(String(k))}: ${colors[k]};`, | ||
| ) | ||
| } | ||
|
|
||
| function shadowVarLines(shadows: ThemeConfig['shadows']): string[] { | ||
| return (Object.keys(shadows) as Array<keyof typeof shadows>).map( | ||
| (k) => ` --shadow-${k}: ${shadows[k]};`, | ||
| ) | ||
| } | ||
|
|
||
| function radiusVarLines(radii: ThemeConfig['radii']): string[] { | ||
| return (Object.keys(radii) as Array<keyof typeof radii>).map( | ||
| (k) => ` --radius-${k}: ${radii[k]}px;`, | ||
| ) | ||
| } |
Summary
Implements the boundary-revision delta from
context/plans/build-site-boundary-revision.md(29 tasks T-100..T-129, 5 tiers). Adds two new cavekits (product-boundary, widgets), extendscavekit-schemawith shadow + radius groups + 5 new color slots, and grows the widget catalog from 8 to 11 IDs (badge, pricing-card, testimonial). Single coherent batch; all 220 tests pass.Cavekits added/extended
cavekit-product-boundary.md(R1–R4): in-scope surface, hard-wall denials, frozen 11-widget catalog, cross-cutting purity invariantscavekit-widgets.md(R1–R6): backfills shipped widget-builder + previews; extends to 11 IDs + kpi-tile metric variantcavekit-schema.md: R1 expanded (4 → 9 slots); R8 shadow group; R9 radius group; R4/R5/R7 updated to compose themcavekit-overview.md: 7 domains, 53 requirements, full cross-ref map + dependency graphImplementation deltas
Schema: 9-slot color group, shadow group (4 named CSS strings), radius group (5 numeric pixel slots with sm<=md<=lg<=xl ascending rule). DEFAULT_THEME values from portfolio/DESIGN.md §2/§5/§6. Persistence patches legacy records read-side (no version bump).
Exports: all 12 export functions emit the new tokens.
--color-mutedetc.,--shadow-*(4),--radius-*(5)$color-*,$shadow-*,$radius-*theme.extend.colors(5 new),boxShadow,borderRadiustype: 'boxShadow'), radius group (type: 'dimension')Back-compat preserved: omitting widgets argument yields prior output exactly.
Widget UI: WidgetPreview gains 4 new arms (badge, pricing-card, testimonial, kpi-tile metric variant). kpi-tile takes optional
variant?: 'tile' | 'metric'. Metric variant uses top-hairline + serif numeric + uppercase tracked label per portfolio/DESIGN.md §4.10/§6. WidgetSelector auto-grew to 11 toggles (no API change).Editor: 5 new color pickers, Shadows section (4 textareas), Radii section (5 number inputs). New store actions
updateShadows+updateRadii(validated, undoable, partial).Wiring:
themeToStyleVarsemits all 18 new CSS vars so previews + chrome inherit them automatically.Files
shadows.test.ts,radii.test.ts,backCompat.test.ts,WidgetPreview.test.tsx)build-site-boundary-revision.md)AGENT_HANDOFF.mdBatch 9 section prepended)Validation
Browser verification
With Dark preset:
colors, pluswidgetsarray reflecting selectionBoundary R3 frozen catalog
badge, button, card, empty-state, input, kpi-tile, modal, navbar, pricing-card, table, testimonialAdding/removing/renaming any ID requires a dedicated boundary-revision PR.
Test plan
npm run lint— cleannpm run typecheck— cleannpm run test— 220/220npm run build— succeeds🤖 Generated with Claude Code