This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
TriTimes is a Next.js web application that lets athletes view their historical IronMan and IronMan 70.3 triathlon results. For each result, it shows statistical distributions for each discipline (swim, bike, run) and total time, comparing performance against the athlete's age group and overall field.
Data is collected from https://www.ironman.com/races/im703-new-york/results. Inspired by https://www.hyresult.com/.
app/β Next.js 16 app with React 19, TypeScript, Tailwind CSS v4, Recharts, Vercel Analyticsscripts/β Node.js data pipeline scripts (zero dependencies, requires Node 18+)data/β Race result CSVs andraces.jsonmanifest
/β Landing page with hero section and global athlete search/racesβ Browse all races with distance (70.3/140.6) and year filters/race/[slug]β Single race with in-page athlete search/race/[slug]/result/[id]β Individual result with discipline histograms (age group & overall field toggle)/athlete/[slug]β Athlete profile showing cross-race history/api/searchβ API endpoint for global athlete search (used byGlobalSearchBar)
- Result and race pages use on-demand generation (empty
generateStaticParams) to avoid pre-rendering 75K+ pages at build time - Athlete search uses a pre-built JSON index (
data/athlete-index.json) generated at build time, avoiding CSV parsing on first request - Athlete search uses debounced input (300ms) with a cached deduplicated index
- CSV parse results are cached in memory
- CSVs (
data/*.csv) are committed to git (~400 KB per race) - Raw JSON (
data/raw/) is gitignored (~10 MB per race) - Athlete index (
data/athlete-index.json) is gitignored; generated at build time byscripts/build-search-index.js - Athlete profiles (
data/athlete-profiles.json) is gitignored; compact slugβrace mapping generated at build time - Manifest (
data/races.json) lists all races; auto-generated by scraper, read by app at build time - The app reads races from
data/races.jsonβ adding a new race only requires running the scraper, no code changes
app/src/lib/data.tsβ Server-side data access (reads CSVs, computes histograms, athlete deduplication & profiles)app/src/lib/types.tsβ TypeScript interfaces (AthleteSearchEntry,AthleteProfile,AthleteRaceEntry, etc.)app/src/lib/colors.tsβ Canonical discipline color constants (swim, bike, run, overall, T1, T2)app/src/lib/flags.tsβ Country ISO to flag emoji mappingapp/src/components/GlobalSearchBar.tsxβ Cross-race athlete search with debounce, caching, and keyboard navapp/src/components/SearchBar.tsxβ Single-race in-page athlete searchapp/src/components/DisciplineSections.tsxβ Discipline histograms with age group / overall field toggleapp/src/components/Header.tsxβ Sticky header with navigationapp/src/app/page.tsxβ Landing page with hero section and global searchapp/src/app/races/page.tsxβ Race listing with distance and year filtersapp/src/app/athlete/[slug]/page.tsxβ Athlete profile pageapp/src/app/race/[slug]/result/[id]/page.tsxβ Individual athlete result pageapp/src/app/api/search/route.tsβ Athlete search API endpointscripts/build-search-index.jsβ Pre-builds deduplicated athlete search index from CSVs (runs automatically duringnpm run build)scripts/scrape.jsβ Single-race scraper (also exports reusable functions)scripts/discover.jsβ Discovers event IDs from ironman.com or competitor.com URLsscripts/scrape-all.jsβ Batch scraper using race-registry.jsonscripts/race-registry.jsonβ Registry of known races with group URLsdata/races.jsonβ Race manifest consumed by the app
# Development
cd app && npm run dev
# Build
cd app && npm run build
# Scrape a single race
node scripts/scrape.js <slug> [event-id] [--no-raw]
# Discover event IDs
node scripts/discover.js <url>
# Batch scrape
node scripts/scrape-all.js [--slug=<slug>] [--year=2025] [--dry-run] [--save-raw]
# Build search index (runs automatically during npm run build)
node scripts/build-search-index.jsCanonical colors for triathlon disciplines. Defined in app/src/lib/colors.ts β always import from there, never hardcode.
| Discipline | Hex | Tailwind |
|---|---|---|
| Swim | #3b82f6 |
blue-400 |
| Bike | #ef4444 |
red-400 |
| Run | #f59e0b |
amber-400 |
| Overall | #9ca3af |
gray-400 |
| T1 | #8b5cf6 |
purple-400 |
| T2 | #ec4899 |
pink-400 |
- Use Conventional Commits for commit messages and PR titles (e.g.
feat:,fix:,docs:,chore:,refactor:)