Skip to content

Design Tokens

The shared vocabulary for every V2 screen. Tailwind-mappable; explicit overrides flagged. Owned by Mira (UX/UI Designer); consumed by Juno (Frontend) when wiring tailwind.config.js. Cross-cuts: every page in design, i18n architecture.

Breakpoints

Mobile-first. Specs target three sizes:

Token Width Reference device Layout intent
sm (default) 375 px iPhone SE / 13 mini, one-handed phone Single column, full-width primary actions, large hit targets.
md 768 px Tablet portrait, small-screen laptop Two-column where the form / list pair benefits from a sidebar.
lg 1280 px Desktop laptop Three-zone layout for dashboard (queue / activity / quick-actions). Forms remain max ~720 px wide; no edge-to-edge text.

Tailwind defaults already match (sm:640, md:768, lg:1024). We deliberately choose md 768 px and lg 1280 px as our primary tablet + desktop breakpoints; the 640 px breakpoint is unused. Tailwind config note for Juno:

// tailwind.config.js — overrides only
theme: {
  screens: {
    md: '768px',
    lg: '1280px',
  },
}

Type scale

Stefan strings on a phone with strong daylight and reading-age glasses; readability beats density. Base is 16 px (Tailwind default). The scale is conservative — no microcopy below 12 px.

Role Token Size / line height Weight Use
Display text-display 28 / 36 px 700 Receipt total (top-left quadrant per ADR-0002). Reserved for the receipt; UI does not use it.
H1 text-h1 24 / 32 px 700 Screen title (one per page).
H2 text-h2 20 / 28 px 600 Section header inside a screen.
H3 text-h3 18 / 24 px 600 Card title (e.g. an order row's client name).
Body text-body 16 / 24 px 400 Default paragraph + form inputs.
Small text-small 14 / 20 px 400 Secondary metadata (e.g. "Strung 2026-04-30").
Tiny text-tiny 12 / 16 px 500 Tag / badge labels (state badges, BYO marker). Floor of the scale.

Font family: Inter as primary (system fallback system-ui, sans-serif). Inter's tabular-numerals variant (font-feature-settings: "tnum") is used for tension, prices, and dates so columns align without manual fudging.

Color palette

One stringer brand + neutral greys + state colors. The palette is intentionally narrow — Stefan's app is a productivity tool, not a marketing site.

Brand

Role Hex Tailwind name Notes
Primary #3949ab indigo-700 Matches mkdocs theme; primary buttons, focus rings, link color.
Primary-hover #303f9f indigo-800 Hover/active state of primary.
Primary-soft #e8eaf6 indigo-50 Subtle background tint (e.g. selected list-row).

Neutrals

Role Hex Tailwind name
Ink (body text) #0f172a slate-900
Ink-muted #475569 slate-600
Border #e2e8f0 slate-200
Surface #ffffff white
Surface-alt #f8fafc slate-50 (zebra rows / dashboard cards)

State

Role Hex Tailwind name Use
Success #15803d green-700 "Strung" badge background; save-success toast.
Warning #b45309 amber-700 "Ordered" badge (still pending stringing); "due today" markers.
Danger #b91c1c red-700 Error toast / inline validation.
Info #0369a1 sky-700 Info banners (e.g. "Receipt re-emitted").

Contrast. Every text/background pair on the screens specced in Round 1 meets WCAG 2.1 AA (4.5:1 for body, 3:1 for large text). Indigo-700 on white = 8.59:1; slate-900 on white = 16.1:1; white on indigo-700 = 8.59:1. State badges use white text on the listed hex; all pass AA.

Dark mode

Out of scope for Round 1. mkdocs site supports dark mode (palette toggle in mkdocs.yml); the V2 app's dark mode is a follow-up round once light is locked.

Spacing scale

Tailwind's default 4-px grid. We pin the commonly-used values so specs stay consistent:

Token Pixels Use
space-1 4 px Hair-line padding (badge interior).
space-2 8 px Tight inline gap (icon+label).
space-3 12 px Default form-field interior padding (top/bottom).
space-4 16 px Default content padding (card edge, page edge on 375 px).
space-5 20 px Form-field-to-field vertical gap.
space-6 24 px Section-to-section gap.
space-8 32 px Major section break (dashboard "Today" → "Recent").
space-12 48 px Page-top margin (below header) on md+ viewports.

Hit targets + touch ergonomics

Stefan operates the phone with one thumb at the stringing table; hit targets must survive damp / chalky fingers.

  • Minimum 44 × 44 px for any interactive element (per WCAG 2.5.5 + iOS HIG). The ±1 kg tension nudge buttons and the BYO toggles are the most-pressed elements; both are sized 48 × 48 px to give margin.
  • Vertical spacing between adjacent tap targets ≥ 8 px to prevent mis-taps.
  • Primary action button is full-width on sm, max 320 px wide on md+, fixed-height 48 px.

Border-radius scale

Token Pixels Use
rounded-sm 4 px Badges, small chips.
rounded 8 px Form fields, buttons.
rounded-lg 12 px Cards (dashboard order rows).
rounded-full 9999 px Avatars, FAB (if used).

Elevation / shadows

Used sparingly — flat surfaces with borders dominate. Two levels:

Token Box-shadow Use
shadow-card 0 1px 2px rgba(15, 23, 42, 0.06), 0 1px 3px rgba(15, 23, 42, 0.10) Dashboard cards.
shadow-modal 0 10px 30px rgba(15, 23, 42, 0.18) Confirmation dialogs (lifecycle date clears, admin-override).

Iconography

  • Library: Lucide (MIT, Tailwind-friendly, comprehensive). Stefan can swap in custom SVGs per surface if needed.
  • Stroke width: 1.5 px at 24 px size (the Lucide default).
  • Default icon size: 20 px inline with body, 24 px in primary buttons.
  • Color: inherits the surrounding text color; explicit text-slate-600 on neutral icons, text-indigo-700 on primary-action icons.

Motion

Conservative. Every animation < 200 ms; no parallax, no easing show-off. Three motion tokens:

Token Duration Use
motion-fast 100 ms Hover/active state changes, focus rings, BYO toggle flip.
motion-default 200 ms Page-section reveals, accordion expand/collapse, HTMX swap fades.
motion-out 150 ms Toast dismiss, modal exit.

prefers-reduced-motion: reduce reduces all of the above to 0 ms (instant) per WCAG 2.3.3 — Tailwind's motion-reduce: modifier covers it.

i18n affordance

Every UI string in a spec is one of three types:

  1. Catalogue-driven ({% trans %}) — translatable. Default. Specs render the EN form; the catalogue key is implied by the string ("Add Stringjob" → dashboard.add_stringjob).
  2. Data — pulled from the database (client name, racket model, comments). Never translated. See i18n architecture — Catalogue / data localization.
  3. Format — numbers, dates, currency. Babel's format_currency() and format_date(); the default for V2 is ISO YYYY-MM-DD and CHF X.XX in both locales (per receipt-content OQ-R-2).

Specs flag any string whose meaning shifts between locales (e.g. button labels where DE is materially longer than EN — common — get reserved width).

Where this maps in code

  • app/static/css/tokens.css — CSS custom properties (--color-primary, --space-4, etc.). Source of truth at runtime; Tailwind config reads these via theme.extend.
  • tailwind.config.js — maps the names above to Tailwind's theme.extend.colors, theme.extend.spacing, theme.extend.fontSize.
  • app/templates/_macros/icons.html — Lucide SVG sprite + a {% icon name, size %} macro Juno wires.

Pax + Juno own the wiring; Mira owns the values.

Cross-references