Skip to content

Chronicle18/PitWall

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

6 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

The Pit Wall

A personal F1 dashboard. Editorial newsprint × paddock telemetry.

Live web: privatepitwall.vercel.app · Mobile: scan with Expo Go

The Pit Wall — desktop and mobile

Race detail   Driver detail

📱 Try the mobile app

Scan with Expo Go
  1. Install Expo Go from the App Store / Play Store
  2. Scan the QR code with your iPhone Camera (iOS) or directly from inside Expo Go (Android)
  3. The app loads instantly, hitting the live API at privatepitwall.vercel.app
Or open this deep link on your phone with Expo Go installed:
exp://u.expo.dev/67389a52-0a95-4023-9002-b8a6a234b97e/group/06d79723-6445-4552-b79c-7fd93838c30f

A single-page F1 2026 season dashboard with per-race and per-driver detail pages, shipping to web (vanilla HTML/CSS/JS) and mobile (Expo React Native). Real data from the Jolpica F1 API flows through a FastAPI normalization layer with SWR caching on Upstash Redis, deployed to Vercel as a single project — static frontend on the same domain as the Python serverless backend.

The aesthetic is intentional: warm paper (#f1ebdd) + carbon (#0a0a09), exactly one accent (#d93423 racing red), reserved gold for P1. Two display fonts (Instrument Serif, JetBrains Mono) carry the entire system. No Tailwind, no design libraries, no glassmorphism — hand-authored CSS with tokens, motion limited to CSS keyframes (web) and Reanimated (mobile), both honoring prefers-reduced-motion.


What's interesting about the codebase

Single design token source. Colors, type, spacing, motion durations all live in shared/design/tokens.json. Web reads them into CSS variables; mobile imports the JSON directly. Adding a new team color is one edit; no hex codes scattered across components.

Data is always behind a provider. Clients never reach into the JSON file or the FastAPI URL directly — they go through getProvider(). A single env flag flips between StaticProvider (bundled JSON) and LiveProvider (FastAPI). Both implement the same DataProvider interface.

SWR cache with a Protocol abstraction. The server's Cache Protocol defines get_or_refresh(key, fresh_ttl_s, stale_until_s, refresh) and has two implementations: InMemoryCache for dev/tests and UpstashCache for serverless prod. Swapping backends required zero callsite changes — the abstraction was designed for the swap from day one.

Three-tier fallback chain. Live → retry live → bundled static snapshot → inline emergency payload. Failed network calls never produce a blank page or red error modal; stale data shows a calm ink-3 chip and the UI keeps rendering. PRD §13 enumerates every failure mode and its designed response.

Editorial overlay vs. live data. Curated copy (driver bios, race briefings, watchlist items, ticker) lives in server/app/data/editorial-2026.json as a separate layer. The mapper turns Jolpica's results into a typed SeasonSnapshot; the editorial overlay merges curation onto it server-side. Bio prose substitutes the real team name dynamically — Leclerc reads "Ferrari team orders," not a hardcoded "Mercedes" string from the original mockup.

Drift-guard discipline. Where two implementations of the same logic must coexist (TS provider + JS data-loader during the static-bundle era), both files carry comments explicitly flagging them as twins. Phase B1 collapsed both into a single composed bundle emitted by the server, removing the drift-guard burden entirely.

Same-origin deploy. One Vercel project serves both web/ (static) and server/ (Python serverless) at the same domain via vercel.json rewrites. The frontend hits /v1/snapshot as a same-origin request — no CORS preflight, no separate API host, one URL to remember.


Stack

Layer Tech
Web Vanilla HTML/CSS/JS — no build step, no framework
Mobile Expo SDK 52 + expo-router + TypeScript + Reanimated
Server FastAPI + httpx + pydantic v2 (Python 3.11)
Cache Upstash Redis (REST API, HTTP-based, serverless-friendly)
Data Jolpica F1 API (Ergast-compatible)
Deploy Vercel — static frontend + Python serverless, single project
Tests Playwright (web) + Jest/RNTL (mobile) + pytest + httpx mock
Tooling uv (Python deps), no monorepo orchestrator (per-subproject npm)

Architecture

                        ┌──────────────────────────┐
   Browser / mobile ──> │  data-loader.js   /      │ ──> static fallback
                        │  StaticProvider/Live     │     (bundled JSON)
                        └────────────┬─────────────┘
                                     │ live mode
                                     v
                        ┌──────────────────────────┐
                        │  /v1/snapshot            │
                        │  /v1/race/{r}            │ ── FastAPI
                        │  /v1/driver/{c}          │
                        └────────────┬─────────────┘
                                     │
                                     v
                        ┌──────────────────────────┐
                        │   SWR cache (Upstash)    │
                        │   fresh 10m / stale 24h  │
                        └────────────┬─────────────┘
                                     │ miss
                                     v
                        ┌──────────────────────────┐
                        │   Jolpica F1 API +       │
                        │   editorial overlay      │
                        └──────────────────────────┘

Project structure

web/                  vanilla HTML/CSS/JS dashboard (Playwright tests)
mobile/               Expo React Native app (Jest + RNTL)
server/               FastAPI backend (pytest + httpx mocks)
shared/               TS types, DataProvider, design tokens, static JSON
api/                  Vercel Python serverless entry (imports server/app)
scripts/              Test runner + bundle regen + upstream fixture capture
docs/superpowers/     Design specs and implementation plans (the trail)
PRD.md                Authoritative product spec
PROGRESS.md           Append-only ledger of completed work
CLAUDE.md             Codebase conventions for future sessions

Local development

# Backend (FastAPI on :8000, real Jolpica data)
cd server && uv sync && uv run uvicorn app.main:app --reload

# Web (Playwright runs against this)
cd web && npx http-server -p 5173 ../    # from repo root

# Mobile
cd mobile && npm install && npx expo start

To flip the web frontend to live mode locally, add this before data-loader.js in web/index.html:

<script>
  window.__PITWALL_CONFIG__ = {
    source: "live",
    serverBaseUrl: "http://localhost:8000",
  };
</script>

For mobile, edit mobile/app.json → expo.extra.pitwall.source from "static" to "live".


Tests

A single entrypoint runs all three suites:

bash scripts/test-all.sh
Suite Count Coverage
Playwright 10 Dashboard sections, detail pages, live tick, reduced-motion
Jest + RNTL 7 useCountdown, drivers table, error states
pytest 35 Schema, mappers, editorial overlay, cache SWR, routes

Upstream fixtures are captured from real Jolpica responses via scripts/capture-upstream.py and checked into server/tests/fixtures/. Tests never hit the network or generate fixtures at runtime.


Deploying

The repo is deployed to Vercel as a single project. The vercel.json rewrites send /v1/* to the FastAPI serverless function in api/index.py and serve everything else from web/ as static files. Required environment variables in production:

UPSTASH_REDIS_REST_URL    # cache survives serverless cold starts
UPSTASH_REDIS_REST_TOKEN
ALLOWED_ORIGINS           # CORS allowlist (defaults to "*" for local dev)

The cache backend auto-selects: Upstash if both env vars are present, in-memory otherwise. No code changes between environments.


Roadmap

  • Custom domain in front of the Vercel URL
  • Mobile production build via EAS + TestFlight
  • Promote shared/types.ts validation from a hand-written schema validator to Zod once the data contract grows
  • Visual regression tests on the dashboard hero and detail headers
  • Wire reduced-motion preference through to the mobile app's Reanimated configurations (currently honored only at the component level)

Acknowledgments

Race data: Jolpica F1 API (Ergast-compatible). Initial design system absorbed from a Claude Design handoff bundle and extended in shared/design/tokens.json. PRD lives at the repo root for all authoritative product decisions.

About

A personal F1 dashboard (`The Pit Wall`) shipping as a web page and a React Native app, both backed by a shared data provider. Built from a Claude Design handoff bundle.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors