# Workshopr Mobile (iOS Companion) | | | |---|---| | **Status** | stable — Companion added to the design system July 2026 | | **Governs** | tokens, components, and interaction patterns for the Workshopr Companion iOS app | | **Source of truth** | `workshopr-companion/DESIGN.md` (intent) · `workshopr-companion/src/theme/` (values: `colors.ts`, `typography.ts`, `spacing.ts`) | | **Guards** | none automated (separate codebase, outside `check-drift.php`'s scope) — treat this file and `app-companion.php` as documentation that must be kept in sync by hand whenever `src/theme/` changes | ## Purpose The web design system (`platform-tokens.css` + `helpers.php` + the `ws_*` catalog) has nothing for mobile — it styles PHP pages, and the Companion is a native React Native / Expo app with its own codebase at `workshopr-companion/`. This file is the design system's pointer into that codebase: a concise reference to the mobile tokens, component inventory, and interaction patterns, so anyone working across both surfaces has one place to check "does this match." For the full narrative and rationale, read `workshopr-companion/DESIGN.md`. For the literal values, read `src/theme/`. Neither this file nor `app-companion.php` is authoritative over the app code — they mirror it, the same relationship `workshopr-design-system.json` has to `platform-tokens.css`. ## What the app is A facilitator's in-the-room companion. The web platform owns *planning*; the Companion owns *running the room live*. One home (Run Mode) with two fast escapes (Cards, Parking Lot) that always return home — never three equal-weight tabs. It ships the same unified Learn Red brand family as the rest of the platform (D1, Red Unification) — there is no separate mobile accent color. ## Tokens ### Palette (`src/theme/colors.ts`) | Token | Value | Use | |---|---|---| | `pageCream` | `#FDF8F1` | Screen background | | `surfaceWhite` | `#FFFFFF` | Cards, sheets | | `stageCharcoal` | `#241C13` | The one dominant "stage" surface — the current block card in Run Mode | | `ink` | `#2A2116` | Primary text | | `inkSecondary` | `#6B6152` | Secondary text | | `inkOnStage` | `#FDF8F1` | Text on the dark stage surface | | `inkOnStageSecondary` | `#C9BFAE` | Secondary text on the stage surface | | `red` | `#E54D4D` | Brand red — matches web `--mainsite-primary` exactly | | `redDark` | `#C73E3E` | Brand red dark — matches web `--mainsite-dark` | | `redTint` | `#FBEAE8` | Soft red fills | | `gold` | `#B8894B` | Reserved accent; timer "wrapping up" warning state | | `border` | `#E7E5E4` | Hairlines | | `borderOnStage` | `#3C3226` | Hairlines on the stage surface | | `success` | `#3F8A5C` | Past-block checkmarks | A `darkColors` variant exists in the same file, fully wired through `ThemeProvider`, but v1 pins `light` unconditionally — Run Mode is a daylight tool and the timer's color-coded states need to stay predictable across every venue. **Timer states** (the single highest-stakes visual signal in the app — state must read by more than color): | State | Color | Signal | |---|---|---| | Normal | `ink` `#2A2116` | numerals only, no badge | | Warning | `gold` `#B8894B` | numerals + "WRAPPING UP" label + clock-badge icon | | Overrun | `redDark` `#C73E3E` | numerals (negative) + "OVER TIME" label + alert icon + "Eating into: [next block]" text | ### Spacing & radius (`src/theme/spacing.ts`) — 4pt grid ``` spacing: xxs 4 · xs 8 · sm 12 · md 16 · lg 24 · xl 32 · xxl 48 · xxxl 64 radius: sm 8 · md 12 · lg 16 · xl 24 · pill 999 ``` Generous by default — Run Mode is read at arm's length, not scanned like a dense dashboard. ### Type (`src/theme/typography.ts`) Fraunces (display) + Inter (body) — the same brand fonts as web, never swapped. No text below ~13px anywhere in the app. | Variant | Font | Size / Line height | |---|---|---| | `display` | Fraunces Bold | 34 / 40 | | `title` | Fraunces SemiBold | 26 / 32 | | `heading` | Fraunces SemiBold | 20 / 26 | | `body` | Inter Regular | 16 / 23 | | `bodyMedium` | Inter Medium | 16 / 23 | | `label` | Inter SemiBold | 13 / 17 (+0.3 tracking) | | `caption` | Inter Regular | 13 / 18 | | `timer` | Fraunces Bold, tabular-nums | **64 / 68** — the hero numeral of Run Mode | | `timerCompact` | Fraunces Bold, tabular-nums | 28 / 32 | ## Component inventory (`src/components/`) **Primitives** — `Screen` (safe-area wrapper), `Text` (the only text component; variants above), `Card`, `PressableScale` (scale 0.97 + haptic, the mobile replacement for hover), `AppIcon` (SF Symbols on iOS / Material on Android). **Run Mode** — `CurrentBlockCard` (the charcoal stage card carrying the timer), `TimerDisplay`, `BlockRow`, `BlockTypeBadge`, `EscapeBar` (persistent secondary footer to Cards + Parking Lot — explicitly not a tab bar), `EmptyState`. **Capture (Parking Lot)** — `ShutterButton`, `CaptureTypeControl` (Output/Note/Quote segmented control), `BlockTagChip`, `AudioRecorderControls`, `QueueStrip` (synced/queued counts), `PermissionGate`, `TextCaptureSheet`, `CaptureConfirmation`. **Cards** — `TakeABeatHeader`, `CategoryGrid` / `CategoryTile`, `MoveCard` (the 30-second card — the phrase is the Fraunces hero), `CardStepper`. ## iOS patterns - **Safe areas** — every screen root uses `Screen`; respects top/bottom insets and the home indicator. Portrait only. - **Tactile press + haptics instead of hover** — there is no hover on a phone; `PressableScale` is the universal feedback primitive. - **Dynamic Type + arm's-length legibility** — this is a stage tool glanced at from a table, not scanned up close; nothing renders below ~13px. - **Dark-mode-ready, light-first** — `darkColors` exists and is wired but not switched on in v1; flipping to `useColorScheme()` is a one-line change in `ThemeProvider.tsx` when prioritized. - **"One home, two escapes"** — Run Mode (`src/app/index.tsx`) is the root/home route. Cards (`src/app/cards.tsx`) and Parking Lot (`src/app/parking-lot.tsx`) are `presentation: 'modal'` routes with an explicit close control back to Run Mode. The block outline (`src/app/outline.tsx`) is a `formSheet` — lighter-weight than a full modal because it's consulted mid-block. Never a tab bar; never three equal-weight destinations. - **Timestamp-based timer** — computed from a stored `startedAt`, not a ticking interval, so it survives the device being locked. - **Offline-first** — captures queue locally and sync when connectivity returns; `QueueStrip` surfaces the synced/queued state. - **SF Symbols iconography** — native SF Symbols on iOS (Material Symbols on Android) via `AppIcon`, not the web platform's Material-only icon font. ## Tech Expo Router (file-based routing). Tokens live in `workshopr-companion/src/theme/` and mirror the values on this page and on `app-companion.php` exactly — if they ever disagree, the code wins and both docs need updating. ## Related - `workshopr-companion/DESIGN.md` — the app's own design bible (fuller narrative, anti-slop checklist, motion notes) - `workshopr-companion/src/theme/` — the literal token values - [`app-companion.php`](app-companion.php) — the rendered per-app guide page in this microsite (palette swatches, type scale, component demos, do/don't) - [`theming.md`](theming.md) — how web apps derive identity from platform tokens; the Companion follows the same "alias, don't fork" spirit even though it isn't CSS-driven - `START-HERE.md` — the source-of-truth hierarchy this file slots into