docs: add design spec for design system packaging
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>
This commit is contained in:
@@ -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.<YYYYMMDD>.<short-sha>`) 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 <dev|latest>`
|
||||||
|
|
||||||
|
**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.
|
||||||
Reference in New Issue
Block a user