8223a566e4
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.
85 lines
4.1 KiB
HTML
85 lines
4.1 KiB
HTML
<!doctype html>
|
|
<html><head><meta charset="utf-8">
|
|
<title>TRM Mobile · UI Kit</title>
|
|
<link rel="stylesheet" href="../../colors_and_type.css">
|
|
<link rel="stylesheet" href="../kit.css">
|
|
<style>
|
|
body { background: var(--paper-3); display: flex; align-items: center; justify-content: center; padding: 32px; min-height: 100vh; }
|
|
.stage { display: flex; gap: 32px; align-items: flex-start; }
|
|
</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="ios-frame.jsx"></script>
|
|
<script type="text/babel" src="BibCard.jsx"></script>
|
|
<script type="text/babel" src="SplitList.jsx"></script>
|
|
<script type="text/babel" src="FollowList.jsx"></script>
|
|
<script type="text/babel" src="BottomNav.jsx"></script>
|
|
<script type="text/babel">
|
|
const { useState } = React;
|
|
|
|
const SPLITS = [
|
|
{ name: 'Start', km: 0, time: '09:00:00.0', delta: '—', passed: true },
|
|
{ name: 'Split 1', km: 2.5, time: '00:10:38.4', delta: '-0:01.2', passed: true },
|
|
{ name: 'Split 2', km: 5.0, time: '00:21:14.7', delta: '-0:02.0', passed: true },
|
|
{ name: 'Split 3', km: 7.5, time: '00:31:55.1', delta: '+0:00.4', passed: true },
|
|
{ name: 'Split 4', km: 9.0, time: '—', delta: '—', passed: false },
|
|
{ name: 'Finish', km: 10, time: '—', delta: '—', passed: false },
|
|
];
|
|
|
|
const PEOPLE = [
|
|
{ bib: 247, name: 'Maya Chen', event: 'Coastline 10K', lastSplit: 'Split 3', time: '31:55.1', delta: '+0:00.4' },
|
|
{ bib: 188, name: 'Ravi Park', event: 'Coastline 10K', lastSplit: 'Split 3', time: '32:01.6', delta: '-0:01.8' },
|
|
{ bib: 44, name: 'Noemi Vega', event: 'Coastline 10K', lastSplit: 'Split 3', time: '32:08.4', delta: '+0:02.1' },
|
|
{ bib: 612, name: 'Diego Acosta', event: 'Twin Peaks Tri', lastSplit: 'Bike', time: '1:12:04', delta: '-0:18.0' },
|
|
];
|
|
|
|
function MyRace() {
|
|
return (
|
|
<div style={{ display: 'flex', flexDirection: 'column', gap: 14, padding: 14 }}>
|
|
<BibCard bib="247" name="Maya Chen" event="Coastline 10K · Spring 2026" projected="00:42:12" status="ON PACE · PB" />
|
|
<SplitList splits={SPLITS} />
|
|
</div>
|
|
);
|
|
}
|
|
|
|
function Following({ onSelect }) {
|
|
return (
|
|
<div style={{ padding: 14 }}>
|
|
<FollowList people={PEOPLE} onSelect={onSelect} />
|
|
</div>
|
|
);
|
|
}
|
|
|
|
function App() {
|
|
const [tab, setTab] = useState('race');
|
|
return (
|
|
<div className="stage">
|
|
<IOSDevice title="My race" width={390} height={780}>
|
|
<div style={{ background: 'var(--paper-2)', minHeight: '100%', display: 'flex', flexDirection: 'column' }}>
|
|
<div style={{ flex: 1, overflow: 'auto' }}>
|
|
{tab === 'race' && <MyRace />}
|
|
{tab === 'follow' && <Following onSelect={() => setTab('race')} />}
|
|
{tab === 'find' && <div style={{ padding: 24, textAlign: 'center', color: 'var(--ink-4)', fontFamily: 'var(--font-mono)', fontSize: 12 }}>Search any event by name or bib.</div>}
|
|
{tab === 'me' && <div style={{ padding: 24, textAlign: 'center', color: 'var(--ink-4)', fontFamily: 'var(--font-mono)', fontSize: 12 }}>Profile · history · personal bests</div>}
|
|
</div>
|
|
<BottomNav active={tab} onNav={setTab} />
|
|
</div>
|
|
</IOSDevice>
|
|
<IOSDevice title="Following" width={390} height={780}>
|
|
<div style={{ background: 'var(--paper-2)', minHeight: '100%', display: 'flex', flexDirection: 'column' }}>
|
|
<div style={{ flex: 1, overflow: 'auto' }}><Following /></div>
|
|
<BottomNav active="follow" onNav={() => {}} />
|
|
</div>
|
|
</IOSDevice>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
ReactDOM.createRoot(document.getElementById('root')).render(<App />);
|
|
</script>
|
|
</body></html>
|