Pulled via the Stitch MCP from project 8187200995279352789: the design system spec (neon-HUD dark theme -- Sora/Inter/JetBrains Mono, electric green primary, cyan secondary) plus HTML/screenshot for Map HUD, Plan Screen, Route Planning, and Rides History (all Dark Mode), and the project's own export summary.
Raw reference material only, not yet wired into the app. Flags a real discrepancy in README.md: the export summary describes a different ('Modern Professional Dark', blue) design system than the one actually attached to the project and used by every screen fetched -- treat design-system.md as current, export-summary.md as stale.
317 lines
17 KiB
HTML
317 lines
17 KiB
HTML
<!DOCTYPE html>
|
|
|
|
<html class="dark" lang="en" style=""><head></head><body class="antialiased selection:bg-primary-container selection:text-on-primary-container flex flex-col h-screen relative text-on-surface bg-surface"><svg aria-hidden="true" class="inline-defs-container" style="position:absolute;width:0;height:0;overflow:hidden"></svg>
|
|
<meta charset="utf-8"/>
|
|
<meta content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" name="viewport"/>
|
|
<title>RIPPR - Record Activity</title>
|
|
<link href="https://fonts.googleapis.com" rel="preconnect"/>
|
|
<link crossorigin="" href="https://fonts.gstatic.com" rel="preconnect"/>
|
|
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500;700&family=Sora:wght@400;600;700&display=swap" rel="stylesheet"/>
|
|
<link href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:wght,FILL@100..700,0..1&display=swap" rel="stylesheet"/>
|
|
<script src="https://cdn.tailwindcss.com?plugins=forms,container-queries"></script>
|
|
<script id="tailwind-config">
|
|
tailwind.config = {
|
|
darkMode: "class",
|
|
theme: {
|
|
extend: {
|
|
"colors": {
|
|
"secondary-fixed-dim": "#aec7f6",
|
|
"primary-fixed": "#d6e3ff",
|
|
"tertiary-fixed-dim": "#ffb68c",
|
|
"inverse-surface": "#e5e2e1",
|
|
"on-surface-muted": "#9ca3af",
|
|
"error": "#ffb4ab",
|
|
"inverse-primary": "#005db8",
|
|
"on-primary-fixed-variant": "#00458d",
|
|
"secondary": "#aec7f6",
|
|
"primary-fixed-dim": "#aac7ff",
|
|
"surface-container": "#201f1f",
|
|
"primary": "#aac7ff",
|
|
"tertiary-container": "#e3711f",
|
|
"surface-variant": "#353534",
|
|
"on-background": "#e5e2e1",
|
|
"on-secondary-fixed-variant": "#2e476f",
|
|
"primary-container": "#4090fe",
|
|
"outline-variant": "#414753",
|
|
"background": "#131313",
|
|
"surface": "#131313",
|
|
"on-tertiary": "#532200",
|
|
"on-tertiary-fixed": "#321200",
|
|
"on-surface": "#e5e2e1",
|
|
"surface-container-low": "#1c1b1b",
|
|
"on-primary-container": "#002958",
|
|
"secondary-fixed": "#d6e3ff",
|
|
"accent-error": "#ffb4ab",
|
|
"on-primary-fixed": "#001b3e",
|
|
"surface-container-lowest": "#0e0e0e",
|
|
"surface-tint": "#aac7ff",
|
|
"error-container": "#93000a",
|
|
"secondary-container": "#2e476f",
|
|
"surface-main": "#0a0a0c",
|
|
"tertiary-fixed": "#ffdbc9",
|
|
"surface-container-high": "#2a2a2a",
|
|
"on-primary": "#002f64",
|
|
"on-surface-bright": "#ffffff",
|
|
"on-tertiary-container": "#481d00",
|
|
"on-error": "#690005",
|
|
"surface-card": "#16161a",
|
|
"surface-container-highest": "#353534",
|
|
"surface-bright": "#393939",
|
|
"on-secondary-container": "#9db6e4",
|
|
"on-tertiary-fixed-variant": "#763400",
|
|
"on-error-container": "#ffdad6",
|
|
"on-secondary-fixed": "#001b3d",
|
|
"tertiary": "#ffb68c",
|
|
"outline": "#8b919f",
|
|
"surface-dim": "#131313",
|
|
"on-secondary": "#143057",
|
|
"inverse-on-surface": "#313030",
|
|
"on-surface-variant": "#c1c6d5"
|
|
},
|
|
"borderRadius": {
|
|
"DEFAULT": "0.25rem",
|
|
"lg": "0.5rem",
|
|
"xl": "0.75rem",
|
|
"full": "9999px"
|
|
},
|
|
"spacing": {
|
|
"margin-mobile": "16px",
|
|
"gutter": "24px",
|
|
"margin-desktop": "32px",
|
|
"base": "4px",
|
|
"max-width": "1280px"
|
|
},
|
|
"fontFamily": {
|
|
"headline-md": [
|
|
"Inter"
|
|
],
|
|
"label-sm": [
|
|
"Inter"
|
|
],
|
|
"body-lg": [
|
|
"Inter"
|
|
],
|
|
"body-md": [
|
|
"Inter"
|
|
],
|
|
"headline-sm": [
|
|
"Inter"
|
|
],
|
|
"headline-lg": [
|
|
"Inter"
|
|
],
|
|
"headline-lg-mobile": [
|
|
"Inter"
|
|
],
|
|
"label-md": [
|
|
"Inter"
|
|
],
|
|
"display-telemetry": ["Inter"],
|
|
"label-caps": ["Inter"]
|
|
},
|
|
"fontSize": {
|
|
"headline-md": [
|
|
"24px",
|
|
{
|
|
"lineHeight": "32px",
|
|
"fontWeight": "600"
|
|
}
|
|
],
|
|
"label-sm": [
|
|
"11px",
|
|
{
|
|
"lineHeight": "16px",
|
|
"letterSpacing": "0.5px",
|
|
"fontWeight": "500"
|
|
}
|
|
],
|
|
"body-lg": [
|
|
"16px",
|
|
{
|
|
"lineHeight": "24px",
|
|
"fontWeight": "400"
|
|
}
|
|
],
|
|
"body-md": [
|
|
"14px",
|
|
{
|
|
"lineHeight": "20px",
|
|
"fontWeight": "400"
|
|
}
|
|
],
|
|
"headline-sm": [
|
|
"20px",
|
|
{
|
|
"lineHeight": "28px",
|
|
"fontWeight": "600"
|
|
}
|
|
],
|
|
"headline-lg": [
|
|
"32px",
|
|
{
|
|
"lineHeight": "40px",
|
|
"letterSpacing": "-0.02em",
|
|
"fontWeight": "700"
|
|
}
|
|
],
|
|
"headline-lg-mobile": [
|
|
"28px",
|
|
{
|
|
"lineHeight": "36px",
|
|
"letterSpacing": "-0.01em",
|
|
"fontWeight": "700"
|
|
}
|
|
],
|
|
"label-md": [
|
|
"12px",
|
|
{
|
|
"lineHeight": "16px",
|
|
"letterSpacing": "0.5px",
|
|
"fontWeight": "500"
|
|
}
|
|
],
|
|
"display-telemetry": ["64px", { "lineHeight": "64px", "letterSpacing": "-0.04em", "fontWeight": "700" }],
|
|
"label-caps": ["12px", { "lineHeight": "16px", "letterSpacing": "0.1em", "fontWeight": "500" }]
|
|
}
|
|
},
|
|
},
|
|
}
|
|
</script>
|
|
<style>
|
|
body { margin: 0; overflow: hidden; background-color: #131313; color: #e5e2e1; min-height: max(884px, 100dvh); }
|
|
.hud-panel { background: rgba(32, 31, 31, 0.8); backdrop-filter: blur(20px); border: 1px solid rgba(255, 255, 255, 0.1); }
|
|
.glass-btn { background: rgba(32, 31, 31, 0.9); backdrop-filter: blur(10px); border: 1px solid rgba(255,255,255,0.1); }
|
|
</style>
|
|
<!-- Background Map Placeholder -->
|
|
<div class="absolute inset-0 z-0 bg-surface">
|
|
<div class="w-full h-full bg-cover bg-center opacity-40 mix-blend-luminosity" data-alt="A light mode stylized city map view for a fitness tracking application. Minimalist aesthetic with high contrast blue routes highlighting paths against light gray blocks representing buildings and terrain. The map appears like a clean interface overlay." data-location="Tokyo" style="background-image: url('https://lh3.googleusercontent.com/aida-public/AB6AXuDr5_s-KgoDdw3V0jHBFYtHVB3aikGStl0j7TM7kPJnauzs3U1eRnVMaMh4ywSZTMLAo_LxEZnu9rTzBPq-EGev_TiD3t0VY9osDcW0xKKUZxL2rx7TPn0qh2lor7H-TT1FWRur2eVHGsIsu-YT3ht-zwG1VrgIArB7Dxx5IgNThsHCroZ6oFbBB_Uo1Y8rIjPn8NPkxuUVxmKjpNze6RAmNCuYj9V3ivWdnkUNS_Z6ybPWERqbvRA4')"></div>
|
|
<!-- Routes and Location Overlay -->
|
|
<svg class="absolute inset-0 w-full h-full pointer-events-none" preserveaspectratio="xMidYMid slice" viewbox="0 0 400 800">
|
|
<defs>
|
|
<lineargradient id="speedGradient" x1="10%" x2="50%" y1="90%" y2="50%">
|
|
<stop offset="0%" stop-color="#4090fe"></stop>
|
|
<stop offset="40%" stop-color="#aac7ff"></stop>
|
|
<stop offset="70%" stop-color="#ffb4ab"></stop>
|
|
<stop offset="100%" stop-color="#ffb4ab"></stop>
|
|
</lineargradient>
|
|
</defs>
|
|
<!-- Planned Route -->
|
|
<path d="M 200 400 C 250 250, 150 150, 300 50" fill="none" opacity="0.8" stroke="#8b919f" stroke-dasharray="10 10" stroke-linecap="round" stroke-linejoin="round" stroke-width="6"></path>
|
|
<!-- Completed Route -->
|
|
<path d="M 100 750 C 150 600, 100 500, 200 400" fill="none" stroke="url(#speedGradient)" stroke-linecap="round" stroke-linejoin="round" stroke-width="6"></path>
|
|
<!-- Pulsing Location Dot -->
|
|
<circle cx="200" cy="400" fill="#4090fe" r="8"></circle>
|
|
<circle cx="200" cy="400" fill="none" r="8" stroke="#4090fe" stroke-width="2">
|
|
<animate attributename="r" dur="1.5s" from="8" repeatcount="indefinite" to="24"></animate>
|
|
<animate attributename="opacity" dur="1.5s" from="1" repeatcount="indefinite" to="0"></animate>
|
|
</circle>
|
|
</svg>
|
|
</div>
|
|
<!-- TopAppBar JSON Component -->
|
|
<!-- Main Content Canvas (HUD Layout) -->
|
|
<main class="flex-1 relative z-10 flex flex-col pb-40 px-2 md:px-margin-desktop w-full max-w-7xl mx-auto md:ml-24 md:w-[calc(100%-6rem)] p-2">
|
|
<!-- Compact Telemetry Row -->
|
|
<div class="flex flex-row gap-1 w-full" id="telemetry-container">
|
|
<div class="metric-card flex-1 hud-panel p-2 rounded-lg flex flex-col items-center justify-center cursor-pointer transition-all duration-300 overflow-hidden min-w-0">
|
|
<span class="metric-title font-label-caps text-[9px] sm:text-[10px] text-on-surface-variant uppercase whitespace-nowrap truncate w-full text-center">Speed</span>
|
|
<div class="metric-value font-display-telemetry text-base text-primary mt-1 transition-all duration-300 whitespace-nowrap">24.5 km/h</div>
|
|
</div>
|
|
<div class="metric-card flex-1 hud-panel p-2 rounded-lg flex flex-col items-center justify-center cursor-pointer transition-all duration-300 overflow-hidden min-w-0">
|
|
<span class="metric-title font-label-caps text-[9px] sm:text-[10px] text-on-surface-variant uppercase whitespace-nowrap truncate w-full text-center">Avg Speed</span>
|
|
<div class="metric-value font-display-telemetry text-lg text-on-surface mt-1 transition-all duration-300">22.1</div>
|
|
</div>
|
|
<div class="metric-card flex-1 hud-panel p-2 rounded-lg flex flex-col items-center justify-center cursor-pointer transition-all duration-300 overflow-hidden min-w-0">
|
|
<span class="metric-title font-label-caps text-[9px] sm:text-[10px] text-on-surface-variant uppercase whitespace-nowrap truncate w-full text-center">Dist</span>
|
|
<div class="metric-value font-display-telemetry text-base text-secondary mt-1 transition-all duration-300 whitespace-nowrap">12.4 km</div>
|
|
</div>
|
|
<div class="metric-card flex-1 hud-panel p-2 rounded-lg flex flex-col items-center justify-center cursor-pointer transition-all duration-300 overflow-hidden min-w-0">
|
|
<span class="metric-title font-label-caps text-[9px] sm:text-[10px] text-on-surface-variant uppercase whitespace-nowrap truncate w-full text-center">Time</span>
|
|
<div class="metric-value font-display-telemetry text-lg text-on-surface mt-1 transition-all duration-300">45:12</div>
|
|
</div>
|
|
</div>
|
|
</main>
|
|
<!-- Full Width Recording Control Bar -->
|
|
<div class="fixed bottom-[80px] md:bottom-0 md:left-24 w-full md:w-[calc(100%-6rem)] z-40">
|
|
<div class="flex flex-row w-full h-16 shadow-[0_-4px_6px_-1px_rgba(0,0,0,0.5)]">
|
|
<button class="flex-1 bg-tertiary-container text-on-tertiary-container font-headline-md flex items-center justify-center transition-colors duration-300 border-r border-white/10"><span class="material-symbols-outlined text-[32px]" style='font-variation-settings: "FILL" 1;'>pause</span></button>
|
|
<button class="flex-1 bg-error-container text-on-error-container font-headline-md flex items-center justify-center transition-colors duration-300"><span class="material-symbols-outlined text-[32px]" style='font-variation-settings: "FILL" 1;'>stop</span></button>
|
|
</div>
|
|
</div>
|
|
<!-- BottomNavBar JSON Component -->
|
|
<nav class="bg-surface/80 backdrop-blur-xl fixed bottom-0 w-full z-50 pb-safe border-t border-white/10 md:hidden">
|
|
<div class="flex justify-around items-center h-20 px-margin-mobile font-label-caps text-label-caps font-display-telemetry"><button class="flex flex-col items-center justify-center text-primary relative after:content-[''] after:absolute after:-bottom-1 after:w-1 after:h-1 after:bg-primary after:rounded-full hover:text-primary/80 transition-transform duration-150 scale-95">
|
|
<span class="material-symbols-outlined" style='font-variation-settings: "FILL" 1;'>map</span>
|
|
</button>
|
|
<button class="flex flex-col items-center justify-center text-on-surface-variant hover:text-primary/80 transition-transform duration-150"><span class="material-symbols-outlined" style='font-variation-settings: "FILL" 0;'>book</span></button>
|
|
<button class="flex flex-col items-center justify-center text-on-surface-variant hover:text-primary/80 transition-transform duration-150">
|
|
<span class="material-symbols-outlined" style="font-variation-settings: 'FILL' 0;">route</span>
|
|
</button>
|
|
<button class="flex flex-col items-center justify-center text-on-surface-variant hover:text-primary/80 transition-transform duration-150">
|
|
<span class="material-symbols-outlined" style="font-variation-settings: 'FILL' 0;">settings</span>
|
|
</button></div>
|
|
</nav>
|
|
<!-- Desktop Side Nav (Visible only on md+) -->
|
|
<aside class="hidden md:flex flex-col fixed left-0 top-16 bottom-0 w-24 bg-surface/80 backdrop-blur-xl border-r border-white/10 z-40 items-center py-8 gap-12">
|
|
<button class="flex flex-col items-center justify-center text-on-surface-variant hover:text-primary/80 transition-colors w-full py-4 font-label-caps text-label-caps">
|
|
<span class="material-symbols-outlined mb-2 text-2xl">map</span>
|
|
Map
|
|
</button>
|
|
<button class="flex flex-col items-center justify-center text-primary relative after:content-[''] after:absolute after:left-0 after:top-1/2 after:-translate-y-1/2 after:w-1 after:h-8 after:bg-primary after:rounded-r-full hover:text-primary/80 transition-colors w-full py-4 font-label-caps text-label-caps bg-white/5">
|
|
<span class="material-symbols-outlined mb-2 text-2xl" style="font-variation-settings: 'FILL' 1;">directions_bike</span>
|
|
Rides
|
|
</button>
|
|
<button class="flex flex-col items-center justify-center text-on-surface-variant hover:text-primary/80 transition-colors w-full py-4 font-label-caps text-label-caps">
|
|
<span class="material-symbols-outlined mb-2 text-2xl">route</span>
|
|
Plan
|
|
</button>
|
|
</aside>
|
|
<script>
|
|
document.addEventListener('DOMContentLoaded', () => {
|
|
// Recording Toggle Logic
|
|
const toggleBtn = document.getElementById('recordingToggle');
|
|
const toggleIcon = document.getElementById('toggleIcon');
|
|
const toggleText = document.getElementById('toggleText');
|
|
let isRecording = true;
|
|
|
|
toggleBtn?.addEventListener('click', () => {
|
|
isRecording = !isRecording;
|
|
if(isRecording) {
|
|
toggleBtn.classList.remove('bg-error-container', 'text-on-error-container');
|
|
toggleBtn.classList.add('bg-primary-container', 'text-on-primary-container');
|
|
toggleIcon.textContent = 'stop_circle';
|
|
toggleText.textContent = 'Stop Recording';
|
|
} else {
|
|
toggleBtn.classList.remove('bg-primary-container', 'text-on-primary-container');
|
|
toggleBtn.classList.add('bg-error-container', 'text-on-error-container');
|
|
toggleIcon.textContent = 'play_arrow';
|
|
toggleText.textContent = 'Start Recording';
|
|
}
|
|
});
|
|
|
|
// Telemetry Expansion Logic
|
|
const metrics = document.querySelectorAll('.metric-card');
|
|
|
|
metrics.forEach(metric => {
|
|
metric.addEventListener('click', () => {
|
|
const isExpanded = metric.classList.contains('flex-[2.5]');
|
|
|
|
// Reset all
|
|
metrics.forEach(m => {
|
|
m.classList.remove('flex-[2.5]', 'bg-surface-container-highest');
|
|
m.classList.add('flex-1');
|
|
m.querySelector('.metric-value').classList.remove('text-2xl', 'md:text-3xl');
|
|
m.querySelector('.metric-value').classList.add('text-lg');
|
|
});
|
|
|
|
// Expand if it wasn't already expanded
|
|
if (!isExpanded) {
|
|
metric.classList.remove('flex-1');
|
|
metric.classList.add('flex-[2.5]', 'bg-surface-container-highest');
|
|
metric.querySelector('.metric-value').classList.remove('text-lg');
|
|
metric.querySelector('.metric-value').classList.add('text-2xl', 'md:text-3xl');
|
|
}
|
|
});
|
|
});
|
|
});
|
|
</script>
|
|
</body></html> |