- dist/tokens.css: canonical light + @media dark semantic-token palette
extracted from inventory (cross-checked against authd and buchinese).
Covers --fg, --fg-muted, --bg, --bg-elevated, --surface, --accent*,
--danger*, --border*, --input-*, --warning, primitive --gray-* scale.
- dist/base.css: `input, textarea, select { font-size: 16px; }` prevents
iOS Safari auto-zoom on focus.
- tests/tokens.test.ts: vitest — token presence in both light and dark
blocks; WCAG AA contrast (>=4.5:1 body, >=3.0:1 UI) via inline
hex-to-luminance math. All 10 tests green.
- tests/base.test.ts: vitest — asserts 16px rule covers all three selectors.
- .gitea/workflows/release.yml: tag-triggered CI — npm ci + npm test,
CHANGELOG version gate, dist/scripts security grep, Gitea release
artifact upload.
- README.md: four-step consumer integration guide with copy-pasteable
snippets (package.json dep, Dockerfile cp, HTML link tags, app CSS).
- CHANGELOG.md: v0.1.0 entry.
npm audit --omit=dev: 0 vulnerabilities
Closes #1
129 lines
3.5 KiB
Markdown
129 lines
3.5 KiB
Markdown
# @bchen/ui
|
|
|
|
Shared CSS tokens and UI components for the bchen.dev fleet.
|
|
|
|
## What's included
|
|
|
|
| Export | Contents |
|
|
|--------|----------|
|
|
| `@bchen/ui/tokens.css` | Semantic-token palette — light mode `:root` block + `@media (prefers-color-scheme: dark)` override |
|
|
| `@bchen/ui/base.css` | `input, textarea, select { font-size: 16px; }` — prevents iOS Safari auto-zoom on focus |
|
|
|
|
## Consumer integration (four steps)
|
|
|
|
### 1. Add the dependency
|
|
|
|
```json
|
|
// package.json
|
|
"dependencies": {
|
|
"@bchen/ui": "git+https://gitea.bchen.dev/brendan/bchen-ui.git#v0.1.0"
|
|
}
|
|
```
|
|
|
|
Then run `npm install` (requires `git` in your build image — see Step 2).
|
|
|
|
### 2. Dockerfile — copy CSS to your public dir
|
|
|
|
After `npm ci`, copy the dist files into your served static directory so they're available at runtime without bundler involvement:
|
|
|
|
```dockerfile
|
|
FROM node:24-alpine
|
|
RUN apk add --no-cache python3 make g++ git
|
|
|
|
WORKDIR /app
|
|
COPY package*.json ./
|
|
RUN npm ci
|
|
|
|
# Copy @bchen/ui dist files into public vendor dir
|
|
RUN mkdir -p public/vendor/@bchen/ui && \
|
|
cp -r node_modules/@bchen/ui/dist/. public/vendor/@bchen/ui/
|
|
|
|
COPY . .
|
|
```
|
|
|
|
### 3. HTML — link vendor CSS before your app stylesheet
|
|
|
|
```html
|
|
<head>
|
|
<!-- vendor CSS first -->
|
|
<link rel="stylesheet" href="/vendor/@bchen/ui/tokens.css">
|
|
<link rel="stylesheet" href="/vendor/@bchen/ui/base.css">
|
|
|
|
<!-- your app CSS after -->
|
|
<link rel="stylesheet" href="/style.css">
|
|
</head>
|
|
```
|
|
|
|
### 4. App CSS — your overrides come after vendor
|
|
|
|
Your `style.css` can reference the tokens directly:
|
|
|
|
```css
|
|
/* style.css — loaded after vendor, so tokens are already defined */
|
|
body {
|
|
background: var(--bg);
|
|
color: var(--fg);
|
|
font-family: var(--font);
|
|
}
|
|
|
|
a { color: var(--accent); }
|
|
.muted { color: var(--fg-muted); }
|
|
```
|
|
|
|
Dark mode is automatic — no JavaScript required. The `tokens.css` `@media (prefers-color-scheme: dark)` block overrides the palette based on the OS setting.
|
|
|
|
## Token reference
|
|
|
|
### Primitive palette (resolve base values)
|
|
|
|
| Token | Light | Dark |
|
|
|-------|-------|------|
|
|
| `--black` | `#000` | — |
|
|
| `--white` | `#fff` | — |
|
|
| `--gray-50` | `#fafafa` | — |
|
|
| `--gray-100` | `#f0f0f0` | — |
|
|
| `--gray-200` | `#e0e0e0` | — |
|
|
| `--gray-400` | `#999` | — |
|
|
| `--gray-600` | `#555` | — |
|
|
| `--red` | `#c00` | — |
|
|
|
|
### Semantic color tokens
|
|
|
|
| Token | Light | Dark |
|
|
|-------|-------|------|
|
|
| `--fg` | `#000` | `#e5e7eb` |
|
|
| `--fg-muted` | `#555` | `#9ca3af` |
|
|
| `--bg` | `#fff` | `#0f1115` |
|
|
| `--bg-elevated` | `#fafafa` | `#1a1d23` |
|
|
| `--surface` | `#fff` | `#0f1115` |
|
|
| `--accent` | `#000` | `#e5e7eb` |
|
|
| `--accent-fg` | `#fff` | `#0f1115` |
|
|
| `--accent-hover` | `#555` | `#cbd5e1` |
|
|
| `--danger` | `#c00` | `#f87171` |
|
|
| `--danger-fg` | `#fff` | `#0f1115` |
|
|
| `--danger-bg` | `#fff5f5` | `rgba(248,113,113,0.12)` |
|
|
| `--warning` | `#555` | — |
|
|
| `--border-color` | `#e0e0e0` | `#374151` |
|
|
| `--border-strong` | `#000` | `#6b7280` |
|
|
| `--border` | `1px solid #000` | `1px solid #6b7280` |
|
|
|
|
### Input tokens
|
|
|
|
| Token | Light | Dark |
|
|
|-------|-------|------|
|
|
| `--input-bg` | `#fff` | `#1a1d23` |
|
|
| `--input-fg` | `#000` | `#e5e7eb` |
|
|
| `--input-border` | `#000` | `#6b7280` |
|
|
| `--input-bg-focus` | `#fafafa` | `#0f1115` |
|
|
|
|
## WCAG contrast guarantees
|
|
|
|
All values in `tokens.css` are tested (see `tests/tokens.test.ts`) to meet:
|
|
|
|
- **AA body text (≥ 4.5:1):** `--fg/--bg`, `--fg-muted/--bg` in both light and dark
|
|
- **UI elements (≥ 3.0:1):** `--border-strong/--bg` in both light and dark
|
|
|
|
## Changelog
|
|
|
|
See [CHANGELOG.md](./CHANGELOG.md).
|