# Design System Packaging Implementation Plan

> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
>
> **IMPORTANT: Use an isolated git worktree** — another agent may be working on the main tree. Create a worktree before starting any task. The worktree must be created from the current working state (not a clean `main`) because several provider/util files are uncommitted.

**Goal:** Package the Cameleer3 design system as `@cameleer/design-system` and publish it to Gitea's npm registry via CI/CD.

**Architecture:** Vite library mode builds the design system into an ESM bundle + CSS + TypeScript declarations. A Gitea Actions workflow publishes snapshot versions on every push to main, and stable versions on `v*` tags. Consuming apps install from Gitea's npm registry.

**Tech Stack:** Vite (library mode), vite-plugin-dts, TypeScript, CSS Modules, Gitea Actions

**Spec:** `docs/superpowers/specs/2026-03-18-design-system-packaging-design.md`

---

## Prerequisites

Before starting, ensure these currently-untracked files are committed (they are referenced by the library entry point):

- `src/design-system/providers/CommandPaletteProvider.tsx`
- `src/design-system/providers/GlobalFilterProvider.tsx`
- `src/design-system/utils/timePresets.ts`

The git remote should be added **before** creating a worktree, since remotes are repo-level config shared across all worktrees.

---

## File Map

| Action | File | Responsibility |
|--------|------|----------------|
| Create | `src/design-system/index.ts` | Library entry point — re-exports all components, imports global CSS |
| Create | `vite.lib.config.ts` | Vite library build config (separate from app build) |
| Create | `.gitea/workflows/publish.yml` | CI/CD: test, build, publish on push to main / tag |
| Modify | `.gitignore` | Add `dist/` to prevent build artifacts from being committed |
| Modify | `package.json` | Rename, add exports/peers/publishConfig/files/repository |
| Modify | `tsconfig.node.json` | Include `vite.lib.config.ts` |
| Modify | `CLAUDE.md` | Add consumer usage docs for AI agents |
| Modify | `COMPONENT_GUIDE.md` | Add package import paths for consumers |

---

### Task 1: Add git remote and commit untracked files

**Files:**
- None created (git operations + staging existing untracked files)

- [ ] **Step 1: Add the origin remote**

```bash
git remote add origin https://gitea.siegeln.net/cameleer/design-system.git
```

If `origin` already exists, use `git remote set-url origin https://gitea.siegeln.net/cameleer/design-system.git` instead.

- [ ] **Step 2: Verify remote**

```bash
git remote -v
```

Expected: `origin  https://gitea.siegeln.net/cameleer/design-system.git (fetch)` and `(push)`

- [ ] **Step 3: Commit untracked provider/util files**

These files are required by the library entry point (Task 3) but are currently untracked:

```bash
git add src/design-system/providers/CommandPaletteProvider.tsx src/design-system/providers/GlobalFilterProvider.tsx src/design-system/utils/timePresets.ts
git commit -m "feat: add CommandPaletteProvider, GlobalFilterProvider, and timePresets"
```

- [ ] **Step 4: Commit any other pending changes**

Check `git status` — there may be other modified files from a prior agent's work. Stage and commit anything that belongs on main before proceeding:

```bash
git status
```

If there are modified files, commit them with an appropriate message before continuing.

---

### Task 2: Install vite-plugin-dts and add dist/ to .gitignore

**Files:**
- Modify: `package.json` (devDependencies)
- Modify: `.gitignore`

- [ ] **Step 1: Install the plugin**

```bash
npm install -D vite-plugin-dts
```

Note: `vite-plugin-dts` with `rollupTypes: true` requires `@microsoft/api-extractor` as a peer dependency. If the install warns about this, also install it:

```bash
npm install -D @microsoft/api-extractor
```

- [ ] **Step 2: Verify it's in devDependencies**

```bash
node -e "console.log(JSON.parse(require('fs').readFileSync('package.json','utf8')).devDependencies['vite-plugin-dts'])"
```

Expected: a version string like `^4.x.x`

- [ ] **Step 3: Add `dist/` to `.gitignore`**

Append to `.gitignore`:

```
dist/
```

This prevents build artifacts from being accidentally committed.

- [ ] **Step 4: Commit**

```bash
git add package.json package-lock.json .gitignore
git commit -m "chore: add vite-plugin-dts and ignore dist/"
```

---

### Task 3: Create library entry point

**Files:**
- Create: `src/design-system/index.ts`

- [ ] **Step 1: Create `src/design-system/index.ts`**

```ts
import './tokens.css'
import './reset.css'

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'
```

This file imports `tokens.css` and `reset.css` at the top so Vite includes them in the bundled `style.css`. Without these imports, all `var(--*)` tokens would resolve to nothing in consuming apps.

- [ ] **Step 2: Verify TypeScript is happy**

```bash
npx tsc -b --noEmit
```

Expected: no errors

- [ ] **Step 3: Commit**

```bash
git add src/design-system/index.ts
git commit -m "feat: add library entry point for design system package"
```

---

### Task 4: Create Vite library build config

**Files:**
- Create: `vite.lib.config.ts`
- Modify: `tsconfig.node.json`

- [ ] **Step 1: Create `vite.lib.config.ts`**

Note: `__dirname` works in Vite config files despite this being an ESM project — Vite transpiles config files before executing them.

```ts
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import dts from 'vite-plugin-dts'
import { resolve } from 'path'

export default defineConfig({
  plugins: [
    react(),
    dts({
      include: ['src/design-system'],
      outDir: 'dist',
      rollupTypes: true,
    }),
  ],
  css: {
    modules: {
      localsConvention: 'camelCase',
      generateScopedName: 'cameleer_[name]_[local]_[hash:5]',
    },
  },
  build: {
    lib: {
      entry: resolve(__dirname, 'src/design-system/index.ts'),
      formats: ['es'],
      fileName: () => 'index.es.js',
    },
    rollupOptions: {
      external: ['react', 'react-dom', 'react-router-dom', 'react/jsx-runtime'],
    },
    cssFileName: 'style',
  },
})
```

Key choices:
- `rollupTypes: true` consolidates all `.d.ts` into a single `dist/index.d.ts`
- `generateScopedName: 'cameleer_[name]_[local]_[hash:5]'` makes class names debuggable in consumer devtools
- `react/jsx-runtime` is externalized (peer dep of React, not bundled)
- `cssFileName: 'style'` ensures output is `dist/style.css`
- `fileName: () => 'index.es.js'` forces a deterministic output filename — Vite 6 defaults to `.mjs` for ES format which would mismatch `package.json` exports

- [ ] **Step 2: Update `tsconfig.node.json` to include the new config file**

Change the `include` array from:

```json
"include": ["vite.config.ts"]
```

to:

```json
"include": ["vite.config.ts", "vite.lib.config.ts"]
```

- [ ] **Step 3: Test the library build**

The `build:lib` script isn't in `package.json` yet (added in Task 5). Run directly:

```bash
npx vite build --config vite.lib.config.ts
```

Expected: `dist/` directory created with `index.es.js`, `style.css`, and `index.d.ts`

- [ ] **Step 4: Verify the output**

```bash
ls dist/
```

Expected: `index.es.js`, `style.css`, `index.d.ts`

Check that `style.css` contains token variables:

```bash
grep "bg-body" dist/style.css
```

Expected: matches found (proves tokens.css was included)

Check that type declarations contain exported components:

```bash
grep "Button" dist/index.d.ts
```

Expected: matches found (proves types were generated)

If `index.d.ts` is missing or empty, `rollupTypes` may have failed silently. In that case, install `@microsoft/api-extractor` and rebuild, or set `rollupTypes: false` (which produces individual `.d.ts` files — less clean but functional).

- [ ] **Step 5: Clean up and commit**

```bash
rm -rf dist
git add vite.lib.config.ts tsconfig.node.json
git commit -m "feat: add Vite library build config with dts generation"
```

---

### Task 5: Update package.json for library publishing

**Files:**
- Modify: `package.json`

- [ ] **Step 1: Update package.json**

Apply these changes to the existing `package.json`:

1. Change `"name"` from `"cameleer3"` to `"@cameleer/design-system"`
2. Change `"version"` from `"0.0.0"` to `"0.1.0"`
3. Remove `"private": true`
4. Add `"main": "./dist/index.es.js"`
5. Add `"module": "./dist/index.es.js"`
6. Add `"types": "./dist/index.d.ts"`
7. Add `"exports"` block (note: `types` must come first):
   ```json
   "exports": {
     ".": {
       "types": "./dist/index.d.ts",
       "import": "./dist/index.es.js"
     },
     "./style.css": "./dist/style.css"
   }
   ```
8. Add `"files": ["dist"]`
9. Add `"sideEffects": ["*.css"]`
10. Add `"publishConfig"`:
    ```json
    "publishConfig": {
      "registry": "https://gitea.siegeln.net/api/packages/cameleer/npm/"
    }
    ```
11. Add `"repository"`:
    ```json
    "repository": {
      "type": "git",
      "url": "https://gitea.siegeln.net/cameleer/design-system.git"
    }
    ```
12. Add `"peerDependencies"`:
    ```json
    "peerDependencies": {
      "react": "^19.0.0",
      "react-dom": "^19.0.0",
      "react-router-dom": "^7.0.0"
    }
    ```
13. Add to `"scripts"`:
    ```json
    "build:lib": "vite build --config vite.lib.config.ts"
    ```

The final `package.json` should look like:

```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": {
    ".": {
      "types": "./dist/index.d.ts",
      "import": "./dist/index.es.js"
    },
    "./style.css": "./dist/style.css"
  },
  "files": ["dist"],
  "sideEffects": ["*.css"],
  "publishConfig": {
    "registry": "https://gitea.siegeln.net/api/packages/cameleer/npm/"
  },
  "repository": {
    "type": "git",
    "url": "https://gitea.siegeln.net/cameleer/design-system.git"
  },
  "scripts": {
    "dev": "vite",
    "build": "tsc -b && vite build",
    "build:lib": "vite build --config vite.lib.config.ts",
    "lint": "eslint .",
    "preview": "vite preview",
    "test": "vitest"
  },
  "peerDependencies": {
    "react": "^19.0.0",
    "react-dom": "^19.0.0",
    "react-router-dom": "^7.0.0"
  },
  "dependencies": {
    "react": "^19.0.0",
    "react-dom": "^19.0.0",
    "react-router-dom": "^7.0.0"
  },
  "devDependencies": {
    "@testing-library/jest-dom": "^6.6.3",
    "@testing-library/react": "^16.3.0",
    "@testing-library/user-event": "^14.6.1",
    "@types/react": "^19.0.0",
    "@types/react-dom": "^19.0.0",
    "@vitejs/plugin-react": "^4.3.0",
    "happy-dom": "^20.8.4",
    "typescript": "^5.6.0",
    "vite": "^6.0.0",
    "vite-plugin-dts": "<version installed in Task 2>",
    "vitest": "^3.0.0"
  }
}
```

- [ ] **Step 2: Verify the full library build works end-to-end**

```bash
npm run build:lib
```

Expected: succeeds, `dist/` contains `index.es.js`, `style.css`, `index.d.ts`

- [ ] **Step 3: Clean up and commit**

```bash
rm -rf dist
git add package.json
git commit -m "feat: configure package.json for @cameleer/design-system publishing"
```

---

### Task 6: Create Gitea Actions workflow

**Files:**
- Create: `.gitea/workflows/publish.yml`

- [ ] **Step 1: Create `.gitea/workflows/publish.yml`**

```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

      - name: Install dependencies
        run: npm ci

      - name: Run tests
        run: npx vitest run

      - name: Build library
        run: npm run build:lib

      - name: Publish package
        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 '@cameleer:registry=https://gitea.siegeln.net/api/packages/cameleer/npm/' > .npmrc
          echo '//gitea.siegeln.net/api/packages/cameleer/npm/:_authToken=${{ secrets.REGISTRY_TOKEN }}' >> .npmrc
          npm publish --tag "$TAG"
```

Note: The `.npmrc` is written with `echo` commands (not a heredoc) to avoid YAML indentation being included in the file content, which would break npm's parsing.

- [ ] **Step 2: Commit**

```bash
mkdir -p .gitea/workflows
git add .gitea/workflows/publish.yml
git commit -m "ci: add Gitea Actions workflow for npm publishing"
```

---

### Task 7: Update documentation for consumers

**Files:**
- Modify: `CLAUDE.md`
- Modify: `COMPONENT_GUIDE.md`

- [ ] **Step 1: Add consumer section to `CLAUDE.md`**

Add the following section at the end of `CLAUDE.md`:

```markdown
## Using This Design System in Other Apps

This design system is published as `@cameleer/design-system` to the Gitea npm registry.

### Registry: `https://gitea.siegeln.net/api/packages/cameleer/npm/`

### Setup in a consuming app

1. Add `.npmrc` to the project root:

```
@cameleer:registry=https://gitea.siegeln.net/api/packages/cameleer/npm/
//gitea.siegeln.net/api/packages/cameleer/npm/:_authToken=${GITEA_TOKEN}
```

Note: CI pipelines for consuming apps also need this `.npmrc` and a `GITEA_TOKEN` secret to fetch the package during `npm ci`.

2. Install:

```bash
# Snapshot builds (during development)
npm install @cameleer/design-system@dev

# Stable releases
npm install @cameleer/design-system
```

3. Add fonts to `index.html` (required — the package does not bundle fonts):

```html
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=DM+Sans:ital,opsz,wght@0,9..40,300;0,9..40,400;0,9..40,500;0,9..40,600;0,9..40,700&family=JetBrains+Mono:wght@400;500;600&display=swap" rel="stylesheet">
```

Without these, `var(--font-body)` and `var(--font-mono)` fall back to `system-ui` / `monospace`.

4. Import styles once at app root, then use components:

```tsx
import '@cameleer/design-system/style.css'
import { Button, AppShell, ThemeProvider } from '@cameleer/design-system'
```

### Import Paths (Consumer)

```tsx
// All components from single entry
import { Button, Input, Modal, DataTable, AppShell } from '@cameleer/design-system'

// Types
import type { Column, DataTableProps, SearchResult } from '@cameleer/design-system'

// Providers
import { ThemeProvider, useTheme } from '@cameleer/design-system'
import { CommandPaletteProvider, useCommandPalette } from '@cameleer/design-system'
import { GlobalFilterProvider, useGlobalFilters } from '@cameleer/design-system'

// Utils
import { hashColor } from '@cameleer/design-system'

// Styles (once, at app root)
import '@cameleer/design-system/style.css'
```
```

- [ ] **Step 2: Update the `## Import Paths` section in `COMPONENT_GUIDE.md`**

Find the `## Import Paths` section heading and replace everything from that heading down to the next `##` heading (or end of file) with:

```markdown
## Import Paths

### Within this repo (design system development)

```tsx
import { Button, Input, Badge } from './design-system/primitives'
import { DataTable, Modal, Toast } from './design-system/composites'
import type { Column, SearchResult, FeedEvent } from './design-system/composites'
import { AppShell } from './design-system/layout/AppShell'
import { ThemeProvider, useTheme } from './design-system/providers/ThemeProvider'
```

### From consuming apps (via npm package)

```tsx
import '@cameleer/design-system/style.css'  // once at app root
import { Button, Input, Modal, DataTable, AppShell, ThemeProvider } from '@cameleer/design-system'
import type { Column, DataTableProps, SearchResult } from '@cameleer/design-system'
```

See `CLAUDE.md` "Using This Design System in Other Apps" for full setup instructions.
```

- [ ] **Step 3: Commit**

```bash
git add CLAUDE.md COMPONENT_GUIDE.md
git commit -m "docs: add consumer usage guide for @cameleer/design-system package"
```

---

### Task 8: Push to Gitea and verify CI

**Files:**
- None (git operations only)

- [ ] **Step 1: Push all commits to Gitea**

```bash
git push -u origin main
```

- [ ] **Step 2: Check CI status**

Use the Gitea MCP server or visit `https://gitea.siegeln.net/cameleer/design-system/actions` to monitor the workflow run. The workflow should:
1. Install deps
2. Run tests
3. Build the library
4. Publish a snapshot version with the `dev` tag

- [ ] **Step 3: Verify the package is published**

Check the Gitea package registry at `https://gitea.siegeln.net/cameleer/-/packages`. The `@cameleer/design-system` package should appear with a `0.0.0-snapshot.*` version tagged as `dev`.

If the workflow fails, check the job logs via the Gitea MCP server's `actions_run_read` tool for diagnostics.
