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 index af52607..dcd2551 100644 --- a/docs/superpowers/specs/2026-03-18-design-system-packaging-design.md +++ b/docs/superpowers/specs/2026-03-18-design-system-packaging-design.md @@ -4,6 +4,7 @@ **Status:** Approved **Package:** `@cameleer/design-system` **Registry:** Gitea npm registry at `gitea.siegeln.net` +**Repository:** `https://gitea.siegeln.net/cameleer/design-system` ## Goal @@ -25,9 +26,12 @@ Package the Cameleer3 design system as a reusable npm library so other React app ## 1. Library Entry Point -New file `src/design-system/index.ts` — the single public API: +New file `src/design-system/index.ts` — the single public API. It must import global CSS at the top so that `tokens.css` and `reset.css` are included in the bundled `dist/style.css`: ```ts +import './tokens.css' +import './reset.css' + export * from './primitives' export * from './composites' export * from './layout' @@ -38,7 +42,9 @@ 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: +Without the CSS imports, all `var(--*)` tokens used in component CSS Modules would resolve to nothing in consuming apps. + +Consumers import the bundled CSS once at their app root: ```ts import '@cameleer/design-system/style.css' @@ -51,9 +57,13 @@ 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` +- **CSS Modules scoping:** `cameleer_[name]_[local]_[hash:5]` — debuggable in consumer devtools, unique enough to avoid collisions - **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"` +- **New devDependency:** `vite-plugin-dts` must be installed + +`tsconfig.node.json` must be updated to include `vite.lib.config.ts`. Output structure: @@ -78,13 +88,20 @@ Updates to `package.json`: "types": "./dist/index.d.ts", "exports": { ".": { - "import": "./dist/index.es.js", - "types": "./dist/index.d.ts" + "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" + }, "peerDependencies": { "react": "^19.0.0", "react-dom": "^19.0.0", @@ -94,6 +111,8 @@ Updates to `package.json`: ``` - `"private": true` is removed +- `"types"` comes first in the exports conditions (TypeScript resolution requirement) +- `publishConfig` ensures `npm publish` targets the Gitea registry, not npmjs.org - Existing `scripts`, `dependencies`, and `devDependencies` remain for the app build - `peerDependencies` tells consumers what to provide @@ -111,7 +130,7 @@ Workflow at `.gitea/workflows/publish.yml`: 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` +6. Configure `.npmrc` with scoped registry + auth token 7. `npm publish --tag ` **Runner:** ARM64 with `node:22-bookworm-slim` container image. @@ -145,7 +164,10 @@ jobs: 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 + cat > .npmrc << 'NPMRC' + @cameleer:registry=https://gitea.siegeln.net/api/packages/cameleer/npm/ + //gitea.siegeln.net/api/packages/cameleer/npm/:_authToken=${{ secrets.REGISTRY_TOKEN }} + NPMRC npm publish --tag "$TAG" ``` @@ -160,6 +182,8 @@ In any consuming app (e.g., `cameleer3-server/ui`): //gitea.siegeln.net/api/packages/cameleer/npm/:_authToken=${GITEA_TOKEN} ``` +Note: The consuming app's CI pipeline also needs this `.npmrc` and a `GITEA_TOKEN` secret to fetch the package during `npm ci`. + **2. Install:** ```bash @@ -170,7 +194,19 @@ npm install @cameleer/design-system@dev npm install @cameleer/design-system ``` -**3. Use:** +**3. Add fonts to `index.html`:** + +The design system uses DM Sans and JetBrains Mono via Google Fonts. These must be loaded by the consuming app since font `` tags are not part of the library output: + +```html + + + +``` + +Without these, `var(--font-body)` and `var(--font-mono)` will fall back to `system-ui` / `monospace`. + +**4. Use:** ```tsx // Import styles once at app root @@ -185,7 +221,8 @@ import { Button, Input, Modal, AppShell, ThemeProvider } from '@cameleer/design- Update `CLAUDE.md` and `COMPONENT_GUIDE.md` in this repo with: - The package name and registry URL -- How consuming apps should configure `.npmrc` +- How consuming apps should configure `.npmrc` (including CI) +- Font loading requirement (Google Fonts link) - Import patterns for consumers (`@cameleer/design-system` instead of relative paths) - Note that `style.css` must be imported once at the app root