Files
spa/TRM_Design_System-handoff/trm-design-system/project/ui_kits/leaderboard/index.html
T
julian 8223a566e4 docs: import TRM design handoff + defer adoption to phase 3.8
A design handoff bundle generated by Claude Design (claude.ai/design)
on 2026-05-02. Defines the Bloomberg/F1-pit-wall aesthetic for TRM:
- ink-on-paper base + race-flag red accent (#E8412B)
- square-edged everything, sharp printed offset shadows
- mono numerics (JetBrains Mono) for any changing value
- Goldplay (real licensed font, three weights in bundle fonts/)
- four surfaces designed: dashboard / leaderboard / mobile / marketing
  (SPA scope is the first two)

The bundle is committed in-tree at TRM_Design_System-handoff/ so 3.8
has the full source material when it picks the work up. Includes:
- Top-level + project READMEs (the design spec)
- chats/chat1.md (intent + iteration history)
- colors_and_type.css (token set, drop-in for Tailwind 4 @theme)
- fonts/ (Goldplay regular/semibold/bold)
- ui_kits/ (HTML prototypes per surface)
- preview/ (per-token visual reference cards)

Updated phase-3-dogfood-readiness/README.md task 3.8 row to point at
the bundle and document the recommended approach (retheme shadcn via
CSS variable overrides + Tailwind 4 @theme, not replace).

Why deferred: foundational tokens are non-blocking for Phase 1 (login
+ placeholder home) and Phase 2 (live map without chrome). Applying
them now would either delay dogfood-blocking work or land partial
styling that gets reworked when 3.8 lands the full pass.
2026-05-02 19:11:57 +02:00

97 lines
5.2 KiB
HTML

<!doctype html>
<html><head><meta charset="utf-8">
<title>TRM Live Leaderboard</title>
<link rel="stylesheet" href="../../colors_and_type.css">
<link rel="stylesheet" href="../kit.css">
<style>
body { background: var(--night); color: var(--night-fg); }
.pill.live { color: var(--flag); border-color: var(--flag); }
table.lb.night { border-color: var(--night-line); }
table.lb.night th { background: var(--night-3); color: var(--night-fg-3); border-bottom-color: var(--night-line); }
table.lb.night td { border-bottom-color: var(--night-line); color: var(--night-fg); font-size: 14px; height: 36px; }
table.lb.night tr:hover td { background: var(--night-3); }
.loss { color: #ff8a78 !important; }
.card { background: var(--night-2); border-color: var(--night-line); color: var(--night-fg); }
.card-head { background: var(--night-3); border-bottom-color: var(--night-line); }
.card-head h3 { color: var(--night-fg); }
</style>
<script src="https://unpkg.com/react@18.3.1/umd/react.development.js" integrity="sha384-hD6/rw4ppMLGNu3tX5cjIb+uRZ7UkRJ6BPkLpg4hAu/6onKUg4lLsHAs9EBPT82L" crossorigin="anonymous"></script>
<script src="https://unpkg.com/react-dom@18.3.1/umd/react-dom.development.js" integrity="sha384-u6aeetuaXnQ38mYT8rp6sbXaQe3NL9t+IBXmnYxwkUI2Hw4bsp2Wvmx4yRQF1uAm" crossorigin="anonymous"></script>
<script src="https://unpkg.com/@babel/standalone@7.29.0/babel.min.js" integrity="sha384-m08KidiNqLdpJqLq95G/LEi8Qvjl/xUYll3QILypMoQ65QorJ9Lvtp2RXYGBFj1y" crossorigin="anonymous"></script>
</head>
<body>
<div id="root"></div>
<script type="text/babel" src="HeroClock.jsx"></script>
<script type="text/babel" src="LiveTable.jsx"></script>
<script type="text/babel" src="MiniMap.jsx"></script>
<script type="text/babel" src="Ticker.jsx"></script>
<script type="text/babel">
const { useState, useEffect } = React;
const ROWS = [
{ pos: 1, bib: 247, name: 'Maya Chen', wave: 'Elite', split: '00:42:18.4', gap: '—', pace: '04:13', last: '0:18 ago' },
{ pos: 2, bib: 188, name: 'Ravi Park', wave: 'Elite', split: '00:42:22.6', gap: '+0:04.2', pace: '04:14', last: '0:22 ago' },
{ pos: 3, bib: 44, name: 'Noemi Vega', wave: 'Elite', split: '00:42:30.1', gap: '+0:11.7', pace: '04:15', last: '0:30 ago' },
{ pos: 4, bib: 302, name: 'Tomás Riera', wave: 'Elite', split: '00:42:55.0', gap: '+0:36.6', pace: '04:18', last: '0:55 ago' },
{ pos: 5, bib: 119, name: 'Anika Joshi', wave: 'Elite', split: '00:43:01.2', gap: '+0:42.8', pace: '04:19', last: '1:01 ago' },
{ pos: 6, bib: 401, name: 'Felix Okafor', wave: 'Open', split: '00:43:08.7', gap: '+0:50.3', pace: '04:19', last: '1:08 ago' },
{ pos: 7, bib: 77, name: 'Sara Lindqvist',wave: 'Open', split: '00:43:14.0', gap: '+0:55.6', pace: '04:20', last: '1:14 ago' },
{ pos: 8, bib: 215, name: 'David Müller', wave: 'Open', split: '00:43:22.4', gap: '+1:04.0', pace: '04:21', last: '1:22 ago' },
{ pos: 9, bib: 503, name: 'Inés Báez', wave: 'Open', split: '00:43:30.6', gap: '+1:12.2', pace: '04:21', last: '1:30 ago' },
{ pos:10, bib: 162, name: 'Kenji Sato', wave: 'Open', split: '00:43:38.0', gap: '+1:19.6', pace: '04:22', last: '1:38 ago' },
];
const CHECKPOINTS = [
{ label: 'START', km: 0, x: 30, y: 120, passed: true },
{ label: 'CP1', km: 2.5, x: 150, y: 60, passed: true },
{ label: 'CP2', km: 5.0, x: 270, y: 80, passed: true },
{ label: 'CP3', km: 7.5, x: 380, y: 70, passed: true },
{ label: 'CP4', km: 9.0, x: 480, y: 90, passed: false },
{ label: 'FINISH', km: 10, x: 570, y: 110, passed: false },
];
const TICKER = [
{ cp: 'CP3', bib: 247, name: 'M. Chen', time: '00:42:18.4', delta: '-0:01.6' },
{ cp: 'CP3', bib: 188, name: 'R. Park', time: '00:42:22.6', delta: '+0:00.4' },
{ cp: 'CP3', bib: 44, name: 'N. Vega', time: '00:42:30.1', delta: '+0:01.9' },
{ cp: 'CP3', bib: 302, name: 'T. Riera', time: '00:42:55.0', delta: '+0:04.4' },
{ cp: 'CP3', bib: 119, name: 'A. Joshi', time: '00:43:01.2', delta: '-0:02.1' },
{ cp: 'CP3', bib: 401, name: 'F. Okafor', time: '00:43:08.7', delta: '+0:00.6' },
];
function App() {
const [t, setT] = useState({ h: 1, m: 23, s: 45, ms: 6 });
useEffect(() => {
const id = setInterval(() => {
setT(p => {
let ms = p.ms + 1;
let s = p.s, m = p.m, h = p.h;
if (ms > 9) { ms = 0; s++; }
if (s > 59) { s = 0; m++; }
if (m > 59) { m = 0; h++; }
return { h, m, s, ms };
});
}, 100);
return () => clearInterval(id);
}, []);
const pad = n => String(n).padStart(2, '0');
const time = `${pad(t.h)}:${pad(t.m)}:${pad(t.s)}.${t.ms}`;
return (
<div style={{ minHeight: '100vh', display: 'flex', flexDirection: 'column' }}>
<HeroClock time={time} eventName="Coastline 10K · Spring 2026" sport="Run · 10K" distance="Coastal route, flat" weather="18°C · light SW · dry" />
<div style={{ flex: 1, padding: '24px 32px', display: 'grid', gridTemplateColumns: '2fr 1fr', gap: 24, minHeight: 0 }}>
<div style={{ minHeight: 0, overflow: 'hidden' }}>
<LiveTable rows={ROWS} />
</div>
<MiniMap checkpoints={CHECKPOINTS} />
</div>
<Ticker items={TICKER} />
</div>
);
}
ReactDOM.createRoot(document.getElementById('root')).render(<App />);
</script>
</body></html>