- 01.What We Did
- 02.Phase 1: Content Block Specimens (61 components)
- 03.Phase 2: Layout Compositions (30 components)
- 04.Phase 3: Recharts Visualizations (25 components)
- 05.Phase 4: Nav Restructuring
- 06.Technical Insights
- 07.Parallel Agent Orchestration
- 08.Linter Race Condition
- 09.ReactFlow Node Type Strategy
- 10.File Summary
- 11.Open Questions
Design System Chorus: Content Blocks, Layouts, and Charts
Expanded design system from ~120 to 224 specimens, adding content blocks, full-page layouts, and Recharts data visualizations.
Design System Chorus: Content Blocks, Layouts, and Charts
Date: 2026-01-23 Context: Continuing from the previous session that ported the design system canvas into a Next.js 16 app. Today's session expanded the library from ~120 specimens (primitives: packets, grids, backdrops, marks, icons, dividers, frames, status, hovers, textures, loading, connectors, headlines, gauges, ambient) to 224 total lab components, adding 61 content block specimens, 30 full-page layout compositions, and 25 Recharts data visualizations.
What We Did
Phase 1: Content Block Specimens (61 components)
Wired 61 new content block specimens into the design system canvas across 13 categories:
- Cards (6):
card-project,card-post,card-stat,card-tool,card-link,card-empty - Panels (5):
panel-system,panel-activity,panel-specs,panel-comparison,panel-terminal - Entries (5):
entry-timeline,entry-log,entry-changelog,entry-activity,entry-directory - Banners (5):
banner-status,banner-announcement,banner-metrics,banner-cta,banner-alert - Quotes (5):
quote-pull,quote-block,quote-code,quote-testimonial,quote-highlight - Navigation (5):
nav-breadcrumb,nav-pagination,nav-tabs,nav-section-index,nav-prev-next - Steps (5):
steps-vertical,steps-horizontal,steps-branch,steps-progress,steps-annotated - Tables (5):
table-specs,table-comparison,table-data,table-mini,table-matrix - Previews (5):
preview-article,preview-project,preview-link,preview-embed,preview-repo - Footers (5):
footer-columns,footer-mini,footer-colophon,footer-newsletter,footer-social - Sections (5):
section-feature,section-logos,section-faq,section-pricing,section-about - Separators (5):
separator-numbered,separator-pause,separator-ornament,separator-stat,separator-context
Build fixes during this phase:
steps-progress.tsx— Biome lint error: unused variable. Removed dead code.banner-cta.tsx— Export name mismatch. The file exportedBannerCtabut the page importedBannerCTA. Fixed the component to exportBannerCTA.backdrop-wind.tsxandbackdrop.tsx— Pre-existing TypeScript error: optional chaining on left side of assignment (canvas?.style.width = ...). This is invalid TS — you can't assign to an optional chain. Fixed by wrapping inif (canvas)guard:
// Before (invalid):
canvas?.style.width = `${width}px`;
// After:
if (canvas) {
canvas.style.width = `${width}px`;
}Phase 2: Layout Compositions (30 components)
Brainstormed 6 categories of full-page layout compositions themed "Design Engineer Showcase":
| Category | Components | Examples |
|---|---|---|
| Heroes (5) | layout-hero-split, layout-hero-statement, layout-hero-terminal, layout-hero-metrics, layout-hero-minimal | Name/title with generative SVG art, terminal-style with typing effect |
| Case Studies (5) | layout-case-header, layout-case-breakdown, layout-case-process, layout-case-results, layout-case-gallery | Before/after metrics, timeline process diagrams |
| Work Grids (5) | layout-grid-bento, layout-grid-masonry, layout-grid-featured, layout-grid-timeline, layout-grid-categorized | Asymmetric bento boxes, filtered category views |
| About (5) | layout-about-split, layout-about-skills, layout-about-philosophy, layout-about-experience, layout-about-now | Radar chart skills, chronological experience |
| Lab/Experiments (5) | layout-lab-interactive, layout-lab-code, layout-lab-comparison, layout-lab-diagram, layout-lab-metrics | Live code editors, architecture diagrams |
| Contact (5) | layout-contact-minimal, layout-contact-form, layout-contact-social, layout-contact-collab, layout-contact-status | Terminal-style forms, availability calendars |
Orchestration pattern: Launched 6 parallel Opus agents, each responsible for one category (5 components). All 30 components built concurrently. Each agent received the design language spec (dark-first, monospace, white/10 borders, corner marks, emerald/amber accents) and built self-contained components.
New ReactFlow node type — LayoutNode:
function LayoutNode({ data }: { data: LayoutData }) {
return (
<div className="relative border border-white/10 rounded-lg bg-[#0a0a0a] overflow-hidden">
<div className="relative">
{data.children} {/* Renders at natural size, no fixed width/height */}
</div>
<div className="absolute top-2 right-3">
<div className="text-[10px] font-mono text-white/40">{data.title}</div>
</div>
</div>
);
}Unlike SpecimenNode (fixed 300x300) or PreviewNode (fixed 600x380), LayoutNode renders at natural content size. Layout specimens set their own dimensions (typically width: 800, height: 460-600). Placed on the canvas in a 2-column grid with 860px horizontal and 480-660px vertical spacing depending on category.
Scoped CSS animations with React 19 <style> hoisting:
<style href="layout-hero-split-scroll" precedence="low">
{`@keyframes layout-hero-split-scroll {
0%, 100% { transform: translateY(0); opacity: 0.4; }
50% { transform: translateY(4px); opacity: 0.7; }
}`}
</style>The href acts as a deduplication key — React 19 hoists these to <head> and ensures only one instance per href value exists in the document. precedence="low" places them before utility CSS. This avoids global CSS pollution while keeping animations co-located with their components.
Phase 3: Recharts Visualizations (25 components)
Built 25 chart specimens across 5 categories:
- Time Series (5):
chart-line-deploys,chart-line-performance,chart-area-commits,chart-line-uptime,chart-area-response - Distributions (5):
chart-bar-languages,chart-bar-bundle,chart-bar-coverage,chart-bar-activity,chart-bar-stacked - Comparisons (5):
chart-comparison-frameworks,chart-comparison-versions,chart-scatter-correlation,chart-comparison-before-after,chart-composed-overview - Radial (5):
chart-radar-skills,chart-pie-stack,chart-radialbar-progress,chart-radar-comparison,chart-pie-minimal - Widgets (5):
chart-sparkline-grid,chart-kpi-trend,chart-heatmap-grid,chart-gauge-ring,chart-waterfall
Recharts dark theme pattern:
const tickStyle = {
fontSize: 10,
fontFamily: 'monospace',
fill: 'rgba(255,255,255,0.4)',
};
<CartesianGrid stroke="rgba(255,255,255,0.06)" strokeDasharray="none" />
<XAxis
dataKey="month"
tick={tickStyle}
axisLine={{ stroke: 'rgba(255,255,255,0.15)', strokeWidth: 1 }}
tickLine={false}
/>Key elements: 6% opacity grid (barely visible, not distracting), 15% opacity axis lines, 40% opacity tick labels, all monospace. Accent colors: emerald (#10b981), amber (#f59e0b), white/40 for tertiary data.
TypeScript issues with Recharts:
-
chart-line-performance.tsx— TheLegendcomponent does not accept apayloadprop in its type definitions (even though the docs show custom legends receiving it). Removed the prop and let Recharts auto-derive legend items from the chart children. -
chart-bar-activity.tsx— Tooltip formatter type mismatch. Theformattercallback receivesvaluetyped asValueType(union of string | number | readonly (string | number)[]). Had to type the parameter asunknownto avoid strict mode errors:
// Before (type error):
formatter={(value) => [`${value} commits`, 'Activity']}
// After:
formatter={(value: unknown) => [`${value} commits`, 'Activity']}- Various
Celldeprecation warnings from recharts (component vs children pattern) — not blocking errors, left as-is.
Phase 4: Nav Restructuring
Between content phases, the navigation was restructured from a flat section list to categorized groups. Each category has:
interface Category {
label: string; // "Canvas", "Decorators", "Feedback", etc.
description: string; // Short description of the category
tag: string; // Hex tag like "0x01"
feeds?: string[]; // Dependencies (used for edge generation)
sections: { id: SectionId; label: string }[];
}Added an IndexView that uses @dagrejs/dagre for automatic graph layout. Categories become nodes with feeds relationships becoming directed edges. The dagre algorithm computes positions with rankdir: 'TB', nodesep: 80, ranksep: 120. This produces a dependency DAG showing how Canvas feeds Decorators+Feedback, which feeds Typography+Components, which feeds Layout, which feeds Pages.
Technical Insights
Parallel Agent Orchestration
Pattern for building 30+ components simultaneously:
- Define the design language spec once (color palette, spacing, typography, visual motifs)
- Assign decoupled categories to parallel agents (each agent owns 5 files, no shared state)
- Each agent creates self-contained components — no cross-component imports needed
- Orchestrator handles wiring (the page.tsx imports and specimen arrays)
- Post-build: fix lint/type errors that only surface when everything is composed
Trade-off: agents can't see each other's work, so some visual consistency drift is inevitable. But the design spec constrains this enough that results are cohesive.
Linter Race Condition
The Biome linter (running in watch mode or via editor integration) would remove "unused" imports from page.tsx between the time components were imported and the specimen arrays were added. This happened because:
- Agent adds imports at top of file
- Linter runs, sees no usage of those imports yet, removes them
- Agent adds specimen arrays that reference the imports
- Build fails: missing imports
Fix: Add the specimen arrays first (which reference not-yet-imported symbols), then add imports. The linter won't remove imports that are actually used. Alternatively, add both in a single atomic edit.
ReactFlow Node Type Strategy
Four node types handle different specimen sizes:
| Type | Size | Use Case |
|---|---|---|
SpecimenNode | Fixed 300x300 | Small atomic components (packets, marks, icons) |
FrameNode | Grid of 300x300 cells | Grouped specimens in a single frame |
PreviewNode | Fixed 600x380 | Full-width backdrops |
LayoutNode | Natural content size | Full-page compositions (800px+ wide) |
All share the same visual treatment: border-white/10, bg-[#0a0a0a], monospace title label at top-right.
File Summary
/Users/joel/Code/brubkr/src/app/(standalone)/design-system/page.tsx— 1372 lines, main canvas orchestrator/Users/joel/Code/brubkr/src/components/lab/— 224 component files total- 30 layout compositions (
layout-*.tsx) - 25 chart specimens (
chart-*.tsx) - 61 content block specimens (cards, panels, entries, banners, quotes, navs, steps, tables, previews, footers, sections, separators)
- ~108 primitive specimens (packets, grids, backdrops, icons, marks, dividers, frames, status, hovers, textures, loading, connectors, headlines, gauges, ambient)
- 30 layout compositions (
Open Questions
- The page.tsx is 1372 lines — should the specimen arrays be split into separate files? Currently works because it's a single client component and tree-shaking handles the rest.
- Layout specimens each define their own width/height. Should there be a shared
LAYOUT_Wconstant or is per-component flexibility better? - Chart specimens are 400px wide for the container. Some (radar, pie) look better square. May need a
ChartNodetype that's 400x300 fixed. - The dagre index view is nice but doesn't show specimen count per category. Could add that as a badge on each category node.