# 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.96–0.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). ```css --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: ```css /* 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 labels** — `15px, 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.94–0.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 |