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:
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 onmd+, 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-600on neutral icons,text-indigo-700on 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:
- Catalogue-driven (
{% trans %}) — translatable. Default. Specs render the EN form; the catalogue key is implied by the string ("Add Stringjob" →dashboard.add_stringjob). - Data — pulled from the database (client name, racket model, comments). Never translated. See i18n architecture — Catalogue / data localization.
- Format — numbers, dates, currency. Babel's
format_currency()andformat_date(); the default for V2 is ISOYYYY-MM-DDandCHF X.XXin 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 viatheme.extend.tailwind.config.js— maps the names above to Tailwind'stheme.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¶
- Architecture: i18n, ADR-0002 receipt PDF (uses the
text-display28 px scale). - Requirements: every Round-1 spec (add-stringjob, stringer-dashboard) references this page for token values.
- Accessibility: WCAG 2.1 AA targets pinned in Color palette and Hit targets.