diff --git a/docs/superpowers/specs/2026-03-18-design-system-packaging-design.md b/docs/superpowers/specs/2026-03-18-design-system-packaging-design.md new file mode 100644 index 0000000..af52607 --- /dev/null +++ b/docs/superpowers/specs/2026-03-18-design-system-packaging-design.md @@ -0,0 +1,192 @@ +# Design System Packaging — Design Spec + +**Date:** 2026-03-18 +**Status:** Approved +**Package:** `@cameleer/design-system` +**Registry:** Gitea npm registry at `gitea.siegeln.net` + +## Goal + +Package the Cameleer3 design system as a reusable npm library so other React applications in the Cameleer ecosystem can consume it via `npm install`. Publishing is automated via Gitea Actions. + +## Decisions + +| Decision | Choice | Rationale | +|----------|--------|-----------| +| Registry | Gitea built-in npm registry | Already have Gitea infrastructure | +| Package scope | `@cameleer/design-system` | Matches the org | +| Export style | Single package, flat exports | Simple DX, tree-shaking handles unused code | +| What's included | Everything (primitives, composites, layout, providers, utils, tokens) | All consuming apps are Cameleer apps | +| Build tool | Vite library mode | Already using Vite, CSS Modules first-class | +| Output format | ESM only | All consumers are Vite/ESM | +| Versioning | Tag-based releases + snapshot on every main push | Snapshots for dev, tags for milestones | +| Runner arch | ARM64 | Gitea runner is ARM64 | +| Auth secret | `REGISTRY_TOKEN` (org-level) | Existing all-access token | + +## 1. Library Entry Point + +New file `src/design-system/index.ts` — the single public API: + +```ts +export * from './primitives' +export * from './composites' +export * from './layout' +export * from './providers/ThemeProvider' +export * from './providers/CommandPaletteProvider' +export * from './providers/GlobalFilterProvider' +export * from './utils/hashColor' +export * from './utils/timePresets' +``` + +CSS (tokens.css, reset.css, all component CSS Modules) is bundled into a single `dist/style.css`. Consumers import it once: + +```ts +import '@cameleer/design-system/style.css' +``` + +## 2. Vite Library Build Config + +A separate `vite.lib.config.ts` to keep library and app builds independent: + +- **Entry:** `src/design-system/index.ts` +- **Output:** `dist/index.es.js` (ESM) +- **CSS:** Extracted to `dist/style.css` +- **Externals:** `react`, `react-dom`, `react-router-dom` (peer deps, not bundled) +- **Types:** `vite-plugin-dts` generates `dist/index.d.ts` with full TypeScript declarations +- **Build script:** `"build:lib": "vite build --config vite.lib.config.ts"` + +Output structure: + +``` +dist/ + index.es.js # ESM bundle + style.css # All CSS (tokens + reset + component modules) + index.d.ts # TypeScript declarations +``` + +## 3. Package Configuration + +Updates to `package.json`: + +```json +{ + "name": "@cameleer/design-system", + "version": "0.1.0", + "type": "module", + "main": "./dist/index.es.js", + "module": "./dist/index.es.js", + "types": "./dist/index.d.ts", + "exports": { + ".": { + "import": "./dist/index.es.js", + "types": "./dist/index.d.ts" + }, + "./style.css": "./dist/style.css" + }, + "files": ["dist"], + "sideEffects": ["*.css"], + "peerDependencies": { + "react": "^19.0.0", + "react-dom": "^19.0.0", + "react-router-dom": "^7.0.0" + } +} +``` + +- `"private": true` is removed +- Existing `scripts`, `dependencies`, and `devDependencies` remain for the app build +- `peerDependencies` tells consumers what to provide + +## 4. Gitea Actions CI/CD Pipeline + +Workflow at `.gitea/workflows/publish.yml`: + +**Triggers:** +- Push to `main` → publish snapshot (`0.0.0-snapshot..`) with `dev` dist-tag +- Push tag `v*` → publish stable release (e.g., `1.0.0`) with `latest` dist-tag + +**Steps:** +1. Checkout at ref +2. `npm ci` (install deps) +3. `npx vitest run` (gate: don't publish broken code) +4. `npm run build:lib` (build the library) +5. Determine version from tag or generate snapshot version +6. Configure `.npmrc` with `REGISTRY_TOKEN` +7. `npm publish --tag ` + +**Runner:** ARM64 with `node:22-bookworm-slim` container image. + +```yaml +name: Build & Publish + +on: + push: + branches: [main] + tags: ['v*'] + +jobs: + publish: + runs-on: linux-arm64 + container: + image: node:22-bookworm-slim + steps: + - uses: actions/checkout@v4 + - run: npm ci + - run: npx vitest run + - run: npm run build:lib + - run: | + if [[ "$GITHUB_REF" == refs/tags/v* ]]; then + VERSION="${GITHUB_REF_NAME#v}" + npm version "$VERSION" --no-git-tag-version + TAG="latest" + else + SHORT_SHA=$(echo "$GITHUB_SHA" | head -c 7) + DATE=$(date +%Y%m%d) + npm version "0.0.0-snapshot.${DATE}.${SHORT_SHA}" --no-git-tag-version + TAG="dev" + fi + echo "//gitea.siegeln.net/api/packages/cameleer/npm/:_authToken=${{ secrets.REGISTRY_TOKEN }}" > .npmrc + npm publish --tag "$TAG" +``` + +## 5. Consumer Setup + +In any consuming app (e.g., `cameleer3-server/ui`): + +**1. Add `.npmrc` to project root:** + +``` +@cameleer:registry=https://gitea.siegeln.net/api/packages/cameleer/npm/ +//gitea.siegeln.net/api/packages/cameleer/npm/:_authToken=${GITEA_TOKEN} +``` + +**2. Install:** + +```bash +# During development (snapshot builds) +npm install @cameleer/design-system@dev + +# For stable releases (later) +npm install @cameleer/design-system +``` + +**3. Use:** + +```tsx +// Import styles once at app root +import '@cameleer/design-system/style.css' + +// Import components +import { Button, Input, Modal, AppShell, ThemeProvider } from '@cameleer/design-system' +``` + +## 6. Documentation Updates + +Update `CLAUDE.md` and `COMPONENT_GUIDE.md` in this repo with: + +- The package name and registry URL +- How consuming apps should configure `.npmrc` +- Import patterns for consumers (`@cameleer/design-system` instead of relative paths) +- Note that `style.css` must be imported once at the app root + +This ensures other AI agents working on consuming Cameleer apps understand how to use the design system.