dimensionlab-website/docs/superpowers/specs/2026-06-20-turbo-component-library-design.md

7.6 KiB

Turbo Component Library Migration Design

Context

The current Dimension Lab website is a single Bun/Vite React package. It owns the browser app, Bun production server, dashboard model, persistence, datasource adapters, reusable UI components, Storybook, Playwright checks, and container deployment from one package.json.

The UI components are already mostly generic and content-free under src/lib/ui, but they are not reusable by another project because they live inside the app package, depend on app path aliases, and share the app build, test, and Storybook configuration. One file in that area, src/lib/ui/model-renderer.ts, imports the Dimension Lab dashboard model and therefore is an app adapter, not reusable component-library code.

The migration goal is to turn the repository into a Turborepo workspace where the Dimension Lab website consumes a separate reusable React component package.

Target Repository Shape

Use one application workspace and one component-library workspace:

.
├── apps/
│   └── web/
│       ├── src/
│       ├── tests/
│       ├── drizzle/
│       ├── Containerfile
│       └── package.json
├── packages/
│   └── ui/
│       ├── src/
│       ├── .storybook/
│       ├── package.json
│       └── tsconfig.json
├── package.json
├── turbo.json
├── tsconfig.base.json
└── bun.lock

The root package is private and contains only workspace orchestration: workspaces, Turbo scripts, shared dev dependencies where useful, and the lock file. Runtime dependencies belong to the workspace that imports them.

Package Ownership

packages/ui is a compiled React library named @dimensionlab/ui.

It owns:

  • Reusable React components currently under src/lib/ui/components.
  • UI CSS tokens and component styles.
  • Theme helpers currently under src/lib/ui/theme.ts.
  • Generic UI prop/data types currently under src/lib/ui/types.ts.
  • Generic formatting helpers currently under src/lib/ui/format.ts.
  • Generic Storybook stories and story fixtures.
  • UI render tests, boundary tests, and Storybook inventory tests.

It must not import from the website app, the dashboard model, server modules, database modules, datasource modules, or deployment files.

The library publishes explicit package exports:

  • @dimensionlab/ui for component and type exports.
  • @dimensionlab/ui/styles.css for the combined token/component CSS entry.
  • Optional explicit subpath exports for future direct imports where useful.

The compiled output goes to packages/ui/dist and includes JavaScript, declaration files, and copied CSS. The package stays private for now but is structured so it can later be published or moved into another Dimension Lab repo without taking the website runtime with it.

apps/web owns:

  • The Vite React browser app.
  • The Bun production server and API routes.
  • Dashboard model, fixtures, schema, validation, and model migrations.
  • Drizzle/SQLite persistence and checked-in SQL migrations.
  • Datasource adapters and runtime dashboard loading.
  • Agent dashboard configuration endpoint.
  • The dashboardDocumentToUiDashboard adapter that maps the app model to UI package props.
  • Playwright e2e tests and deployment container.

Why A Compiled Package

The component package should be compiled rather than a just-in-time source package. This is slightly more setup, but it better fits reuse outside the current app because consumers can import stable JavaScript and declarations instead of relying on their bundler to transpile this repo's TypeScript source. It also gives Turbo a cacheable @dimensionlab/ui#build task.

Build And Task Graph

Root scripts delegate through Turbo:

  • bun run dev runs the web dev server and any required dependency tasks.
  • bun run build runs package builds in dependency order.
  • bun run check runs TypeScript checks for all workspaces.
  • bun run test:unit runs Vitest unit tests for all workspaces.
  • bun run build-storybook builds Storybook from packages/ui.
  • bun run test:e2e runs the web app Playwright suite.
  • bun run test:qa is the full release gate.

turbo.json defines build, check, test:unit, build-storybook, test:e2e, and test:qa tasks. Build outputs include dist/**, storybook-static/**, and build/** as appropriate.

The web app depends on @dimensionlab/ui using Bun workspace syntax. The web Vite config aliases $lib to apps/web/src/lib; the UI package should not use that app alias.

Storybook

Storybook moves with the component package. It should load @dimensionlab/ui/styles.css, use React Vite Storybook, and keep the existing generic story inventory. Environment-specific Dimension Lab labels, hostnames, links, fallback values, and datasource names remain forbidden in package UI source and stories.

The repository should no longer have a root Storybook tied to the web app.

Deployment

The deployed website remains the same service from the outside:

  • Production command remains bun build/index.js inside the runtime image.
  • The container still exposes port 3000 and mounts /data.
  • Runtime environment variables and database behavior remain unchanged.

The Containerfile moves to apps/web/Containerfile or remains root with updated workspace-aware copy/build steps. The chosen layout must preserve the existing Podman service contract used by dimensionlab-website.service.

Testing Strategy

The migration must add or update tests that prove the new boundaries:

  • The root package is a Bun workspace with apps/* and packages/*.
  • The web app imports UI from @dimensionlab/ui, not from local copied component files.
  • packages/ui does not import from apps/web, $lib/server, $lib/model, or any website runtime module.
  • dashboardDocumentToUiDashboard lives in apps/web and is tested there.
  • Storybook inventory is evaluated against packages/ui.
  • The full QA gate still covers typecheck, unit tests, app build, Storybook build, and Playwright desktop/mobile checks.

Completion Criteria

The migration is complete only when current evidence proves all of these:

  • Root package.json is a private workspace root with Turbo scripts.
  • turbo.json exists and models the workspace task graph.
  • The web application lives under apps/web.
  • The reusable React component library lives under packages/ui.
  • packages/ui/package.json is named @dimensionlab/ui and has compiled exports for code, types, and CSS.
  • The web app depends on @dimensionlab/ui through the workspace.
  • Reusable components and Storybook have been removed from the web app package.
  • App-specific model/server/database/datasource code has not moved into packages/ui.
  • dashboardDocumentToUiDashboard is outside the UI package.
  • bun run check passes from the root.
  • bun run test:unit passes from the root.
  • bun run build passes from the root.
  • bun run build-storybook passes from the root.
  • bun run test:e2e passes from the root.
  • The production container can still be built and run with the same service contract.

Migration Approach

Implement this on branch codex/turbo-component-library in focused commits:

  1. Add the workspace/Turbo scaffolding and boundary tests.
  2. Move UI code and Storybook to packages/ui.
  3. Move the app runtime into apps/web and wire it to @dimensionlab/ui.
  4. Move the model-to-UI adapter into the web app.
  5. Update build, test, Playwright, Storybook, README, and container paths.
  6. Run the full QA gate, push a ready PR, perform independent review, resolve blockers, merge to main, and deploy only when checks and review are clean.