Design system — WalkRPG
Design system — WalkRPG
Foundation document for all UI decisions. Informs mobile implementation (phase 11+) and wiki component styling. Every wireframe page references this document for token names.
1. Color tokens
1.1 Region palette (canonical — from data/src/content/regions/*.ts)
These six colors are canon from phase 5b. Do not create replacements without a D-level decision via game-director.
| Token | Hex | Region | Character |
|---|---|---|---|
--color-region-plenny | #c9a04a | Plenny / Centre | Bureaucratic gold, parchment warmth |
--color-region-frostlands | #7fb8d4 | Frostlands / North | Ice-blue, crystalline |
--color-region-passage-arc | #9b7fc4 | Arc of Passage / NE-Bridge | Violet, hybrid, contested |
--color-region-mglica | #6b8f7a | Mglica / East | Fog-green, muted, dreaming |
--color-region-beber | #c47a4a | Beber / West | Ochre-rust, bestial, savanna |
--color-region-targosie | #d65a5a | Targosie / South | Coral-red, mercantile, urgent |
1.2 Neutral base palette
Dark default. The neutral ramp is warm (slight golden cast) to echo the Plenny parchment tone without competing with region accents.
| Token | Hex | Usage |
|---|---|---|
--color-bg | #0f0e0c | App background (near-black, warm) |
--color-surface | #1a1916 | Cards, panels, bottom sheet base |
--color-surface-variant | #252320 | Input fields, pressed states, table rows |
--color-surface-elevated | #302e2b | Modals, popovers, top-of-stack drawers |
--color-on-surface | #f0ece3 | Primary text on dark surfaces |
--color-on-surface-muted | #a09880 | Secondary text, hints, placeholders |
--color-border | #3a3730 | Dividers, strokes |
--color-border-subtle | #252320 | Inset card borders, barely visible |
1.3 State colors
| Token | Hex | Usage |
|---|---|---|
--color-success | #4caf7d | Streak active, quest complete, accepted steps |
--color-warning | #e0a843 | Streak about to decay (1 day left), provisional state |
--color-error | #e05555 | Rejected steps, auth failure, destructive actions |
--color-info | #5b99c4 | Tooltips, informational banners |
--color-disabled | #4a4843 | Disabled buttons, locked content |
--color-on-disabled | #6b6760 | Text on disabled elements |
1.4 Faction accent colors
One accent per faction, used for reputation tier badges, faction strip cells, and faction-gated content borders.
| Token | Hex | Faction (EN handle) | PL name |
|---|---|---|---|
--color-faction-unfinished-guild | #c9a04a | Unfinished Guild | Cech Niedokończonych Wypraw |
--color-faction-practical-academy | #5b99c4 | Practical Academy | Akademia Praktyczna |
--color-faction-quiet-lights | #9b7fc4 | Quiet Lights Brotherhood | Bractwo Cichych Świateł |
--color-faction-free-merchantry | #4caf7d | Free Merchantry | Wolnokupiectwo |
--color-faction-wild-path | #c47a4a | Wild Path | Dziki Trakt |
Note: Unfinished Guild shares #c9a04a with Plenny because the Guild IS Plenny’s dominant faction. On screens where both region and faction appear together, the region card uses the token by name (--color-region-plenny) and the faction badge uses its own (--color-faction-unfinished-guild). Visual overlap is intentional — tonal coherence.
1.5 Contrast audit (WCAG 2.2 AA)
Background for all checks: --color-bg = #0f0e0c.
| Foreground token | Hex | Against --color-bg | Against --color-surface | AA body (4.5:1) | AA large (3:1) |
|---|---|---|---|---|---|
--color-on-surface | #f0ece3 | ~16.8:1 | ~14.2:1 | PASS | PASS |
--color-on-surface-muted | #a09880 | ~6.1:1 | ~5.1:1 | PASS | PASS |
--color-region-plenny | #c9a04a | ~6.9:1 | ~5.8:1 | PASS | PASS |
--color-region-frostlands | #7fb8d4 | ~7.8:1 | ~6.6:1 | PASS | PASS |
--color-region-passage-arc | #9b7fc4 | ~5.5:1 | ~4.6:1 | PASS | PASS |
--color-region-mglica | #6b8f7a | ~4.6:1 | ~3.9:1 | PASS (just) | PASS |
--color-region-beber | #c47a4a | ~4.9:1 | ~4.1:1 | PASS | PASS |
--color-region-targosie | #d65a5a | ~4.6:1 | ~3.9:1 | PASS (just) | PASS |
--color-success | #4caf7d | ~5.6:1 | ~4.7:1 | PASS | PASS |
--color-warning | #e0a843 | ~7.5:1 | ~6.3:1 | PASS | PASS |
--color-error | #e05555 | ~4.7:1 | ~3.9:1 | PASS (just) | PASS |
--color-info | #5b99c4 | ~5.8:1 | ~4.9:1 | PASS | PASS |
Warning for Mglica, Beber, Targosie region colors used as body text: these barely clear AA at 4.5:1. Use them only as accent strokes, badges, and large-text headings — not as body copy color on dark surfaces.
Light mode note: if a light mode is ever implemented, the full contrast table must be re-run against #f0ece3 backgrounds. The region accent palette was designed for dark-first — several colors will fail AA on white.
2. Typography scale
W-level decision: Typeface family is Literata (serif, variable font, Google Fonts, free OFL) for lore/display headings + Inter (sans-serif, variable font, Google Fonts, free OFL) for UI chrome. Both have full Polish diacritic support (ą ć ę ł ń ó ś ź ż and uppercase variants).
Why Literata for lore/display:
- Variable font (one file, multiple weights via font-variation-settings).
- Designed for dense reading at small sizes (e-reader heritage) — useful for map descriptions and quest text.
- Cartographic / parchment character at display size without being a novelty face.
- Google Fonts means no licensing cost at any scale.
Why Inter for chrome:
- Universally legible at small sizes with Polish diacritics clean.
- Designed for screen UI — consistent stem weights, generous x-height.
- Variable font — single file.
CEO open question (D-level): If a licensed “more characterful” display face becomes desirable for marketing assets (e.g. app store screenshots, loading screen), that would require a D-level decision with game-director before it enters the design system. Literata is the working choice — solid and free.
2.1 Scale
| Token | Typeface | Size (px) | Line height | Weight | Role |
|---|---|---|---|---|---|
--type-display | Literata | 32px | 1.2 | 700 | Region titles, keystone names on tree, onboarding headers |
--type-h1 | Literata | 24px | 1.25 | 600 | Screen titles |
--type-h2 | Inter | 18px | 1.3 | 600 | Section headings within screens |
--type-body-lg | Inter | 16px | 1.5 | 400 | Quest description, node description popover body |
--type-body | Inter | 14px | 1.5 | 400 | Standard UI text, labels, tab names |
--type-caption | Inter | 12px | 1.4 | 400 | Hints, timestamps, tree node micro-labels |
Minimum body font on-device: 14px. At 200% OS font scaling (WCAG 1.4.4), 14px becomes effectively 28px — layout must not overflow or clip at this size. See section 5 (a11y baseline) for compliance details.
3. Spacing scale
Base unit: 4px. All spacing values are multiples.
| Token | px | Usage examples |
|---|---|---|
--space-1 | 4px | Icon-to-label gap, micro-padding inside badges |
--space-2 | 8px | Inner padding of compact chips, badge inner horizontal |
--space-3 | 12px | List item vertical padding, tab bar inner vertical |
--space-4 | 16px | Card inner padding, modal horizontal margin |
--space-6 | 24px | Section gap within a screen, between quest pill and streak ribbon |
--space-8 | 32px | Between major sections, top/bottom of modal |
--space-12 | 48px | Minimum tap target size (also minimum interactive zone height) |
--space-16 | 64px | Bottom nav bar total height, large header zones |
Touch target baseline: All interactive elements — buttons, tabs, node cells in tree, quest pills, faction strip items — must present a minimum 48dp hit area. Visual size may be smaller (e.g. tree nodes rendered as 24dp circles), but the invisible tap target wraps to 48dp. This applies to both iOS (pt) and Android (dp).
4. Iconography stance
W-level decision: System icon vendor per platform + a set of named custom lore icons (to be authored in phase 11 visual asset sprint).
4.1 System icons (chrome)
| Platform | Library | Usage |
|---|---|---|
| iOS | SF Symbols 5 | Navigation, standard actions (back, share, settings, search, checkmark) |
| Android | Material Symbols (outlined weight) | Same roles on Android — matched icon semantics, not pixel-identical shapes |
System icons handle: tab navigation glyphs, action buttons, disclosure chevrons, close/dismiss, check states, loading spinners.
4.2 Custom lore icons (named, to be authored phase 11)
13 custom icons named here as contracts. Visual design deferred to phase 11.
| Handle | Description | Usage context |
|---|---|---|
icon.defining-image-fragment | Cech ring motif (18th-century surveyor’s hand, watermark weight) overlaid on a fragment of moved coastline; coastline drawn twice — warm-brown current line plus ghost-blue alpha offset curve per D-012 double-printed-lines technique; warm parchment background (#f0ece3); no central figure, no magical glow | Brand-kit primary mark — splash, loading screen, app icon, marketing thumbnail. Distinct from icon.cech-seal (faction badge / quest-issuer) and icon.compass-rose (navigation chrome); D-012 canon-locked |
icon.cech-seal | Wax seal with ring motif | Unfinished Guild faction badge, quest issuer marker |
icon.leak-shard | Crystallised magic fragment | Crafting currency, leak-related node modifiers |
icon.footprint | Single stylized walker footprint | Step count delta, energy unit icon |
icon.compass-rose | 4-point compass with Cech ring accent | Navigation fallback, map-related nodes |
icon.faction-academy | Flame-over-book | Practical Academy badge |
icon.faction-quiet-lights | Lantern with halo | Quiet Lights Brotherhood badge |
icon.faction-merchantry | Coin with guild mark | Free Merchantry badge |
icon.faction-wild-path | Bare-foot track in grass | Wild Path badge |
icon.keystone-lock | Padlock with Cech ring | Locked keystone state on tree |
icon.notable-node | Hexagon with inner diamond | Notable node type marker |
icon.mastery-pick | Three-pronged choice symbol | Mastery popover header |
icon.streak-flame | Stylized walking flame | Streak ribbon accent |
5. Accessibility baseline (walking-specific)
Every screen and flow must satisfy all six checks. Deviations require a documented waiver per screen.
5.1 One-handed use while walking
Rule: All primary actions reachable with one thumb in the bottom 60% of the screen. Destructive or secondary actions may live in the top 40%. The tree viewer (which requires 2D navigation) is the highest-risk screen — see tree-viewer wireframe for mitigations.
Check: Primary CTA on every screen is at bottom, inside the safe zone (below the fold, above the system nav bar). Bottom nav tabs are the primary navigation affordance.
5.2 Glance legibility (2-second comprehension)
Rule: The most critical piece of information on each screen must be readable in a 2-second glance without focusing. This means: large primary numeral or status indicator, high-contrast, centered or top-anchored.
Implementation per screen:
- Home: Streak count is the largest element after the region card header.
- Tree: Available points count is persistent at top-right. Current allocation state (how many points spent) is visible without scrolling.
- Post-walk sync: Step delta is the single dominant element (+N,NNN steps). Energy delta secondary.
- Onboarding: One instruction per screen. No multi-step copy on a single view.
5.3 Tap target minimum (48dp)
All interactive elements in the product are 48dp minimum hit area. See spacing section for the token (--space-12). Tree nodes are the exception: at small zoom levels, nodes render smaller than 48dp visually. The hit target must still be 48dp; overlap is resolved by nearest-node selection at small scales with a disambiguation popover if two nodes are within 48dp of each other.
5.4 Color contrast (WCAG 2.2 AA)
- Body text: 4.5:1 against background. (Met by
--color-on-surfaceand--color-on-surface-muted— see audit table.) - Large text (≥18px regular, ≥14px bold): 3:1 minimum. (All region accent colors pass at large size.)
- UI components (borders, focus rings, active states): 3:1 against adjacent surfaces.
- Focus ring: 2dp solid
--color-region-plenny(#c9a04a) against--color-surfaceyields ~5.8:1 — PASS.
5.5 Font scaling (200%)
Rule: No text must clip, truncate with ellipsis at primary-content level, or overflow container bounds at 200% OS font scale.
Implementation notes:
- All flex/grid layouts use
min-heightnot fixedheightfor text-bearing rows. - Button labels: single line only for actions up to 12 characters; longer actions wrap to two lines within the button (button height grows). PL labels average 30% longer than EN — design against PL lengths.
- Tab bar labels: at 200% they will almost certainly overflow a 4-tab bar. Mitigation: at 200% scale, tab bar hides text labels and shows icons only (a fallback layout must be designed and tested). See bilingual layout section for tab label character budgets.
- Tree node micro-labels hide at 200% — nodes use icon glyph only and tooltip popover on long-press.
5.6 Audio and haptic alternatives
Every primary visual feedback channel must have an audio or haptic companion:
| Visual feedback | Audio/haptic alt |
|---|---|
| Streak updated (day counter increments) | Haptic: medium impact + success tone |
| Step count delta appeared | Haptic: light impact |
| Node allocated | Haptic: medium impact |
| Keystone unlocked | Haptic: heavy impact + distinctive sound |
| Quest beat completed | Haptic: success pattern + chime |
| Error / rejected steps | Haptic: error pattern |
| Onboarding step advance | Haptic: light tick |
Audio and haptic are opt-out in settings, not opt-in. Players who cannot feel or hear can disable; default-on covers those who walk with eyes forward.
6. Bilingual layout rule
Player-facing strings exist in both en and pl. PL strings average 20-40% longer than EN equivalents (inflection, compound nouns). UI must be designed against PL string lengths as the constraint.
6.1 Character budget reference
| Element | EN budget | PL budget | Worked example |
|---|---|---|---|
| Bottom tab label | ≤8 ch | ≤10 ch | EN: “Quests” (6) / PL: “Zadania” (7) — fits |
| Quest pill (active quest title) | ≤28 ch | ≤36 ch | EN: “Quest 001 — The First Road” (28) / PL: “Quest 001 — Pierwszy Trakt” (28) — both fit |
| Streak ribbon label | ≤20 ch | ≤26 ch | EN: “Day 7 — Tier 1 bonus” (20) / PL: Dzień 7 — Premia I stopnia (26) — design for 26 |
| Region card title | ≤12 ch | ≤16 ch | EN: “Plenny” (6) / PL: “Plenny” (6) — same; some regions differ: EN “Frostlands” (10) / PL Krainy Mrozłu (13) |
| Button label (primary CTA) | ≤14 ch | ≤18 ch | EN: “Begin Journey” (13) / PL: Rozpocznij Wyprawę (18) — design at 18 |
| Faction tier badge | ≤12 ch | ≤14 ch | EN: “Apprentice” (10) / PL: “Praktykant” (10) — fits; EN: “Stranger” (8) / PL: “Obcy” (4) — fine |
6.2 Overflow handling
- Text elements that can overflow must use
ellipsistruncation with a full-text tooltip on long-press. - Primary elements (quest title, keystone name) must NOT truncate — they must wrap or the layout must expand to accommodate.
- Never use
overflow: hiddenon content that carries gameplay state.
7. Dark vs light mode
W-level decision: Dark is the default. Light mode is optional, toggled in settings.
Rationale:
- Walking outdoors in daylight: high-contrast dark UI with bright region accent colors reads better in sunlight than a white background (which washes out). The region palette was designed for a dark background.
- OLED battery: most mid-to-high-end Android phones and all iPhone 12+ use OLED panels. Dark pixels draw near-zero current. Walking sessions can be long (30+ minutes). Battery impact is material.
- Tone: the Pratchett-warm + epic-stakes tone corresponds to parchment, ink, lamp-light — which maps naturally to a warm dark palette (near-black backgrounds, golden accents). A white-background UI would read clinical against the world’s character.
- Hardcore RPG audience (D-008): the PoE-inspired audience expects dark UIs. This is the genre convention for passive-tree style games.
Light mode (optional): A fully compliant light palette exists as an option for users with visual accessibility needs or personal preference. Light mode uses --color-bg: #f5f0e8 (warm parchment), --color-surface: #ede8de, --color-on-surface: #1a1816. The region accent palette retains its hex values but must be re-audited for contrast on the light background — several accents that pass on dark will fail on light (notably Mglica #6b8f7a and Targosie #d65a5a). Light mode contrast audit is a phase 11 deliverable, not phase 9.
This document is W-level autonomous. Typography family choice (Literata + Inter) and dark-first default are decisions within ui-designer authority. Licensing costs: both are OFL/free — no D-level escalation required.