Frontend: Design system foundation — Tailwind 4 theme, CSS tokens, fonts #16

Closed
opened 2026-04-02 11:25:58 +02:00 by marcel · 2 comments
Owner

Summary

Set up the design system foundation that all screens depend on. This is the single source of truth for every color, font, spacing token, and component pattern in the app.

Stack

  • Tailwind 4 with CSS-first @theme configuration (no tailwind.config.js)
  • CSS custom properties layered: base → semantic → component tokens

Fonts

Token Family Usage
--font-display Fraunces Display headings
--font-sans DM Sans Body text, UI elements
--font-mono DM Mono Code, token display

Constraint: Never use font-weight above 600. Never use font-style: italic outside Fraunces.

Color Tokens

Neutrals

  • --color-page (#FAFAF7), --color-surface (#F5F4EE), --color-subtle (#EDECEA)
  • --color-border (#D8D7D0)
  • --color-text (#1C1C18), --color-text-muted (#6B6A63)

Green scale

  • --green-tint (#E8F5EA), --green-light (#AEDCB0), --green (#3D8C4A), --green-dark (#2E6E39), --green-deeper (#1E4A26)

Yellow scale

  • --yellow-tint (#FDF6D8), --yellow-light (#F9E08A), --yellow (#F2C12E), --yellow-dark (#C49610), --yellow-text (#8A6800)

Status

  • --color-error (#DC4C3E)

Spacing

8px base grid, 4px half-step:

  • --space-1 (4px) through --space-20 (80px)

Radii

  • --radius-xs (2px), --radius-sm (4px), --radius-md (6px, default), --radius-lg (10px), --radius-xl (16px), --radius-full (9999px)

Constraint: Default border-radius is --radius-md (6px), NOT --radius-lg.

Elevation

Flat by default. Shadows only on interactive surfaces:

  • --shadow-card: 0 1px 3px rgba(28,28,24,.06), 0 1px 2px rgba(28,28,24,.04)
  • --shadow-raised: 0 4px 12px rgba(28,28,24,.08), 0 2px 4px rgba(28,28,24,.04)
  • --shadow-overlay: 0 8px 32px rgba(28,28,24,.12), 0 2px 8px rgba(28,28,24,.06)

Button tokens

All buttons: 13px, weight 500, tracking 0.04em, font-sans.

Acceptance Criteria

  • Tailwind 4 @theme block defines all tokens above
  • CSS custom properties are available globally
  • Fonts loaded (Fraunces, DM Sans, DM Mono)
  • WCAG 2.2 AA contrast ratios verified (4.5:1 normal text, 3:1 large text)
## Summary Set up the design system foundation that all screens depend on. This is the single source of truth for every color, font, spacing token, and component pattern in the app. ## Stack - **Tailwind 4** with CSS-first `@theme` configuration (no `tailwind.config.js`) - **CSS custom properties** layered: base → semantic → component tokens ## Fonts | Token | Family | Usage | |-------|--------|-------| | `--font-display` | Fraunces | Display headings | | `--font-sans` | DM Sans | Body text, UI elements | | `--font-mono` | DM Mono | Code, token display | **Constraint:** Never use `font-weight` above 600. Never use `font-style: italic` outside Fraunces. ## Color Tokens ### Neutrals - `--color-page` (#FAFAF7), `--color-surface` (#F5F4EE), `--color-subtle` (#EDECEA) - `--color-border` (#D8D7D0) - `--color-text` (#1C1C18), `--color-text-muted` (#6B6A63) ### Green scale - `--green-tint` (#E8F5EA), `--green-light` (#AEDCB0), `--green` (#3D8C4A), `--green-dark` (#2E6E39), `--green-deeper` (#1E4A26) ### Yellow scale - `--yellow-tint` (#FDF6D8), `--yellow-light` (#F9E08A), `--yellow` (#F2C12E), `--yellow-dark` (#C49610), `--yellow-text` (#8A6800) ### Status - `--color-error` (#DC4C3E) ## Spacing 8px base grid, 4px half-step: - `--space-1` (4px) through `--space-20` (80px) ## Radii - `--radius-xs` (2px), `--radius-sm` (4px), `--radius-md` (6px, **default**), `--radius-lg` (10px), `--radius-xl` (16px), `--radius-full` (9999px) **Constraint:** Default `border-radius` is `--radius-md` (6px), NOT `--radius-lg`. ## Elevation Flat by default. Shadows only on interactive surfaces: - `--shadow-card`: `0 1px 3px rgba(28,28,24,.06), 0 1px 2px rgba(28,28,24,.04)` - `--shadow-raised`: `0 4px 12px rgba(28,28,24,.08), 0 2px 4px rgba(28,28,24,.04)` - `--shadow-overlay`: `0 8px 32px rgba(28,28,24,.12), 0 2px 8px rgba(28,28,24,.06)` ## Button tokens All buttons: 13px, weight 500, tracking 0.04em, `font-sans`. ## Acceptance Criteria - [ ] Tailwind 4 `@theme` block defines all tokens above - [ ] CSS custom properties are available globally - [ ] Fonts loaded (Fraunces, DM Sans, DM Mono) - [ ] WCAG 2.2 AA contrast ratios verified (4.5:1 normal text, 3:1 large text)
marcel added the kind/uipriority/high labels 2026-04-02 11:29:46 +02:00
Author
Owner

Spec references: All journey specs in specs/frontend/ depend on this design system. See any j*.html file for token usage examples.

**Spec references:** All journey specs in `specs/frontend/` depend on this design system. See any `j*.html` file for token usage examples.
Author
Owner

Implemented on feat/issue-16-design-system

What was built

src/app.html — Google Fonts loaded via preconnect + stylesheet link (Fraunces, DM Sans, DM Mono).

src/app.css — Tailwind 4 @theme block with all tokens:

  • Neutrals: --color-page/surface/subtle/border/text/text-muted
  • Green, yellow, blue, purple, orange scales (blue/purple/orange added — used throughout journey specs)
  • Status: --color-error
  • Spacing: --space-1 (4px) … --space-20 (80px)
  • Radii: --radius-xs--radius-full
  • Shadows: --shadow-card/raised/overlay
  • Button base tokens: --btn-font-size/font-weight/letter-spacing

WCAG finding

--green (#3D8C4A) fails AA with white text (4.16:1 < 4.5:1). Button backgrounds now use --green-dark (#2E6E39) which gives 6.2:1. --green remains in the palette for non-text use (borders, tints, icons). This decision is documented in a comment on the token and in the contrast test.

Tests

  • src/lib/design-system/contrast.test.ts — 4 WCAG AA contrast assertions
  • src/lib/design-system/tokens.test.ts — 35 token completeness assertions (one per required token)

All 39 tests pass. npm run check clean.

Commits

  • 7c8d725 test(design-system): assert WCAG 2.2 AA contrast for key color pairs
  • 0a2ef75 feat(design-system): add Tailwind 4 @theme tokens, fonts, and completeness tests
## Implemented on `feat/issue-16-design-system` ### What was built **`src/app.html`** — Google Fonts loaded via preconnect + stylesheet link (Fraunces, DM Sans, DM Mono). **`src/app.css`** — Tailwind 4 `@theme` block with all tokens: - Neutrals: `--color-page/surface/subtle/border/text/text-muted` - Green, yellow, blue, purple, orange scales (blue/purple/orange added — used throughout journey specs) - Status: `--color-error` - Spacing: `--space-1` (4px) … `--space-20` (80px) - Radii: `--radius-xs` … `--radius-full` - Shadows: `--shadow-card/raised/overlay` - Button base tokens: `--btn-font-size/font-weight/letter-spacing` ### WCAG finding **`--green` (#3D8C4A) fails AA with white text (4.16:1 < 4.5:1).** Button backgrounds now use `--green-dark` (#2E6E39) which gives 6.2:1. `--green` remains in the palette for non-text use (borders, tints, icons). This decision is documented in a comment on the token and in the contrast test. ### Tests - `src/lib/design-system/contrast.test.ts` — 4 WCAG AA contrast assertions - `src/lib/design-system/tokens.test.ts` — 35 token completeness assertions (one per required token) All 39 tests pass. `npm run check` clean. ### Commits - `7c8d725` test(design-system): assert WCAG 2.2 AA contrast for key color pairs - `0a2ef75` feat(design-system): add Tailwind 4 @theme tokens, fonts, and completeness tests
Sign in to join this conversation.