Refresh the Flutter snapshot: UI-01 through UI-09, plus a fresh installable APK
Full UI redesign pass complete: persistent tab shell with an always-visible background map, Modern Professional Dark theme, monochrome dark map tiles, offline skeleton map, a shared GlassPanel/FloatingPill component kit, customizable HUD telemetry widgets, and the Map HUD / Plan & Route Planning / Rides History screen rebuilds. 374 tests passing, up from 316. The APK is a fresh release build (debug-signed, no release signing config exists yet). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012Xki7YAcc2TiN2PRZJ2tXr
This commit is contained in:
62
rippr-flutter-src/docs/design/stitch-export/README.md
Normal file
62
rippr-flutter-src/docs/design/stitch-export/README.md
Normal file
@@ -0,0 +1,62 @@
|
||||
# Stitch export — Rippr Minimalist Activity Tracker
|
||||
|
||||
Pulled from [stitch.withgoogle.com](https://stitch.withgoogle.com) project
|
||||
`8187200995279352789` via the Stitch MCP server on 2026-08-23.
|
||||
|
||||
## Correction
|
||||
|
||||
An earlier version of this README got the design systems backwards. There are **three**
|
||||
distinct design systems in this project (`list_design_systems`), and the four screens
|
||||
below actually render with the one this doc originally called "stale." That's now fixed.
|
||||
|
||||
| Asset | Display name | Mode | Primary | Fonts | Used by the 4 screens below? |
|
||||
|---|---|---|---|---|---|
|
||||
| `assets/e73325c5365a4a56b93823236777e95a` | **Modern Professional Dark** | Dark | Blue `#1978e5` (rendered `#4090fe`/`#aac7ff`) | Inter (everywhere) | **Yes — confirmed by hex-matching the downloaded HTML** |
|
||||
| `assets/41670631a4b64b118fb9ea1b54f1bd60` | Rippr (neon HUD) | Dark | Electric green `#00FF41` | Sora / Inter / JetBrains Mono | No |
|
||||
| `assets/3938003907261369831` | Modern Professional | Light | Blue `#1275e2` | Inter | No |
|
||||
|
||||
**The one you specifically asked for** (screen ID
|
||||
`asset-stub-assets_e73325c5365a4a56b93823236777e95a`, i.e.
|
||||
`assets/e73325c5365a4a56b93823236777e95a`) is **Modern Professional Dark** — deep
|
||||
charcoal/navy (`#0a0a0c` base, `#131313` surface, `#16161a` cards), Professional Blue
|
||||
primary, Inter throughout, 8px rounding, tonal-layer depth (no shadows, lighter surface
|
||||
= more elevated), and a soft blue glow on elevated/active elements instead of a shadow.
|
||||
This is `design-system-modern-professional-dark.md` below, and it's the design system
|
||||
actually used by all four screens already pulled.
|
||||
|
||||
`design-system-rippr-neon.md` is kept for reference — it's a real design system in the
|
||||
project (`get_project`'s top-level `designTheme`, i.e. whatever a brand-new screen would
|
||||
default to), but it is **not** what any of the four fetched screens render with.
|
||||
|
||||
## Contents
|
||||
|
||||
| File | What it is |
|
||||
|---|---|
|
||||
| `design-system-modern-professional-dark.md` | **The design system actually applied to the four screens below.** Full spec: color tokens, typography, spacing, elevation, shape, components. |
|
||||
| `design-system-rippr-neon.md` | A second, unused-by-these-screens design system also present in the project (green/cyan HUD aesthetic). Reference only. |
|
||||
| `screens/export-summary.md` | Stitch's own generated project overview. Its "Design System: Modern Professional Dark" section was correct all along. |
|
||||
| `screens/map-hud-dark.html` + `.png` | Map HUD - Dark Mode: active-recording map screen, floating telemetry cards (speed, avg speed, distance, time), Pause/Stop controls. |
|
||||
| `screens/plan-screen-dark.html` + `.png` | Plan Screen - Dark Mode: empty-state route planner, "Tap to drop a pin" prompt. |
|
||||
| `screens/route-planning-dark.html` + `.png` | Route Planning - Dark Mode: pins dropped, distance/time/pin-count header, dotted route line, Start Route button. |
|
||||
| `screens/rides-history-dark.html` + `.png` | Rides History - Dark Mode: search bar, unit toggle, ride cards with a mini-map thumbnail, date, distance, time, avg speed. |
|
||||
|
||||
Each `.html` is Stitch's generated markup (Tailwind CSS, vanilla JS) for that screen — a
|
||||
reference for structure and interaction, not something to drop into the Flutter app
|
||||
as-is.
|
||||
|
||||
## Not pulled as a screen/image
|
||||
|
||||
"Design System" (`assets_e73325c5365a4a56b93823236777e95a`) is a **design-system asset
|
||||
instance**, not a regular screen — Stitch's `get_screen` call only works on
|
||||
`projects/{project}/screens/{screen}` resources and returns `invalid argument` for an
|
||||
`assets/{id}` resource. There is no separate HTML/screenshot file for it the way there is
|
||||
for a screen; its full content (structured theme tokens + style-guideline prose) is
|
||||
fetched via `list_design_systems` instead, and that's what
|
||||
`design-system-modern-professional-dark.md` is built from.
|
||||
|
||||
## Next step
|
||||
|
||||
This is raw material, not yet applied to the app. `lib/src/ui/theme.dart`'s current
|
||||
palette (safety-orange accent, warm near-black ground — see V3-16) is a different
|
||||
direction from **either** of these Stitch design systems. Reconciling or replacing is a
|
||||
decision for whoever picks up the actual UI work on this branch.
|
||||
@@ -0,0 +1,171 @@
|
||||
---
|
||||
name: Modern Professional Dark
|
||||
colors:
|
||||
surface: '#131313'
|
||||
surface-dim: '#131313'
|
||||
surface-bright: '#393939'
|
||||
surface-container-lowest: '#0e0e0e'
|
||||
surface-container-low: '#1c1b1b'
|
||||
surface-container: '#201f1f'
|
||||
surface-container-high: '#2a2a2a'
|
||||
surface-container-highest: '#353534'
|
||||
on-surface: '#e5e2e1'
|
||||
on-surface-variant: '#c1c6d5'
|
||||
inverse-surface: '#e5e2e1'
|
||||
inverse-on-surface: '#313030'
|
||||
outline: '#8b919f'
|
||||
outline-variant: '#414753'
|
||||
surface-tint: '#aac7ff'
|
||||
primary: '#aac7ff'
|
||||
on-primary: '#002f64'
|
||||
primary-container: '#4090fe'
|
||||
on-primary-container: '#002958'
|
||||
inverse-primary: '#005db8'
|
||||
secondary: '#aec7f6'
|
||||
on-secondary: '#143057'
|
||||
secondary-container: '#2e476f'
|
||||
on-secondary-container: '#9db6e4'
|
||||
tertiary: '#ffb68c'
|
||||
on-tertiary: '#532200'
|
||||
tertiary-container: '#e3711f'
|
||||
on-tertiary-container: '#481d00'
|
||||
error: '#ffb4ab'
|
||||
on-error: '#690005'
|
||||
error-container: '#93000a'
|
||||
on-error-container: '#ffdad6'
|
||||
primary-fixed: '#d6e3ff'
|
||||
primary-fixed-dim: '#aac7ff'
|
||||
on-primary-fixed: '#001b3e'
|
||||
on-primary-fixed-variant: '#00458d'
|
||||
secondary-fixed: '#d6e3ff'
|
||||
secondary-fixed-dim: '#aec7f6'
|
||||
on-secondary-fixed: '#001b3d'
|
||||
on-secondary-fixed-variant: '#2e476f'
|
||||
tertiary-fixed: '#ffdbc9'
|
||||
tertiary-fixed-dim: '#ffb68c'
|
||||
on-tertiary-fixed: '#321200'
|
||||
on-tertiary-fixed-variant: '#763400'
|
||||
background: '#131313'
|
||||
on-background: '#e5e2e1'
|
||||
surface-variant: '#353534'
|
||||
surface-main: '#0a0a0c'
|
||||
surface-card: '#16161a'
|
||||
accent-error: '#ffb4ab'
|
||||
on-surface-bright: '#ffffff'
|
||||
on-surface-muted: '#9ca3af'
|
||||
typography:
|
||||
headline-lg:
|
||||
fontFamily: Inter
|
||||
fontSize: 32px
|
||||
fontWeight: '700'
|
||||
lineHeight: 40px
|
||||
letterSpacing: -0.02em
|
||||
headline-lg-mobile:
|
||||
fontFamily: Inter
|
||||
fontSize: 28px
|
||||
fontWeight: '700'
|
||||
lineHeight: 36px
|
||||
letterSpacing: -0.01em
|
||||
headline-md:
|
||||
fontFamily: Inter
|
||||
fontSize: 24px
|
||||
fontWeight: '600'
|
||||
lineHeight: 32px
|
||||
headline-sm:
|
||||
fontFamily: Inter
|
||||
fontSize: 20px
|
||||
fontWeight: '600'
|
||||
lineHeight: 28px
|
||||
body-lg:
|
||||
fontFamily: Inter
|
||||
fontSize: 16px
|
||||
fontWeight: '400'
|
||||
lineHeight: 24px
|
||||
body-md:
|
||||
fontFamily: Inter
|
||||
fontSize: 14px
|
||||
fontWeight: '400'
|
||||
lineHeight: 20px
|
||||
label-md:
|
||||
fontFamily: Inter
|
||||
fontSize: 12px
|
||||
fontWeight: '500'
|
||||
lineHeight: 16px
|
||||
letterSpacing: 0.5px
|
||||
label-sm:
|
||||
fontFamily: Inter
|
||||
fontSize: 11px
|
||||
fontWeight: '500'
|
||||
lineHeight: 16px
|
||||
letterSpacing: 0.5px
|
||||
rounded:
|
||||
sm: 0.25rem
|
||||
DEFAULT: 0.5rem
|
||||
md: 0.75rem
|
||||
lg: 1rem
|
||||
xl: 1.5rem
|
||||
full: 9999px
|
||||
spacing:
|
||||
base: 4px
|
||||
gutter: 24px
|
||||
margin-mobile: 16px
|
||||
margin-desktop: 32px
|
||||
max-width: 1280px
|
||||
---
|
||||
|
||||
## Brand & Style
|
||||
|
||||
This design system embodies a **Corporate / Modern** aesthetic reimagined for a premium dark-mode experience. The brand personality is authoritative, precise, and sophisticated. By shifting to a deep charcoal and navy foundation, the UI evokes a sense of focused calm and high-end engineering.
|
||||
|
||||
The visual style leverages subtle luminescence and high-contrast accents to guide the user's eye, moving away from traditional light surfaces to a more immersive, "command-center" feel. It is tailored for professional environments where clarity and reduced eye strain are paramount.
|
||||
|
||||
## Colors
|
||||
|
||||
The palette is anchored by a **Deep Dark Gray (#0a0a0c)** background to provide a true premium feel without the harshness of pure black.
|
||||
|
||||
- **Primary:** The Professional Blue (#1978e5) is the soul of the interface, used for key actions and brand presence.
|
||||
- **Secondary:** A muted Slate Blue (#465f88) handles auxiliary UI elements and decorative accents.
|
||||
- **Surface Strategy:** We use a "stepped" dark palette. The base is the darkest, while interactive containers or cards use a slightly lighter gray (#16161a) to create perceived depth.
|
||||
- **Contrast:** All functional text is kept at a high luminance (Off-white to Pure White) to ensure AAA accessibility standards against the dark backdrop.
|
||||
|
||||
## Typography
|
||||
|
||||
This design system relies exclusively on **Inter** to maintain a systematic, utilitarian, and clean appearance.
|
||||
|
||||
- **Weight & Scale:** Headlines utilize tighter letter spacing and heavier weights to command attention against the dark background.
|
||||
- **Readability:** Body text uses a generous line-height to prevent "halation"—where light text on dark backgrounds appears to bleed or blur.
|
||||
- **Labels:** Small labels use all-caps or increased letter-spacing to maintain legibility at micro-scales.
|
||||
|
||||
## Layout & Spacing
|
||||
|
||||
The design system employs a **Fixed Grid** philosophy for desktop to maintain structural integrity, transitioning to a fluid model for smaller screens.
|
||||
|
||||
- **Grid:** A 12-column grid is used for desktop (1280px max-width) with 24px gutters.
|
||||
- **Rhythm:** An 8px spatial system governs all padding and margins, ensuring a predictable and professional vertical rhythm.
|
||||
- **Breakpoints:**
|
||||
- **Mobile:** < 600px (4 columns, 16px margins)
|
||||
- **Tablet:** 600px - 1024px (8 columns, 24px margins)
|
||||
- **Desktop:** > 1024px (12 columns, 32px margins)
|
||||
|
||||
## Elevation & Depth
|
||||
|
||||
In dark mode, shadows are less effective. Instead, this design system uses **Tonal Layers** and **Low-Contrast Outlines**.
|
||||
|
||||
- **Elevation Levels:** Higher elevation is communicated by lighter surface colors. A "floating" modal will have a lighter hex than the background.
|
||||
- **Borders:** Subtle 1px borders (#2d3037) are used to define boundaries where tonal shifts are too subtle.
|
||||
- **Glow:** Highly elevated elements (like active primary buttons) may use a soft blue outer glow rather than a black shadow to emphasize their importance.
|
||||
|
||||
## Shapes
|
||||
|
||||
The shape language is defined as **Rounded (Level 2)**, striking a balance between the rigidity of corporate software and the approachability of modern SaaS.
|
||||
|
||||
- **Small Components:** Buttons and inputs use a 0.5rem (8px) radius.
|
||||
- **Large Containers:** Cards and modals use a 1rem (16px) radius to soften the layout and create clear content groupings.
|
||||
|
||||
## Components
|
||||
|
||||
- **Buttons:** Primary buttons are solid Professional Blue with white text. Secondary buttons use a ghost style with a subtle white border and no fill.
|
||||
- **Input Fields:** Backgrounds should be slightly darker or lighter than the container surface, with a 1px border that glows Professional Blue on focus.
|
||||
- **Cards:** Cards use a #16161a background with a very subtle 1px border (#ffffff10) to separate them from the main background.
|
||||
- **Chips:** Used for metadata, chips feature a desaturated secondary blue-grey background with high-contrast labels.
|
||||
- **Lists:** Items are separated by hair-line dividers (#ffffff08) to maintain a clean vertical scan-path.
|
||||
@@ -0,0 +1,171 @@
|
||||
---
|
||||
name: Rippr
|
||||
colors:
|
||||
surface: '#131314'
|
||||
surface-dim: '#131314'
|
||||
surface-bright: '#3a393a'
|
||||
surface-container-lowest: '#0e0e0f'
|
||||
surface-container-low: '#1c1b1c'
|
||||
surface-container: '#201f20'
|
||||
surface-container-high: '#2a2a2b'
|
||||
surface-container-highest: '#353436'
|
||||
on-surface: '#e5e2e3'
|
||||
on-surface-variant: '#b9ccb2'
|
||||
inverse-surface: '#e5e2e3'
|
||||
inverse-on-surface: '#313031'
|
||||
outline: '#84967e'
|
||||
outline-variant: '#3b4b37'
|
||||
surface-tint: '#00e639'
|
||||
primary: '#ebffe2'
|
||||
on-primary: '#003907'
|
||||
primary-container: '#00ff41'
|
||||
on-primary-container: '#007117'
|
||||
inverse-primary: '#006e16'
|
||||
secondary: '#b9f1ff'
|
||||
on-secondary: '#00363f'
|
||||
secondary-container: '#00e0ff'
|
||||
on-secondary-container: '#005f6d'
|
||||
tertiary: '#fff8f4'
|
||||
on-tertiary: '#442b10'
|
||||
tertiary-container: '#ffd5ae'
|
||||
on-tertiary-container: '#7a5b3c'
|
||||
error: '#ffb4ab'
|
||||
on-error: '#690005'
|
||||
error-container: '#93000a'
|
||||
on-error-container: '#ffdad6'
|
||||
primary-fixed: '#72ff70'
|
||||
primary-fixed-dim: '#00e639'
|
||||
on-primary-fixed: '#002203'
|
||||
on-primary-fixed-variant: '#00530e'
|
||||
secondary-fixed: '#a5eeff'
|
||||
secondary-fixed-dim: '#00daf8'
|
||||
on-secondary-fixed: '#001f25'
|
||||
on-secondary-fixed-variant: '#004e5a'
|
||||
tertiary-fixed: '#ffdcbd'
|
||||
tertiary-fixed-dim: '#e7bf99'
|
||||
on-tertiary-fixed: '#2c1701'
|
||||
on-tertiary-fixed-variant: '#5d4124'
|
||||
background: '#131314'
|
||||
on-background: '#e5e2e3'
|
||||
surface-variant: '#353436'
|
||||
typography:
|
||||
display-telemetry:
|
||||
fontFamily: JetBrains Mono
|
||||
fontSize: 64px
|
||||
fontWeight: '700'
|
||||
lineHeight: 64px
|
||||
letterSpacing: -0.04em
|
||||
headline-lg:
|
||||
fontFamily: Sora
|
||||
fontSize: 32px
|
||||
fontWeight: '600'
|
||||
lineHeight: 40px
|
||||
headline-md:
|
||||
fontFamily: Sora
|
||||
fontSize: 24px
|
||||
fontWeight: '600'
|
||||
lineHeight: 32px
|
||||
body-lg:
|
||||
fontFamily: Inter
|
||||
fontSize: 18px
|
||||
fontWeight: '400'
|
||||
lineHeight: 28px
|
||||
body-md:
|
||||
fontFamily: Inter
|
||||
fontSize: 16px
|
||||
fontWeight: '400'
|
||||
lineHeight: 24px
|
||||
label-caps:
|
||||
fontFamily: JetBrains Mono
|
||||
fontSize: 12px
|
||||
fontWeight: '500'
|
||||
lineHeight: 16px
|
||||
letterSpacing: 0.1em
|
||||
display-telemetry-mobile:
|
||||
fontFamily: JetBrains Mono
|
||||
fontSize: 48px
|
||||
fontWeight: '700'
|
||||
lineHeight: 48px
|
||||
rounded:
|
||||
sm: 0.125rem
|
||||
DEFAULT: 0.25rem
|
||||
md: 0.375rem
|
||||
lg: 0.5rem
|
||||
xl: 0.75rem
|
||||
full: 9999px
|
||||
spacing:
|
||||
unit: 4px
|
||||
container-margin: 20px
|
||||
gutter: 12px
|
||||
stack-sm: 8px
|
||||
stack-md: 24px
|
||||
stack-lg: 48px
|
||||
---
|
||||
|
||||
## Brand & Style
|
||||
The design system centers on a high-performance, dark-mode aesthetic tailored for real-time activity tracking. The brand personality is kinetic, precise, and undistracted. It utilizes a **Minimalist-Technical** style, stripping away non-essential chrome to focus entirely on telemetry and movement.
|
||||
|
||||
The visual narrative is driven by a "HUD" (Heads-Up Display) concept: information is layered over the user's environment or activity maps using subtle glassmorphism and high-contrast data points. The emotional response should be one of focus and momentum, making the user feel like a precision instrument.
|
||||
|
||||
## Colors
|
||||
The palette is dominated by **Pitch Black (#0A0A0B)** to preserve OLED battery life and minimize ocular strain during night activities.
|
||||
|
||||
- **Primary (Electric Neon):** Used exclusively for active states, completion goals, and primary "Go" actions.
|
||||
- **Secondary (Cyan Telemetry):** Reserved for secondary data streams, such as cadence or heart rate, to provide visual separation from primary distance/time metrics.
|
||||
- **Surface Strategy:** Backgrounds use deep charcoal. Overlays use a semi-transparent version of the surface color with a 20px background blur to create the HUD effect.
|
||||
|
||||
## Typography
|
||||
The typography system uses a functional split: **Sora** for UI headers and brand moments, **Inter** for standard interface text, and **JetBrains Mono** for all numerical data and telemetry.
|
||||
|
||||
- **Data Legibility:** All numeric values must use the monospaced font to prevent "jumping" layouts during real-time updates.
|
||||
- **Visual Hierarchy:** Use `label-caps` for units (e.g., KM/H, BPM) placed immediately adjacent to large telemetry displays.
|
||||
- **Scale:** On mobile devices, telemetry data should prioritize width; if values exceed 5 digits, scale the font size down dynamically to fit the viewport width minus margins.
|
||||
|
||||
## Layout & Spacing
|
||||
This design system employs a **Fluid HUD Grid**. Elements are anchored to the corners of the screen to keep the center clear for maps or camera feeds.
|
||||
|
||||
- **Grid Model:** A 6-column grid for mobile, 12-column for desktop.
|
||||
- **Padding:** Use a generous 20px "Safe Zone" margin on all edges of the screen to ensure interactive elements are reachable and not clipped by hardware notches or rounded device corners.
|
||||
- **Density:** High density for data readouts (8px spacing between related metrics) and low density for navigation/settings (24px+ spacing) to prevent accidental taps during motion.
|
||||
|
||||
## Elevation & Depth
|
||||
Depth is expressed through **translucency** rather than shadows.
|
||||
|
||||
1. **Level 0 (Base):** Pure black (#0A0A0B).
|
||||
2. **Level 1 (Substrate):** Dark charcoal (#161618) with 0% transparency.
|
||||
3. **Level 2 (HUD Panels):** Surface color at 60% opacity with a `backdrop-filter: blur(20px)`.
|
||||
4. **Level 3 (Modals):** Surface color at 80% opacity with a subtle 1px inner border in a low-opacity white (10%) to define the edge against dark backgrounds.
|
||||
|
||||
Shadows should be avoided entirely to maintain the "lightweight" digital feel.
|
||||
|
||||
## Shapes
|
||||
The shape language is **Technical-Precision**.
|
||||
|
||||
- **Corners:** Use 0.25rem (4px) as the base radius for all containers to maintain a sharp, engineered appearance.
|
||||
- **Interactive Elements:** Buttons and inputs follow the same 4px rule.
|
||||
- **Progress Indicators:** Use sharp, non-rounded ends for progress bars to emphasize a "segmental" or "digital" look.
|
||||
- **Iconography:** Use 2px stroke weights with squared-off ends to match the monospaced typographic theme.
|
||||
|
||||
## Components
|
||||
|
||||
### Buttons
|
||||
- **Primary:** Solid Electric Neon background with Black text. No rounding beyond the base 4px.
|
||||
- **Ghost:** 1px Neon border with transparent background. Text matches border color.
|
||||
- **Telemetry Toggle:** Small, square buttons using monospaced labels.
|
||||
|
||||
### Cards & HUD Panels
|
||||
- All cards use the Level 2 Elevation (Glassmorphism).
|
||||
- Remove all drop shadows. Use a 1px stroke of #FFFFFF at 10% opacity for definition.
|
||||
|
||||
### Data Inputs
|
||||
- Underlined style only (no bounding box) for a lighter footprint.
|
||||
- Active state changes the underline to the Primary Neon color.
|
||||
- Use JetBrains Mono for all numeric input fields.
|
||||
|
||||
### Progress & Gauges
|
||||
- **Circular Gauges:** Use thin 4px strokes. The "track" should be 5% opacity white; the "fill" should be the Primary Neon.
|
||||
- **Pulse:** Active heart rate or GPS signals should use a "scanning" animation—a vertical line moving across the element—rather than a standard fade.
|
||||
|
||||
### Navigation
|
||||
- A bottom-anchored, floating "Dock" with a glassmorphic background.
|
||||
- Active icons are highlighted with a single Neon pixel-dot underneath.
|
||||
@@ -0,0 +1,50 @@
|
||||
# Rippr Project Export
|
||||
|
||||
## Project Overview
|
||||
Rippr is a minimalist activity tracker designed for cyclists and runners, featuring an immersive map-centric interface, real-time telemetry, and streamlined route planning.
|
||||
|
||||
## Design System: Modern Professional Dark
|
||||
**ID:** {{DATA:DESIGN_SYSTEM:DESIGN_SYSTEM_3}}
|
||||
|
||||
### Theme Tokens
|
||||
- **Color Mode:** Dark
|
||||
- **Primary Color:** #1978e5 (Professional Blue)
|
||||
- **Typography:** Inter
|
||||
- **Roundness:** 8px (Round Eight)
|
||||
|
||||
### Core Components
|
||||
- **TopAppBar:** Minimalist, often hidden for immersive map views.
|
||||
- **BottomNavBar:** 4-icon layout (Map, History, Plan, Settings) with a signature active indicator dot.
|
||||
- **Telemetry Cards:** Floating, semi-transparent modules (`bg-surface/60`) with `backdrop-blur-xl` and `white/10` borders.
|
||||
|
||||
---
|
||||
|
||||
## Screen Inventory
|
||||
|
||||
### 1. Map HUD (Active Navigation)
|
||||
- **ID:** {{DATA:SCREEN:SCREEN_8}}
|
||||
- **Description:** Fullscreen map interface with floating telemetry cards at the top showing Speed, Avg Speed, Distance, and Time. Units are integrated inline (e.g., "24.5 km/h").
|
||||
- **Controls:** Detached Pause (Yellow) and Stop (Red) buttons positioned above the bottom navigation.
|
||||
|
||||
### 2. Plan Screen (Ready state)
|
||||
- **ID:** {{DATA:SCREEN:SCREEN_7}}
|
||||
- **Description:** Clean map interface identical to the HUD but without telemetry or ride controls. Features a subtle floating tooltip: "Tap to drop a pin".
|
||||
|
||||
### 3. Route Planning (Active Pins)
|
||||
- **ID:** {{DATA:SCREEN:SCREEN_5}}
|
||||
- **Description:** Secondary state of the Plan screen showing 5 dropped pins connected by a blue dotted line. A floating header displays total distance, estimated time, and pin count.
|
||||
|
||||
### 4. Rides History
|
||||
- **ID:** {{DATA:SCREEN:SCREEN_6}}
|
||||
- **Description:** List of previous routes displayed as cards over a blurred live map background. Each card shows a mini-map preview, date, distance, time, and average speed.
|
||||
|
||||
### 5. Settings / Profile
|
||||
- **ID:** {{DATA:SCREEN:SCREEN_4}}
|
||||
- **Description:** Structured list of user preferences, account details, and cloud sync options. Designed with clean hierarchy and professional dark mode styling.
|
||||
|
||||
---
|
||||
|
||||
## Technical Specifications
|
||||
- **Framework:** HTML5 / CSS3 (Tailwind CSS)
|
||||
- **Interactivity:** Vanilla JavaScript for map state and telemetry updates.
|
||||
- **Visual Effects:** Backdrop filters for UI depth and high-contrast vector paths for route visualization.
|
||||
@@ -0,0 +1,317 @@
|
||||
<!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>
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 89 KiB |
@@ -0,0 +1,336 @@
|
||||
<!DOCTYPE html>
|
||||
|
||||
<html lang="en"><head>
|
||||
<meta charset="utf-8"/>
|
||||
<meta content="width=device-width, initial-scale=1.0" name="viewport"/>
|
||||
<title>RIPPR - Plan Route</title>
|
||||
<script src="https://cdn.tailwindcss.com?plugins=forms,container-queries"></script>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:wght,FILL@100..700,0..1&display=swap" rel="stylesheet"/>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600&family=JetBrains+Mono:wght@400;500;700&family=Sora:wght@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 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",
|
||||
"unit": "4px",
|
||||
"stack-sm": "8px",
|
||||
"stack-lg": "48px",
|
||||
"stack-md": "24px",
|
||||
"container-margin": "20px"
|
||||
},
|
||||
"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"
|
||||
],
|
||||
"label-caps": ["JetBrains Mono"],
|
||||
"display-telemetry-mobile": ["JetBrains Mono"],
|
||||
"display-telemetry": ["JetBrains Mono"]
|
||||
},
|
||||
"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"
|
||||
}
|
||||
],
|
||||
"label-caps": ["12px", { "lineHeight": "16px", "letterSpacing": "0.1em", "fontWeight": "500" }],
|
||||
"display-telemetry-mobile": ["48px", { "lineHeight": "48px", "fontWeight": "700" }],
|
||||
"display-telemetry": ["64px", { "lineHeight": "64px", "letterSpacing": "-0.04em", "fontWeight": "700" }]
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
<style>
|
||||
.map-grid-overlay {
|
||||
background-image:
|
||||
linear-gradient(to right, rgba(255,255,255,0.03) 1px, transparent 1px),
|
||||
linear-gradient(to bottom, rgba(255,255,255,0.03) 1px, transparent 1px);
|
||||
background-size: 40px 40px;
|
||||
}
|
||||
|
||||
.pulse-ring {
|
||||
animation: pulse-ring 2s cubic-bezier(0.215, 0.61, 0.355, 1) infinite;
|
||||
}
|
||||
|
||||
@keyframes pulse-ring {
|
||||
0% {
|
||||
transform: scale(0.8);
|
||||
opacity: 0.8;
|
||||
box-shadow: 0 0 0 0 rgba(170, 199, 255, 0.7);
|
||||
}
|
||||
70% {
|
||||
transform: scale(1);
|
||||
opacity: 0;
|
||||
box-shadow: 0 0 0 20px rgba(170, 199, 255, 0);
|
||||
}
|
||||
100% {
|
||||
transform: scale(0.8);
|
||||
opacity: 0;
|
||||
box-shadow: 0 0 0 0 rgba(170, 199, 255, 0);
|
||||
}
|
||||
}
|
||||
|
||||
.route-line {
|
||||
stroke-dasharray: 10, 10;
|
||||
animation: dash 20s linear infinite;
|
||||
}
|
||||
|
||||
@keyframes dash {
|
||||
to {
|
||||
stroke-dashoffset: -1000;
|
||||
}
|
||||
}
|
||||
|
||||
.tooltip-float {
|
||||
animation: float 3s ease-in-out infinite;
|
||||
}
|
||||
|
||||
@keyframes float {
|
||||
0% { transform: translateY(0px); }
|
||||
50% { transform: translateY(-8px); }
|
||||
100% { transform: translateY(0px); }
|
||||
}
|
||||
</style>
|
||||
<style>
|
||||
body {
|
||||
min-height: max(884px, 100dvh);
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body class="bg-background text-on-surface h-screen w-full overflow-hidden relative selection:bg-primary-container selection:text-on-primary-container antialiased flex flex-col dark">
|
||||
<!-- Fullscreen Map Canvas -->
|
||||
<main class="flex-grow relative h-full w-full z-0 overflow-hidden">
|
||||
<!-- Base Map Layer -->
|
||||
<div class="absolute inset-0 z-0 bg-surface-container-lowest">
|
||||
<img class="w-full h-full object-cover opacity-60 mix-blend-screen invert" data-alt="A dark mode digital map showing a top-down view of city streets and terrain. The map is rendered in dark grey and black tones with subtle lighter outlines for roads." data-location="San Francisco, CA" src="https://lh3.googleusercontent.com/aida-public/AB6AXuA-oqjvzL6bUKZVJD2vvMu-0LPsY-GmGnGrfOmA0Pv5d3jBrTEuOXQ2lhZUXb9SvIGOnr2Wz64OSx0LGVbxce3zkTseW9Q62p9djWl96s-LaFizAtCBJ7okbDdDDBZVyS8RutWkvabaIwEi_lE9yq26OZuanhZ7NS6Ny4AZx69Sah-DvoE1nzwD2AEEpWJXbfX_KRFa3iGbCUa02sKaAUWzoirpWg5yU6Gk6u2OWNRefC6YtASxYmBq"/>
|
||||
</div>
|
||||
<!-- Technical Grid Overlay -->
|
||||
<div class="absolute inset-0 map-grid-overlay z-10 pointer-events-none"></div>
|
||||
<!-- Planned Route Overlay (SVG) -->
|
||||
<svg class="absolute inset-0 w-full h-full z-20 pointer-events-none" preserveaspectratio="xMidYMid slice" viewbox="0 0 1000 1000">
|
||||
<!-- Path connecting dropped pins - Primary Blue -->
|
||||
<path class="route-line opacity-80" d="M 300 700 Q 400 650 450 500 T 700 300" fill="none" stroke="#aac7ff" stroke-linecap="round" stroke-linejoin="round" stroke-width="6"></path>
|
||||
<!-- Waypoint 1 (Dropped Pin) -->
|
||||
<circle cx="450" cy="500" fill="#aac7ff" r="6" stroke="#131313" stroke-width="2"></circle>
|
||||
<!-- Waypoint 2 (Destination) -->
|
||||
<circle cx="700" cy="300" fill="#131313" r="8" stroke="#aac7ff" stroke-width="3"></circle>
|
||||
</svg>
|
||||
<!-- User Location Marker (Pulsing) -->
|
||||
<div class="absolute top-[70%] left-[30%] z-30 transform -translate-x-1/2 -translate-y-1/2 flex items-center justify-center pointer-events-none">
|
||||
<div class="absolute w-4 h-4 bg-primary rounded-full opacity-30"></div>
|
||||
<div class="absolute w-3 h-3 bg-primary rounded-full pulse-ring"></div>
|
||||
<div class="w-2 h-2 bg-primary rounded-full relative z-10 shadow-[0_0_10px_rgba(170,199,255,0.8)]"></div>
|
||||
<!-- Vision cone indicator -->
|
||||
<div class="absolute -top-12 left-1/2 -translate-x-1/2 w-0 h-0 border-l-[20px] border-l-transparent border-r-[20px] border-r-transparent border-t-[40px] border-t-primary/20 origin-bottom transform rotate-45"></div>
|
||||
</div>
|
||||
<!-- Interactive Map Canvas Layer (Invisible, captures clicks for dropping pins) -->
|
||||
<div class="absolute inset-0 z-40 cursor-crosshair" id="map-interaction-layer"></div>
|
||||
</main>
|
||||
<!-- Floating HUD Elements Layer -->
|
||||
<div class="absolute inset-0 z-50 pointer-events-none flex flex-col justify-end">
|
||||
<!-- Tooltip -->
|
||||
<div class="w-full flex justify-center mb-stack-md pointer-events-none px-container-margin">
|
||||
<div class="tooltip-float bg-surface-container-high/90 backdrop-blur-xl border border-outline-variant/30 rounded-full px-6 py-3 flex items-center gap-2 shadow-sm">
|
||||
<span class="material-symbols-outlined text-primary text-xl" style="font-variation-settings: 'FILL' 1;">location_on</span>
|
||||
<span class="font-label-caps text-label-caps text-on-surface">Tap to drop a pin</span>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Bottom Navigation Bar -->
|
||||
<nav class="bg-surface-container/90 backdrop-blur-xl border-t border-outline-variant/20 w-full pb-safe flex justify-around items-center h-20 px-container-margin pointer-events-auto shrink-0 relative shadow-[0_-4px_20px_rgba(0,0,0,0.5)]">
|
||||
<!-- Map (Inactive) -->
|
||||
<button class="flex flex-col items-center justify-center text-on-surface-variant hover:text-primary transition-colors duration-200 w-16 h-full gap-1 group">
|
||||
<span class="material-symbols-outlined text-2xl group-active:scale-95 transition-transform duration-150">map</span>
|
||||
<span class="font-label-caps text-[10px] uppercase tracking-widest">Map</span>
|
||||
</button>
|
||||
<!-- Rides (Inactive) -->
|
||||
<button class="flex flex-col items-center justify-center text-on-surface-variant hover:text-primary transition-colors duration-200 w-16 h-full gap-1 group">
|
||||
<span class="material-symbols-outlined text-2xl group-active:scale-95 transition-transform duration-150">directions_bike</span>
|
||||
<span class="font-label-caps text-[10px] uppercase tracking-widest">Rides</span>
|
||||
</button>
|
||||
<!-- Plan (Active) -->
|
||||
<button class="flex flex-col items-center justify-center text-primary relative after:content-[''] after:absolute after:bottom-2 after:w-1 after:h-1 after:bg-primary after:rounded-full hover:text-primary transition-colors duration-200 w-16 h-full gap-1 group">
|
||||
<span class="material-symbols-outlined text-2xl group-active:scale-95 transition-transform duration-150" style="font-variation-settings: 'FILL' 1;">route</span>
|
||||
<span class="font-label-caps text-[10px] uppercase tracking-widest text-primary">Plan</span>
|
||||
</button>
|
||||
</nav>
|
||||
</div>
|
||||
<!-- Micro-interaction Script for Pin Dropping Simulation -->
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
const mapLayer = document.getElementById('map-interaction-layer');
|
||||
const svgLayer = document.querySelector('svg');
|
||||
let pinCount = 0;
|
||||
|
||||
mapLayer.addEventListener('click', (e) => {
|
||||
const rect = mapLayer.getBoundingClientRect();
|
||||
const x = e.clientX - rect.left;
|
||||
const y = e.clientY - rect.top;
|
||||
|
||||
// Visual feedback of drop
|
||||
const ripple = document.createElement('div');
|
||||
ripple.className = 'absolute rounded-full border-2 border-primary bg-primary/20 pointer-events-none transform -translate-x-1/2 -translate-y-1/2';
|
||||
ripple.style.left = `${x}px`;
|
||||
ripple.style.top = `${y}px`;
|
||||
ripple.style.width = '0px';
|
||||
ripple.style.height = '0px';
|
||||
ripple.style.opacity = '1';
|
||||
ripple.style.transition = 'all 0.5s cubic-bezier(0.165, 0.84, 0.44, 1)';
|
||||
|
||||
mapLayer.parentElement.appendChild(ripple);
|
||||
|
||||
// Trigger animation
|
||||
requestAnimationFrame(() => {
|
||||
ripple.style.width = '60px';
|
||||
ripple.style.height = '60px';
|
||||
ripple.style.opacity = '0';
|
||||
});
|
||||
|
||||
// Clean up ripple
|
||||
setTimeout(() => ripple.remove(), 500);
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</body></html>
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 138 KiB |
@@ -0,0 +1,307 @@
|
||||
<!DOCTYPE html>
|
||||
|
||||
<html class="dark" lang="en" style=""><head>
|
||||
<meta charset="utf-8"/>
|
||||
<meta content="width=device-width, initial-scale=1.0" name="viewport"/>
|
||||
<title>RIPPR - Rides</title>
|
||||
<script src="https://cdn.tailwindcss.com?plugins=forms,container-queries"></script>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:wght,FILL@100..700,0..1&display=swap" rel="stylesheet"/>
|
||||
<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"/>
|
||||
<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"
|
||||
]
|
||||
},
|
||||
"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"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
<style type="text/tailwindcss">
|
||||
@layer utilities {
|
||||
.glass-panel {
|
||||
@apply bg-surface/80 backdrop-blur-[20px] border border-outline-variant;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body class="bg-background text-on-surface min-h-screen pb-[100px]"><div class="fixed inset-0 z-0 bg-surface"><div class="w-full h-full bg-cover bg-center opacity-30 backdrop-blur-md backdrop-blur-xl" data-alt="A light mode stylized city map view for a fitness tracking application." 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><svg class="absolute inset-0 w-full h-full pointer-events-none" preserveaspectratio="xMidYMid slice" viewbox="0 0 400 800"><path d="M 200 400 C 250 250, 150 150, 300 50" fill="none" opacity="0.3" stroke="#aac7ff" stroke-dasharray="10 10" stroke-linecap="round" stroke-linejoin="round" stroke-width="4"></path></svg></div>
|
||||
<!-- TopAppBar -->
|
||||
<!-- Main Content -->
|
||||
<main class="px-margin-mobile max-w-4xl mx-auto flex flex-col gap-6 pt-2 relative z-10">
|
||||
<!-- Search & Filter -->
|
||||
<div class="flex gap-gutter items-center p-2 rounded-lg border border-outline-variant bg-surface-container-lowest shadow-sm">
|
||||
<div class="flex-1 relative">
|
||||
<span class="material-symbols-outlined absolute left-3 top-1/2 -translate-y-1/2 text-on-surface-variant">search</span>
|
||||
<input class="w-full bg-surface-container-lowest border-b border-outline-variant focus:border-primary text-on-surface pl-10 pr-4 py-2 font-body-md text-body-md outline-none transition-colors placeholder:text-on-surface-variant/70" placeholder="Search rides..." type="text"/>
|
||||
</div>
|
||||
<button class="w-10 h-10 flex items-center justify-center rounded hover:bg-surface-container-highest transition-colors text-primary border border-outline-variant shadow-sm bg-surface-container-lowest">
|
||||
<span class="material-symbols-outlined">tune</span>
|
||||
</button>
|
||||
</div>
|
||||
<!-- Summary Stats -->
|
||||
<div class="grid grid-cols-2 md:grid-cols-4 gap-gutter mb-2 z-10">
|
||||
<div class="p-4 rounded-lg flex flex-col gap-1 bg-surface-container-lowest border border-outline-variant shadow-sm"><span class="font-label-sm text-label-sm tracking-wider text-on-surface-variant uppercase">Total Dist</span><span class="font-headline-md text-headline-md text-primary">482<span class="text-label-sm tracking-wider ml-1 text-on-surface-variant">KM</span></span></div>
|
||||
<div class="p-4 rounded-lg flex flex-col gap-1 bg-surface-container-lowest border border-outline-variant shadow-sm"><span class="font-label-sm text-label-sm tracking-wider text-on-surface-variant uppercase">Total Dist</span><span class="font-headline-md text-headline-md text-primary">482<span class="text-label-sm tracking-wider ml-1 text-on-surface-variant">KM</span></span></div>
|
||||
<div class="p-4 rounded-lg flex flex-col gap-1 bg-surface-container-lowest border border-outline-variant shadow-sm"><span class="font-label-sm text-label-sm tracking-wider text-on-surface-variant uppercase">Total Dist</span><span class="font-headline-md text-headline-md text-primary">482<span class="text-label-sm tracking-wider ml-1 text-on-surface-variant">KM</span></span></div>
|
||||
<div class="p-4 rounded-lg flex flex-col gap-1 bg-surface-container-lowest border border-outline-variant shadow-sm"><span class="font-label-sm text-label-sm tracking-wider text-on-surface-variant uppercase">Total Dist</span><span class="font-headline-md text-headline-md text-primary">482<span class="text-label-sm tracking-wider ml-1 text-on-surface-variant">KM</span></span></div>
|
||||
</div>
|
||||
<!-- Rides List -->
|
||||
<div class="flex flex-col gap-gutter">
|
||||
<!-- Ride Item 1 -->
|
||||
<div class="rounded-lg flex flex-col md:flex-row overflow-hidden group cursor-pointer hover:border-primary/50 transition-colors bg-surface-container-lowest border border-outline-variant shadow-sm">
|
||||
<div class="h-32 md:h-auto md:w-48 bg-surface-container-highest relative">
|
||||
<img class="w-full h-full object-cover opacity-90 group-hover:opacity-100 transition-opacity" data-alt="A dark mode minimalist map view showing a neon green GPS route line traversing through city blocks. High contrast, technical aesthetic, muted dark charcoal background." data-location="San Francisco" src="https://lh3.googleusercontent.com/aida-public/AB6AXuAx1uDTgkMgXUBwI-JLBmH1_3Mw1sk91ovk-19wP19I5wktrsuJ9nM757sCThvGGNFlWi88pEzXj9cB_wlHAhQoTdkoXYp1csaGHd6e_iulV84cPEhZNlj6_M-EWzoEdv7CT-q9BJLlkOBPq9D5IaX6mQk1nqZQDfeR0k535iIYJ2rXJFCyAAgCEF0gKEYyumLsUopkQMD2ig13W01XANoAJPCP--li-S1iln0VfzpqY-vmyHLZsO9H"/>
|
||||
<div class="absolute inset-0 bg-gradient-to-t from-surface/80 to-transparent md:hidden"></div>
|
||||
</div>
|
||||
<div class="p-4 flex flex-col justify-between flex-1 gap-4">
|
||||
<div>
|
||||
<h3 class="font-headline-sm text-headline-sm text-on-surface mb-1">Morning Circuit</h3>
|
||||
<p class="font-body-md text-body-md text-on-surface-variant">Oct 24, 06:30 AM</p>
|
||||
</div>
|
||||
<div class="flex gap-6">
|
||||
<div class="flex flex-col">
|
||||
<span class="font-label-sm text-label-sm tracking-wider text-on-surface-variant mb-1 uppercase">DISTANCE</span>
|
||||
<span class="font-headline-sm text-headline-sm text-primary">42.5 KM</span>
|
||||
</div>
|
||||
<div class="flex flex-col">
|
||||
<span class="font-label-sm text-label-sm tracking-wider text-on-surface-variant mb-1 uppercase">TIME</span>
|
||||
<span class="font-headline-sm text-headline-sm text-primary">1:45:22</span>
|
||||
</div>
|
||||
<div class="flex flex-col">
|
||||
<span class="font-label-sm text-label-sm tracking-wider text-on-surface-variant mb-1 uppercase">AVG SPD</span>
|
||||
<span class="font-headline-sm text-headline-sm text-primary">24.2 KPH</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Ride Item 2 -->
|
||||
<div class="rounded-lg flex flex-col md:flex-row overflow-hidden group cursor-pointer hover:border-primary/50 transition-colors bg-surface-container-lowest border border-outline-variant shadow-sm">
|
||||
<div class="h-32 md:h-auto md:w-48 bg-surface-container-highest relative">
|
||||
<img class="w-full h-full object-cover opacity-90 group-hover:opacity-100 transition-opacity" data-alt="A dark mode minimalist map view showing a neon green GPS route line winding through a mountainous region. High contrast, technical aesthetic, deep black background." data-location="Marin Headlands" src="https://lh3.googleusercontent.com/aida-public/AB6AXuA8dTKlnX4_8dnTgJ0UQpocYH5zvSgIxrAirD88D7z-dcBw6SziTL2p8niEuvn-Q-z1M3AwecI2VoHbFXE70wCOQEymO_yZatnGuP5BkKz-orUNu3Lpmy3ZnXrxYVUOF77CdUnY6WWo098sJre88SDaK82aid7U4mo6vcV5DtxUqQ-H9D0N6RvTzWc0gmev36tcOCKDZqlUsRiHMarZOD6QHiPfL7HzGu_KN1gdDF1n-7G0vLs6CzmA"/>
|
||||
<div class="absolute inset-0 bg-gradient-to-t from-surface/80 to-transparent md:hidden"></div>
|
||||
</div>
|
||||
<div class="p-4 flex flex-col justify-between flex-1 gap-4">
|
||||
<div>
|
||||
<h3 class="font-headline-sm text-headline-sm text-on-surface mb-1">Hill Repeats</h3>
|
||||
<p class="font-body-md text-body-md text-on-surface-variant">Oct 22, 17:15 PM</p>
|
||||
</div>
|
||||
<div class="flex gap-6">
|
||||
<div class="flex flex-col">
|
||||
<span class="font-label-sm text-label-sm tracking-wider text-on-surface-variant mb-1 uppercase">DISTANCE</span>
|
||||
<span class="font-headline-sm text-headline-sm text-primary">28.1 KM</span>
|
||||
</div>
|
||||
<div class="flex flex-col">
|
||||
<span class="font-label-sm text-label-sm tracking-wider text-on-surface-variant mb-1 uppercase">TIME</span>
|
||||
<span class="font-headline-sm text-headline-sm text-primary">1:12:05</span>
|
||||
</div>
|
||||
<div class="flex flex-col">
|
||||
<span class="font-label-sm text-label-sm tracking-wider text-on-surface-variant mb-1 uppercase">AVG SPD</span>
|
||||
<span class="font-headline-sm text-headline-sm text-primary">23.4 KPH</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Ride Item 3 -->
|
||||
<div class="rounded-lg flex flex-col md:flex-row overflow-hidden group cursor-pointer hover:border-primary/50 transition-colors opacity-75 bg-surface-container-lowest border border-outline-variant shadow-sm">
|
||||
<div class="h-32 md:h-auto md:w-48 bg-surface-container-highest relative">
|
||||
<img class="w-full h-full object-cover opacity-90 group-hover:opacity-100 transition-opacity grayscale group-hover:grayscale-0" data-alt="A dark mode minimalist map view showing a neon green GPS route line along a coastal highway. High contrast, technical aesthetic, muted dark charcoal background." data-location="Highway 1" src="https://lh3.googleusercontent.com/aida-public/AB6AXuAr-gumM8Tc0ayTzCw3s39Z1j0cydRKq7oGECkbJNXERMpAQaP4xrvoVkTVHzGe7mG83pDzxMMZGy9SpTk19uSKlnM5Vjoz3zurdx6AbWLUisqVcuB6CjXS4wt_hJlUK4TfOICbZdPN2UzCkVYUHClJ1jwfeRwA0SFVxWC2gfneSIXny-aaJMfddGQCFtCB7Xjh6hoFigOGUZB-2yjvcHcgwh_u3hZXLDETlfCtPZmyedY1zuZJ9Lft"/>
|
||||
<div class="absolute inset-0 bg-gradient-to-t from-surface/80 to-transparent md:hidden"></div>
|
||||
</div>
|
||||
<div class="p-4 flex flex-col justify-between flex-1 gap-4">
|
||||
<div>
|
||||
<h3 class="font-headline-sm text-headline-sm text-on-surface mb-1">Coastal Cruise</h3>
|
||||
<p class="font-body-md text-body-md text-on-surface-variant">Oct 18, 08:00 AM</p>
|
||||
</div>
|
||||
<div class="flex gap-6">
|
||||
<div class="flex flex-col">
|
||||
<span class="font-label-sm text-label-sm tracking-wider text-on-surface-variant mb-1 uppercase">DISTANCE</span>
|
||||
<span class="font-headline-sm text-headline-sm text-primary">85.4 KM</span>
|
||||
</div>
|
||||
<div class="flex flex-col">
|
||||
<span class="font-label-sm text-label-sm tracking-wider text-on-surface-variant mb-1 uppercase">TIME</span>
|
||||
<span class="font-headline-sm text-headline-sm text-primary">3:24:10</span>
|
||||
</div>
|
||||
<div class="flex flex-col">
|
||||
<span class="font-label-sm text-label-sm tracking-wider text-on-surface-variant mb-1 uppercase">AVG SPD</span>
|
||||
<span class="font-headline-sm text-headline-sm text-primary">25.1 KPH</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
<!-- BottomNavBar -->
|
||||
<nav class="fixed bottom-0 w-full z-50 pb-safe bg-surface-container-lowest/80 backdrop-blur-xl border-t border-outline-variant flex justify-around items-center h-20 px-margin-mobile md:hidden"><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;">map</span></button><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;">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></nav>
|
||||
<!-- Desktop Side Nav Overlay (Hidden on Mobile, replaces Bottom Nav visually in a real app, keeping it simple here) -->
|
||||
<nav class="hidden md:flex fixed left-0 top-16 bottom-0 w-24 bg-surface-container-lowest/80 backdrop-blur-md border-r border-outline-variant flex-col items-center py-6 gap-6 z-40">
|
||||
<button class="w-12 h-12 flex items-center justify-center rounded hover:bg-surface-container-highest text-on-surface-variant transition-colors group relative">
|
||||
<span class="material-symbols-outlined">map</span>
|
||||
<span class="absolute left-14 bg-surface-container px-2 py-1 rounded font-label-sm text-label-sm tracking-wider text-on-surface opacity-0 group-hover:opacity-100 pointer-events-none transition-opacity">Map</span>
|
||||
</button>
|
||||
<button class="w-12 h-12 flex items-center justify-center rounded bg-primary-container/50 text-primary transition-colors group relative border border-primary/20"><span class="material-symbols-outlined" style='font-variation-settings: "FILL" 1;'>book</span><span class="absolute left-14 bg-surface-container px-2 py-1 rounded font-label-sm text-label-sm tracking-wider text-on-surface opacity-0 group-hover:opacity-100 pointer-events-none transition-opacity">Journal</span></button>
|
||||
<button class="w-12 h-12 flex items-center justify-center rounded hover:bg-surface-container-highest text-on-surface-variant transition-colors group relative">
|
||||
<span class="material-symbols-outlined">route</span>
|
||||
<span class="absolute left-14 bg-surface-container px-2 py-1 rounded font-label-sm text-label-sm tracking-wider text-on-surface opacity-0 group-hover:opacity-100 pointer-events-none transition-opacity">Plan</span>
|
||||
</button>
|
||||
</nav>
|
||||
<style>
|
||||
@media (min-width: 768px) {
|
||||
body { padding-left: 96px; pb: 0; }
|
||||
}
|
||||
</style>
|
||||
</body></html>
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 78 KiB |
@@ -0,0 +1,275 @@
|
||||
<!DOCTYPE html>
|
||||
|
||||
<html class="dark" lang="en" style=""><head>
|
||||
<meta charset="utf-8"/>
|
||||
<meta content="width=device-width, initial-scale=1.0" name="viewport"/>
|
||||
<title>RIPPR - Plan Route</title>
|
||||
<script src="https://cdn.tailwindcss.com?plugins=forms,container-queries"></script>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:wght,FILL@100..700,0..1&display=swap" rel="stylesheet"/>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600&family=JetBrains+Mono:wght@400;500;700&family=Sora:wght@600;700&display=swap" rel="stylesheet"/>
|
||||
<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",
|
||||
"unit": "4px",
|
||||
"stack-sm": "8px",
|
||||
"stack-lg": "48px",
|
||||
"stack-md": "24px",
|
||||
"container-margin": "20px"
|
||||
},
|
||||
"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"],
|
||||
"label-caps": ["JetBrains Mono"]
|
||||
},
|
||||
"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" }],
|
||||
"label-caps": ["12px", { "lineHeight": "16px", "letterSpacing": "0.1em", "fontWeight": "500" }]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style>
|
||||
.map-grid-overlay {
|
||||
background-image:
|
||||
linear-gradient(to right, rgba(255,255,255,0.05) 1px, transparent 1px),
|
||||
linear-gradient(to bottom, rgba(255,255,255,0.05) 1px, transparent 1px);
|
||||
background-size: 40px 40px;
|
||||
}
|
||||
|
||||
.pulse-ring {
|
||||
animation: pulse-ring 2s cubic-bezier(0.215, 0.61, 0.355, 1) infinite;
|
||||
}
|
||||
|
||||
@keyframes pulse-ring {
|
||||
0% {
|
||||
transform: scale(0.8);
|
||||
opacity: 0.8;
|
||||
box-shadow: 0 0 0 0 rgba(170, 199, 255, 0.5);
|
||||
}
|
||||
70% {
|
||||
transform: scale(1);
|
||||
opacity: 0;
|
||||
box-shadow: 0 0 0 20px rgba(170, 199, 255, 0);
|
||||
}
|
||||
100% {
|
||||
transform: scale(0.8);
|
||||
opacity: 0;
|
||||
box-shadow: 0 0 0 0 rgba(170, 199, 255, 0);
|
||||
}
|
||||
}
|
||||
|
||||
.route-line {
|
||||
stroke-dasharray: 10, 10;
|
||||
animation: dash 20s linear infinite;
|
||||
}
|
||||
|
||||
@keyframes dash {
|
||||
to {
|
||||
stroke-dashoffset: -1000;
|
||||
}
|
||||
}
|
||||
|
||||
.tooltip-float {
|
||||
animation: float 3s ease-in-out infinite;
|
||||
}
|
||||
|
||||
@keyframes float {
|
||||
0% { transform: translateY(0px); }
|
||||
50% { transform: translateY(-8px); }
|
||||
100% { transform: translateY(0px); }
|
||||
}
|
||||
</style>
|
||||
<style>
|
||||
body {
|
||||
min-height: max(884px, 100dvh);
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body class="bg-background text-on-surface h-screen w-full overflow-hidden relative selection:bg-primary-container selection:text-on-primary-container antialiased flex flex-col">
|
||||
<!-- Fullscreen Map Canvas -->
|
||||
<main class="flex-grow relative h-full w-full z-0 overflow-hidden">
|
||||
<div class="absolute top-4 left-1/2 -translate-x-1/2 z-50 w-auto px-container-margin pointer-events-none mt-4">
|
||||
<div class="bg-surface/90 backdrop-blur-xl border border-outline-variant/30 rounded-full px-8 py-4 flex items-center gap-8 shadow-sm pointer-events-auto">
|
||||
<div class="flex flex-col items-center">
|
||||
<span class="text-on-surface-variant font-headline-md text-[10px] uppercase tracking-widest mb-1">Distance</span>
|
||||
<span class="font-headline-md text-primary-container">12.4<span class="text-sm ml-1">km</span></span>
|
||||
</div>
|
||||
<div class="w-px h-8 bg-outline-variant/30"></div>
|
||||
<div class="flex flex-col items-center">
|
||||
<span class="text-on-surface-variant font-headline-md text-[10px] uppercase tracking-widest mb-1">Est. Time</span>
|
||||
<span class="font-headline-md text-primary-container">45<span class="text-sm ml-1">m</span></span>
|
||||
</div>
|
||||
<div class="w-px h-8 bg-outline-variant/30"></div>
|
||||
<div class="flex flex-col items-center">
|
||||
<span class="text-on-surface-variant font-headline-md text-[10px] uppercase tracking-widest mb-1">Pins</span>
|
||||
<span class="font-headline-md text-primary-container">5</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Base Map Layer -->
|
||||
<div class="absolute inset-0 z-0 bg-surface-container">
|
||||
<img class="w-full h-full object-cover opacity-30 mix-blend-screen" data-alt="A digital map showing a top-down view of city streets and terrain." data-location="San Francisco, CA" src="https://lh3.googleusercontent.com/aida-public/AB6AXuDtux0iJ6r19x5fCEWn0L1Ai3ziybnKhwYCd-8xyKm8ebAQHTJPdulojF6Pi_31mtsUlhqpI5ejusGg8Rk7y9c20eWK9sqj3uubky70kqSRiHsd37IJzOiRUTYdHCyOg2g7K5uADAg-39nR48gfWmfotW13eKGb1I9OMrc8MJBtj6lD9rtqapC2DG8kLoTz50c_QCt_AVShQyLrft6yW1SShV4mZXP2kwhf4AZsh3bvkzeCQzfIB43E"/>
|
||||
</div>
|
||||
<!-- Technical Grid Overlay -->
|
||||
<div class="absolute inset-0 map-grid-overlay z-10 pointer-events-none"></div>
|
||||
<!-- Planned Route Overlay (SVG) -->
|
||||
<svg class="absolute inset-0 w-full h-full z-20 pointer-events-none" preserveaspectratio="xMidYMid slice" viewbox="0 0 1000 1000">
|
||||
<path class="route-line opacity-80" d="M 400 400 L 450 420 L 500 380 L 550 430 L 600 400" fill="none" stroke="#4090fe" stroke-dasharray="10, 10" stroke-linecap="round" stroke-linejoin="round" stroke-width="4"></path>
|
||||
<circle cx="400" cy="400" fill="#4090fe" r="6" stroke="#131313" stroke-width="2"></circle>
|
||||
<circle cx="450" cy="420" fill="#4090fe" r="6" stroke="#131313" stroke-width="2"></circle>
|
||||
<circle cx="500" cy="380" fill="#4090fe" r="6" stroke="#131313" stroke-width="2"></circle>
|
||||
<circle cx="550" cy="430" fill="#4090fe" r="6" stroke="#131313" stroke-width="2"></circle>
|
||||
<circle cx="600" cy="400" fill="#4090fe" r="6" stroke="#131313" stroke-width="2"></circle>
|
||||
</svg>
|
||||
<!-- User Location Marker (Pulsing) -->
|
||||
<div class="absolute top-[70%] left-[30%] z-30 transform -translate-x-1/2 -translate-y-1/2 flex items-center justify-center pointer-events-none">
|
||||
<div class="absolute w-4 h-4 bg-primary-container rounded-full opacity-30"></div>
|
||||
<div class="absolute w-3 h-3 bg-primary-container rounded-full pulse-ring"></div>
|
||||
<div class="w-2 h-2 bg-primary-container rounded-full relative z-10 shadow-[0_0_10px_rgba(64,144,254,0.5)]"></div>
|
||||
<!-- Vision cone indicator -->
|
||||
<div class="absolute -top-12 left-1/2 -translate-x-1/2 w-0 h-0 border-l-[20px] border-l-transparent border-r-[20px] border-r-transparent border-t-[40px] border-t-primary-container/20 origin-bottom transform rotate-45"></div>
|
||||
</div>
|
||||
<!-- Interactive Map Canvas Layer (Invisible, captures clicks for dropping pins) -->
|
||||
<div class="absolute inset-0 z-40 cursor-crosshair" id="map-interaction-layer"></div>
|
||||
</main>
|
||||
<!-- Floating HUD Elements Layer -->
|
||||
<div class="absolute inset-0 z-50 pointer-events-none flex flex-col justify-end">
|
||||
<!-- Tooltip -->
|
||||
<div class="w-full flex justify-center mb-stack-md pointer-events-none px-container-margin">
|
||||
<button class="pointer-events-auto bg-primary-container text-on-primary-container font-headline-md text-label-caps px-12 py-4 rounded-xl shadow-md uppercase tracking-widest hover:brightness-110 active:scale-95 transition-all">Start Route</button>
|
||||
</div>
|
||||
<!-- Bottom Navigation Bar (Generated from JSON) -->
|
||||
<nav class="bg-surface border-t border-outline-variant/30 w-full pb-safe flex justify-around items-center h-20 px-container-margin pointer-events-auto shrink-0 relative shadow-[0_-4px_20px_rgba(0,0,0,0.2)]">
|
||||
<button class="flex flex-col items-center justify-center text-on-surface-variant hover:text-primary-container transition-colors duration-150">
|
||||
<span class="material-symbols-outlined mb-1" style="font-variation-settings: 'FILL' 0;">map</span>
|
||||
</button>
|
||||
<button class="flex flex-col items-center justify-center text-primary-container relative after:content-[''] after:absolute after:-bottom-1 after:w-1 after:h-1 after:bg-primary-container after:rounded-full hover:text-primary-container transition-colors duration-150 scale-95">
|
||||
<span class="material-symbols-outlined" style="font-variation-settings: 'FILL' 1;">book</span>
|
||||
</button>
|
||||
<button class="flex flex-col items-center justify-center text-on-surface-variant hover:text-primary-container transition-colors 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-container transition-colors duration-150">
|
||||
<span class="material-symbols-outlined mb-1" style="font-variation-settings: 'FILL' 0;">settings</span>
|
||||
</button>
|
||||
</nav>
|
||||
</div>
|
||||
<!-- Micro-interaction Script for Pin Dropping Simulation -->
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
const mapLayer = document.getElementById('map-interaction-layer');
|
||||
const svgLayer = document.querySelector('svg');
|
||||
let pinCount = 0;
|
||||
|
||||
mapLayer.addEventListener('click', (e) => {
|
||||
const rect = mapLayer.getBoundingClientRect();
|
||||
const x = e.clientX - rect.left;
|
||||
const y = e.clientY - rect.top;
|
||||
|
||||
// Visual feedback of drop
|
||||
const ripple = document.createElement('div');
|
||||
ripple.className = 'absolute rounded-full border-2 border-primary-container bg-primary-container/20 pointer-events-none transform -translate-x-1/2 -translate-y-1/2';
|
||||
ripple.style.left = `${x}px`;
|
||||
ripple.style.top = `${y}px`;
|
||||
ripple.style.width = '0px';
|
||||
ripple.style.height = '0px';
|
||||
ripple.style.opacity = '1';
|
||||
ripple.style.transition = 'all 0.5s cubic-bezier(0.165, 0.84, 0.44, 1)';
|
||||
|
||||
mapLayer.parentElement.appendChild(ripple);
|
||||
|
||||
// Trigger animation
|
||||
requestAnimationFrame(() => {
|
||||
ripple.style.width = '60px';
|
||||
ripple.style.height = '60px';
|
||||
ripple.style.opacity = '0';
|
||||
});
|
||||
|
||||
// Clean up ripple
|
||||
setTimeout(() => ripple.remove(), 500);
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</body></html>
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 83 KiB |
Reference in New Issue
Block a user