Covers Vite library mode build, Gitea npm registry publishing, snapshot + tag-based versioning, and consumer setup. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
5.6 KiB
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:
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:
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-dtsgeneratesdist/index.d.tswith 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:
{
"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": trueis removed- Existing
scripts,dependencies, anddevDependenciesremain for the app build peerDependenciestells 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.<YYYYMMDD>.<short-sha>) withdevdist-tag - Push tag
v*→ publish stable release (e.g.,1.0.0) withlatestdist-tag
Steps:
- Checkout at ref
npm ci(install deps)npx vitest run(gate: don't publish broken code)npm run build:lib(build the library)- Determine version from tag or generate snapshot version
- Configure
.npmrcwithREGISTRY_TOKEN npm publish --tag <dev|latest>
Runner: ARM64 with node:22-bookworm-slim container image.
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:
# During development (snapshot builds)
npm install @cameleer/design-system@dev
# For stable releases (later)
npm install @cameleer/design-system
3. Use:
// 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-systeminstead of relative paths) - Note that
style.cssmust be imported once at the app root
This ensures other AI agents working on consuming Cameleer apps understand how to use the design system.