SaaS Starter
Frontend

UseDeploy design system

The aesthetic point of view, where tokens and primitives live, and the rules every page follows.

The frontend has one design system — internally referred to as UseDeploy. It's a refined-minimalism palette descended from Vercel's marketing surfaces and the UseDeploy studio aesthetic: pure black base, paper-white foreground, hairline white-on-white borders at low alpha, a violet ambient glow as the only chromatic signature, and emerald reserved for status dots.

It is not a light theme dressed up dark. It is dark-first. The .dark class exists only so the future theme-switcher (#147) can ship without breaking surfaces — both :root and .dark resolve to the same palette today.

Where it lives

  • Tokensapps/client/app/globals.css. OKLCH variables in :root, mapped into Tailwind via @theme inline { --color-*: var(--*) }.
  • Primitivesapps/client/components/brand/. BrandMark, VersionChip, GridGlowBackground, NoiseOverlay, MonoLabel, AuthCard, BrandCard. Re-exported from @/components/brand.
  • Fonts — Geist Sans + Geist Mono, loaded once in apps/client/app/layout.tsx and exposed as --font-geist-sans / --font-geist-mono for the whole app.
  • Custom utilitiesapps/client/app/globals.css @layer utilities. label-mono, shadow-soft, shadow-soft-lg, card-premium, card-lift.

Why it's centralized

Before the migration tracked in Tasky #145–#151, the codebase ran two themes side by side:

  • The dashboard used a light "Brubank Ocean" palette.
  • Landing and auth used a hand-coded bg-black Vercel-derived dark theme.

Three duplicated wrapper definitions, three drifting palettes. We had pages whose bg-white card on bg-white card on bg-black hero looked plainly broken because no surface shared tokens.

The fix was the rule that now lives in CLAUDE.md: never declare bg-black / text-white / hardcoded oklch(...) literals in components. Use semantic tokens (bg-background, text-foreground, border-border, bg-card, text-muted-foreground, text-accent) and the brand primitives.

The four rules

  1. Use semantic tokens, never raw colors. The full table is on the tokens page.
  2. Don't re-load Geist per component with next/font/google — the root layout owns it.
  3. Don't inline the UseDeploy wordmark, the grid+glow background, or the version chip. Import from @/components/brand.
  4. Auth pages wrap content in <AuthCard>. The (auth)/layout.tsx owns the page chrome; pages own only their form.

Do / don't

// DON'T — hardcoded colors, drifts off the theme.
<div className="bg-black text-white border border-white/10">

// DO — token-driven, dark/light future-proof.
<div className="bg-background text-foreground border border-border">
// DON'T — re-rolls the wordmark; will diverge.
<span className="font-semibold tracking-tight">UseDeploy</span>

// DO — imports the canonical primitive.
import { BrandMark } from '@/components/brand';
<BrandMark />

See conventions for the full list.

On this page