fix: separate hover/focus-visible on .nav-link; restore outline ring for keyboard focus (WCAG 2.4.7)
@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
// 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:
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
<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:
/* 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/--bgin both light and dark - UI elements (≥ 3.0:1):
--border-strong/--bgin both light and dark
Changelog
See CHANGELOG.md.
Description
Shared CSS tokens + framework-less UI components for the bchen.dev fleet. Empty placeholder created during 2026-05-13 queue migration to host the v1 spec issue; first commit pending implementer pickup.
Languages
TypeScript
100%