Files
gw/frontend/THEME.md
T
ponzischeme89 6d44e05de4 v1
2026-04-18 07:23:55 +12:00

13 KiB
Raw Blame History

Goodwalk — Design Language & Theme Reference

Source of truth for visual language, spacing, tone, interaction style, and component feel across the Goodwalk frontend.


Brand Palette

Token Value Usage
--gw-accent-color-1 #FFD100 Highlight / CTA yellow
--gw-accent-color-2 #213021 Deep forest green (primary brand)
--gw-accent-color-3 #59606D Muted slate
--gw-accent-color-4 #E5D6C2 Warm sand
--gw-accent-color-5 #FFFFFF White
--gw-accent-color-6 #000000 Black

Background texture: warm cream gradient (#f4f1ea#f1ede5).

Typography: DM Sans (body), system heading scale.


Desktop Design Language

Tone: Premium, intentional, warm. Not corporate. Feels like a trusted local service with high craft.

Spacing: 8-point grid. Components breathe. Generous white-space is never wasted.

Surfaces: Semi-transparent white (rgba(255,255,255,0.94)) on warm cream background. Cards carry a subtle multi-layer shadow.

Borders: Warm tan #e7ddd1, never cold grey.

Shadows: Multi-stop warm shadows — 0 4px 8px rgba(17,16,14,0.04), 0 16px 34px rgba(17,16,14,0.08).

Navigation: Sticky header with glassmorphism on scroll. Bottom tab bar on mobile (see below).

Radius scale: 8px (inputs), 12px (cards), 16px (modals/sheets), 28px (pills/nav).

Interactions: Subtle hover lifts (translateY(-1px)), scale-down press (scale(0.960.98)), smooth 150ms ease transitions.


Admin Design System

Admin uses a theming layer on top of the base brand. The active theme is stored in localStorage as gw_admin_theme.

Theme Accent Highlight
main #213021 #ffd100
pink #c54b8c #ffcadf
purple #6f55d9 #d8cbff
yellow #9a7700 #ffe08a

Admin CSS variables (set via inline style on the shell element):

--admin-accent, --admin-accent-dark, --admin-accent-rgb
--admin-topnav-start, --admin-topnav-end
--admin-highlight, --admin-highlight-rgb
--admin-button-text
--admin-font-scale

The admin header uses a gradient from --admin-topnav-start--admin-topnav-end.


Members Design System

Members uses the core brand palette directly (no theme switching).

--members-accent:        #213021
--members-accent-dark:   #172217
--members-highlight:     #ffd100
--members-border:        #e7ddd1
--members-surface:       rgba(255, 255, 255, 0.94)
--members-surface-soft:  #f8f4ed
--members-surface-muted: #f2ece3
--members-text:          #1d261d
--members-text-muted:    #6b635c

Mobile Design Language

Mobile users receive a dedicated, purpose-built experience — not a squeezed desktop page. All mobile screens live under /admin/m/ (admin) or /members/m/ (members).

Philosophy

  • Native-app inspired. Feel fast, responsive, and deliberate. Every tap should feel satisfying.
  • One-handed first. Primary actions sit at thumb-reach (bottom half of screen). Navigation is bottom-anchored.
  • Dense but not cramped. Maximum information per screen without feeling overwhelming.
  • No desktop patterns forced onto phones. Never squeeze a data table onto a 390px screen. Use lists, cards, and sheets instead.

Mobile vs Desktop Rules

Rule Desktop Mobile
Navigation Top sticky header + subnav tabs Fixed bottom tab bar
Primary actions Anywhere sensible Bottom-anchored action bar or prominent nav item
Modals Standard centred modal Slide-up bottom sheet
Tables / data grids Full table layout Vertical list with disclosure rows
Forms Multi-column layouts OK Single column, full-width fields
Page titles In header nav Sticky per-page header (MobilePageShell)
Content width Max-width capped at 1320px Full bleed (16px horizontal padding)

Mobile Token Layer

All mobile screens inherit tokens from the MobileAppShell component. These tokens provide a consistent design system shared between Admin and Member mobile experiences:

/* Palette */
--m-accent:        #213021          /* primary action colour */
--m-accent-dark:   #172217          /* hover/active state */
--m-accent-rgb:    33, 48, 33       /* for rgba() usage */
--m-highlight:     #ffd100
--m-bg:            #f4f1ea
--m-surface:       rgba(255,255,255,0.96)
--m-surface-soft:  #f8f4ed
--m-surface-muted: #f2ece3
--m-border:        #e7ddd1
--m-border-soft:   rgba(231,221,209,0.6)
--m-text:          #1d261d
--m-text-muted:    #6b635c
--m-text-faint:    #a09890

/* Shadows */
--m-shadow-card:   0 2px 8px rgba(17,16,14,0.06), 0 8px 20px rgba(17,16,14,0.08)
--m-shadow-sheet:  0 -8px 40px rgba(17,16,14,0.18)
--m-shadow-button: 0 4px 16px rgba(33,48,33,0.28)

/* Radii */
--m-radius-xs:  6px
--m-radius-sm:  10px
--m-radius:     16px      /* default card/button radius */
--m-radius-lg:  24px
--m-radius-xl:  32px      /* bottom sheet corners */

/* Spacing scale (4-point base) */
--m-space-1:  4px
--m-space-2:  8px
--m-space-3:  12px
--m-space-4:  16px
--m-space-5:  20px
--m-space-6:  24px
--m-space-8:  32px
--m-space-10: 40px

/* Type scale */
--m-text-xs:  11px
--m-text-sm:  13px
--m-text-md:  15px    /* body text */
--m-text-lg:  17px    /* page titles */
--m-text-xl:  20px
--m-text-2xl: 24px
--m-text-3xl: 30px    /* stat card values */

/* Touch */
--m-touch-min: 44px   /* minimum touch target (iOS HIG / WCAG) */

/* Layout */
--m-bottom-nav-h: 72px
--m-header-h:     56px
--m-page-px:      16px   /* horizontal page padding */

/* Safe areas (iOS notch/home bar) */
--m-safe-top:    env(safe-area-inset-top, 0px)
--m-safe-bottom: env(safe-area-inset-bottom, 0px)

For the Admin mobile variant, --m-accent, --m-accent-dark, --m-accent-rgb, and --m-highlight are overridden by the admin's active theme variables (--admin-accent etc.), so the admin theme switcher works on mobile too.

Touch Target Minimums

  • Every interactive element must be at least 44 × 44px (iOS HIG) / 48 × 48dp (Material).
  • Use min-height: var(--m-touch-min) on buttons and list rows.
  • Pad tappable areas generously — don't rely on text size alone for target size.

Bottom Navigation Principles

The bottom tab bar is the primary navigation on mobile. It is always visible and never hides during scroll.

  • Max 5 items per bar. Fewer is better.
  • Icons must be recognisable without labels — labels are shown but should not be the only differentiator.
  • One prominent item allowed per bar (filled background, accent colour). Use for the most important CTA (e.g. Add Client, Complete Onboarding).
  • Badge dots use red #e03e3e for unread counts (messages).
  • The bar uses glassmorphism: rgba(251,247,240,0.92) background, backdrop-filter: blur(18px), border-radius: 28px. Never make it a plain opaque bar.
  • Accounts for safe area: padding-bottom: calc(12px + env(safe-area-inset-bottom)).
  • Active state: light green tint rgba(33,48,33,0.08) + accent text colour.
  • Inactive state: --m-text-muted colour.
  • Press: scale(0.94) transform with transition: 0.15s.

Spacing and Rhythm (Small Screens)

  • Page content padding: 16px horizontal.
  • Section vertical spacing: 24px top (tight variant: 16px).
  • Section labels: 11px, 800 weight, 0.08em letter-spacing, uppercase, muted colour.
  • Card internal padding: 16px.
  • List row minimum height: 44px. Default padding: 12px 0.
  • Group gaps: 8px between sibling cards/list items, 12px between elements within a card.
  • Never use horizontal padding smaller than 8px inside a card.

Mobile Card / List Patterns

Cards:

  • border-radius: 16px, warm border #e7ddd1, multi-layer warm shadow.
  • Default: white surface with shadow.
  • Subtle: #f8f4ed surface, no shadow (for secondary content blocks).
  • Highlight: 3px left border in accent colour (for time-sensitive or important content).
  • Press: scale(0.98).

List items (MobileListItem):

  • Full-width rows with leading slot (icon/avatar), label, sublabel, trailing area (meta, badge, chevron).
  • Divider is a 1px line at 60% opacity — not a full-width separator.
  • Never nest more than 2 lines of text in a list item on mobile.
  • Chevron () indicates navigable rows.

Mobile Form Behaviour

  • Single-column layouts only.
  • Labels above inputs (13px, 700 weight).
  • Full-width inputs with min-height: 44px, border-radius: 10px.
  • Focus ring: 3px at 0.12 opacity using accent colour.
  • Error messages below the field in red (#991b1b), not in a toast.
  • Use MobileFormField to wrap every input — it ensures consistent spacing and error handling.
  • Avoid more than 4-5 fields visible at once without sectioning.

Hierarchy of Headers and Page Titles

  1. App header (MobileHeader) — optional sticky brand strip at the very top. Use only when brand context is needed (e.g. on first entry to a section).
  2. Page header (MobilePageShell) — per-page sticky title bar. Always present. Contains: back button (44px touch), centred title (17px, 700), right-side action slot.
  3. Section headings (MobileSection title=) — 11px, 800, uppercase, muted. Acts as a visual grouping label, not a navigational heading.
  4. Card titles / list labels15px, 600 for primary labels, 13px for sublabels.

Do not repeat the page title in the body content. The sticky page header is the single source of truth for where you are.

Motion and Transitions

  • Duration: 150ms for micro-interactions (hover, press), 260ms for sheet entry.
  • Easing: cubic-bezier(0.25, 0.46, 0.45, 0.94) (ease-out feel).
  • Sheet entry: fly transition upward 320px, 260ms.
  • Press scales: 0.940.98 depending on element size (buttons larger → less scale, small icons → more scale).
  • No bouncing or spring physics on primary navigation (too distracting).
  • Reserve spring easing (cubic-bezier(0.34, 1.56, 0.64, 1)) for playful one-off moments only.
  • Skeleton loading: sinusoidal opacity pulse 0.45 → 0.85, 1.4s period, staggered delays.

Empty / Loading / Error States

Use MobileEmptyState for all three variants:

  • Loading: animated skeleton cards (3 staggered shimmer bars).
  • Empty: emoji icon, title, optional body text, optional CTA button.
  • Error: same as empty but role="alert", red title, warning emoji.

Rules:

  • Every list/data section must handle all three states explicitly.
  • Never show a spinner in place of a full-page experience — use skeleton screens.
  • Empty state CTAs should always provide a path forward (e.g. "Book a Walk").

Admin and Member Mobile Alignment

Admin and Member mobile experiences are visually indistinguishable in structure — they share:

  • The same MobileAppShell token layer.
  • The same component library.
  • The same bottom nav design, touch targets, motion, and type scale.
  • The same card/list/section patterns.

They differ in:

  • Admin inherits the admin theme (accent colour changes with theme switcher).
  • Members always uses the green brand palette.
  • Different nav items and routes.
  • Different auth patterns (admin: CSR localStorage; members: server cookie + localStorage).
  • Different feature sets and business logic.

What Must Never Happen in Mobile Views

  • No horizontal scrolling in page content. Horizontal scroll only inside explicit scroll containers (e.g. a chip row).
  • No data tables. Replace with vertical lists with disclosure rows or summary cards.
  • No fixed-position elements other than the bottom nav and action bar. Avoid top banners that take up screen space.
  • No touch targets smaller than 44px. Not even "less important" actions.
  • No modals that cover the full screen (except full-screen sheets on purpose). Prefer slide-up panels.
  • No desktop responsive hacks in mobile components. Do not import mobile tokens into desktop components or add @media (max-width) patches to the mobile components file.
  • No mixing desktop and mobile layouts in the same file (e.g., showing/hiding via CSS in a shared component). Use separate route/layout files.
  • No navigation items that do not exist on mobile. If a feature isn't in the mobile nav, provide a clear path to the desktop version or a dedicated in-app screen.

Component Library Summary

Shared Desktop Components

Located in src/components/ and src/lib/.

Shared Mobile Components

Located in src/lib/mobile/components/. All use the --m-* token layer.

Component Purpose
MobileAppShell Top-level layout wrapper, token layer, bottom nav integration
MobilePageShell Per-page container with sticky title header
MobileHeader Optional branded app header strip
MobileBottomNav Fixed bottom tab bar
MobileSection Content group with optional title and action link
MobileCard Touchable surface card
MobileListItem Single list row (leading/label/sublabel/trailing)
MobileStatCard Metric display card for dashboards
MobileActionBar Sticky bottom CTA button area
MobileSearchBar Touch-friendly search input with clear button
MobileFormField Form field wrapper (label + input slot + error/hint)
MobileEmptyState Empty / loading skeleton / error state
MobileFilterSheet Slide-up bottom sheet for filters
MobileTabs Segmented tab switcher