Files
2026-10-19 15:39:46 +00:00
..
2026-10-19 15:25:27 +00:00
2026-10-19 15:25:27 +00:00
2026-10-19 15:39:46 +00:00
2026-10-19 15:39:46 +00:00
2026-10-19 16:04:17 +02:00
2026-10-19 16:04:17 +02:00
2026-10-19 15:39:46 +00:00
2026-10-19 15:25:27 +00:00
2026-10-19 16:04:17 +02:00
2026-10-19 16:04:17 +02:00
2026-10-19 15:25:27 +00:00
2026-10-19 16:04:17 +02:00
2026-10-19 16:04:17 +02:00
2026-10-19 16:04:17 +02:00
2026-10-19 16:04:17 +02:00
2026-10-19 16:04:17 +02:00
2026-10-19 16:04:17 +02:00
2026-10-19 16:04:17 +02:00
2026-10-19 16:04:17 +02:00

Northstar Design System

A token-driven design system showcase built for AI-assisted development workflows. React 19 · Vite 8 · TypeScript (strict) · Vitest · oxlint.

Quick start

npm install
npm run dev        # dev server on 0.0.0.0:5173

Auth and the API proxy

/api is proxied to the Go backend (API_URL, default http://localhost:8080) so the browser only ever sees the vite origin — on localhost or *.preview.k6n.net. That keeps OIDC redirects and the session cookie first-party: the topbar's Sign in and the overview Session card go through /api/auth/login, the issuer returns to /api/auth/callback on the same host, and useSession() (src/auth) polls /api/auth/session. The proxy forwards X-Forwarded-Host/X-Forwarded-Proto so the backend registers the right redirect URI per host. See the backend README for the server side.

The quality gate

npm run check

One command, four gates, in order:

  1. oxlint — lint over every source file.
  2. scripts/check-tokens.mjs — the token contract: bans hardcoded colors (hex / named / rgb() / hsl() / oklch() / lab()), px font sizes and undefined var(--x) references — in both CSS files and inline style={{ … }} objects. Values may span lines; comments are ignored.
  3. vitest run — unit + component tests (tests/).
  4. tsc -b && vite build — strict typecheck, then production build.

CI runs the same gate on every push and PR (.github/workflows/ci.yml).

Design tokens

src/tokens.css is the single source of truth. Every color, font size, spacing, radius and shadow goes through a var(--token) — components never hardcode values. Themes are plain CSS: :root[data-theme='light'] / .light and :root[data-theme='dark'] / .dark, with a pre-paint script in index.html that applies the saved (or OS-preferred) theme before first paint.

The global density knob is --root-font-size — one line scales the whole app.

Layout

src/
  tokens.css        ← lab() design tokens, dark & light themes
  index.css         ← fonts, token import, base resets
  utils.css         ← token-compliant utility classes
  App.css           ← application showcase styles
  app/              ← AppStateProvider + useAppState(), and the route table (wouter)
  auth/             ← useSession() + UserMenu / SessionCard (OIDC identity via the API proxy)
  pages/            ← one page component per route
  components/
    ui/             ← composable primitives (Overlay, Dialog, Sheet, Tabs, Alert, …)
    icon-registry.ts← the one icon name → lucide component map
    examples/       ← reusable patterns (MetricsDashboard, SettingsPanel, …)
    dev/            ← ErrorBoundary + dev error sink (#dev-errors)
  utils/            ← cn(), useTimeout()
tests/              ← vitest suites (jsdom)
scripts/
  check-tokens.mjs  ← token contract guard

For agents

Read AGENTS.md before changing anything — it documents the token contract, the overlay/icon registries, and the parallel-subagent rules.