Redesign main app: slim top bar, dynamic sky hero, nowcast, scrub chart, unified palette

- One slate/blue palette across the app, dashboard, account/login pages (data colours
  stay orange = warm, blue = cool on the dashboard)
- Slim sticky top bar with location chip, gear (settings) and ? (learn); 4-tab nav
  (Now, Station, Map, Alerts) with a thumb-reach bottom bar on phones
- Hero sky gradient by condition/time with rain, storm, snow and star effects
  (respects reduced motion); keeps the searched city and badges when temps come from your station
- One-line summary, 'Next 2 hours' bars and rain countdown from 15-minute precipitation,
  rain/cold-aware 'Bring' chips; yesterday + 'vs usual' context from the station
- Scrubbable hourly chart (temp, rain chance, wind), 7-day range bars, AQI gauge,
  details tiles instead of drawers, taller radar with location marker + alert polygons
- Skeleton loading instead of a full-screen spinner, sticky section headers, safe areas
- Server: minutely_15 precipitation, public /api/station/summary

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
Nate EmmertandClaude Sonnet 5.5 committed 2026-10-15 12:56:18 -04:00
1 parent 1830fb2e2b
commit fd61e35b56
16 files changed
+846 -221

No files matched your search

+115 -143
View File
@@ -6,8 +6,8 @@
<title>NeCloud Weather</title>
<link rel="manifest" href="/manifest.json">
<link rel="apple-touch-icon" href="/icons/icon.svg">
<meta name="theme-color" content="#f5f2ec" media="(prefers-color-scheme: light)">
<meta name="theme-color" content="#191510" media="(prefers-color-scheme: dark)">
<meta name="theme-color" content="#edf2f7" media="(prefers-color-scheme: light)">
<meta name="theme-color" content="#070c14" media="(prefers-color-scheme: dark)">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
<meta name="apple-mobile-web-app-title" content="NeCloud Weather">
@@ -18,37 +18,36 @@
<link href="https://fonts.googleapis.com/css2?family=Barlow+Condensed:wght@700;800&family=Barlow:wght@400;500;600&display=swap" rel="stylesheet">
<link rel="stylesheet" href="style.css">
<link rel="stylesheet" href="station-live.css">
<link rel="stylesheet" href="app-ui.css">
</head>
<body>
<div id="offline-banner" class="offline-banner hidden">No internet connection — showing cached data</div>
<div id="ptr-indicator" class="ptr-indicator hidden">↓ Refreshing…</div>
<div class="app">
<header class="masthead">
<h1>NeCloud Weather</h1>
<nav class="tabs">
<button class="tab active" data-tab="conditions">Conditions</button>
<header class="topbar">
<a class="brand" href="/" aria-label="NeCloud Weather home"><span class="brand-mark">☁︎</span><span class="brand-name">NeCloud</span></a>
<nav class="tabs" aria-label="Sections">
<button class="tab active" data-tab="conditions">Now</button>
<button class="tab" data-tab="station">Station</button>
<button class="tab" data-tab="unified-map">Map</button>
<button class="tab" data-tab="alerts">Alerts<span class="alert-badge hidden" id="alert-badge-nav"></span></button>
<button class="tab" data-tab="learn">Learn</button>
<button class="tab" data-tab="settings">Settings</button>
</nav>
<span class="tagline">Open-Meteo · RainViewer</span>
<nav class="station-links">
<a href="/dashboard" class="station-link">Dashboard</a>
</nav>
<div class="topbar-actions">
<button class="loc-chip" id="tb-search" type="button" aria-label="Change location"><span class="loc-chip-ico">⌕</span><span id="tb-loc">Location</span></button>
<a class="icon-btn" href="/dashboard" title="Station dashboard" aria-label="Station dashboard">▤</a>
<button class="icon-btn" data-tab="learn" type="button" title="Learn" aria-label="Learn">?</button>
<button class="icon-btn" data-tab="settings" type="button" title="Settings" aria-label="Settings">⚙</button>
</div>
</header>
<!-- Loading -->
<div id="loading" class="loading hidden">
<div class="loading-container">
<div class="loading-icon">🌤️</div>
<div class="loading-text">Fetching weather data…</div>
<div class="loading-dots">
<span></span><span></span><span></span>
</div>
</div>
<!-- Loading: skeleton of the Now tab -->
<div id="loading" class="loading hidden" aria-busy="true" aria-label="Loading weather">
<div class="sk sk-hero"></div>
<div class="sk sk-line"></div>
<div class="sk sk-card"></div>
<div class="sk sk-card tall"></div>
<div class="sk-rows"><div class="sk sk-row"></div><div class="sk sk-row"></div><div class="sk sk-row"></div></div>
</div>
<!-- ── TAB: CONDITIONS ── -->
@@ -66,8 +65,9 @@
</div>
</div>
<!-- ── HERO ── -->
<div class="hero">
<!-- ── HERO (dynamic sky) ── -->
<div class="hero" id="hero">
<canvas id="sky-fx" class="sky-fx" aria-hidden="true"></canvas>
<div class="hero-left">
<div id="cur-icon" class="hero-icon"></div>
<div id="cur-desc" class="hero-desc"></div>
@@ -77,6 +77,7 @@
<div class="hero-center">
<div class="hero-temp"><span id="cur-temp">--</span><span class="deg">°F</span></div>
<div class="feels" id="feels-line">Feels like <span id="cur-feels">--</span>°F</div>
<div class="hero-context hidden" id="hero-context"></div>
<button id="share-hero-btn" class="hero-share-btn" type="button">Share hero card</button>
<p id="share-hero-status" class="hero-share-status hidden"></p>
</div>
@@ -97,107 +98,40 @@
</div>
</div>
<!-- ── SUN & MOON TIMELINE ── -->
<div class="sun-section">
<div class="sun-timeline-wrap">
<div class="sun-endpoint">
<span class="sun-ep-icon">🌅</span>
<span id="sunrise-val" class="sun-ep-time">--</span>
</div>
<div class="sun-track-wrap">
<div class="sun-track">
<div class="sun-progress" id="sun-progress"></div>
<div class="sun-dot" id="sun-dot">☀️</div>
</div>
</div>
<div class="sun-endpoint">
<span class="sun-ep-icon">🌇</span>
<span id="sunset-val" class="sun-ep-time">--</span>
</div>
<!-- ── ONE-LINE SUMMARY ── -->
<div class="summary-line" id="summary-line"></div>
<!-- ── NEXT 2 HOURS + WHAT TO BRING ── -->
<section class="ui-card nowcast" id="nowcast">
<div class="section-head">
<span class="section-label">Next 2 hours</span>
<span class="section-note" id="nowcast-note"></span>
</div>
<div class="moon-row">
<span class="moon-emoji">🌙</span>
<span id="moon-phase-val">--</span>
<span class="daylight-info" id="daylight-info">--</span>
<div class="nowcast-bars" id="nowcast-bars"></div>
<div class="bring-row">
<span class="bring-label">Bring</span>
<div id="recommendations" class="recs-grid"></div>
</div>
</section>
<!-- ── HOURLY ── -->
<div class="section-head sticky-head">
<span class="section-label">Hourly Forecast</span>
<span class="section-note">
<button id="hourly-feels-toggle" class="link-btn">Feels-like</button>
<button id="hourly-extend-btn" class="link-btn">48h</button>
</span>
</div>
<div class="ui-card hourly-card">
<div class="hc-readout" id="hc-readout"></div>
<div id="hourly-chart" class="hourly-chart" aria-label="Hourly temperature, rain chance and wind chart"></div>
</div>
<div id="hourly-scroll" class="hourly-scroll">
<div id="hourly-track" class="hourly-track"></div>
</div>
<!-- ── WIND & ATMOSPHERE (collapsed by default on mobile) ── -->
<details class="details-drawer wind-drawer" id="wind-drawer">
<summary class="drawer-summary">
Wind &amp; Atmosphere
<span class="wind-drawer-peek" id="wind-drawer-peek"></span>
<span class="drawer-chevron">›</span>
</summary>
<div class="drawer-body">
<div class="card-group drawer-card-group">
<div class="stat-card">
<div class="stat-label">Wind</div>
<div class="stat-val"><span id="cur-wind">--</span> <span class="stat-unit wind-unit">mph</span></div>
<div class="stat-sub" id="cur-wind-dir">--</div>
</div>
<div class="stat-card">
<div class="stat-label">Gusts</div>
<div class="stat-val"><span id="cur-gust">--</span> <span class="stat-unit gust-unit">mph</span></div>
</div>
<div class="stat-card">
<div class="stat-label">Humidity</div>
<div class="stat-val"><span id="cur-hum">--</span><span class="stat-unit">%</span></div>
<div class="stat-sub" id="hum-feel"></div>
</div>
<div class="stat-card">
<div class="stat-label">Dewpoint</div>
<div class="stat-val"><span id="cur-dew">--</span>°</div>
</div>
<div class="stat-card">
<div class="stat-label">Pressure</div>
<div class="stat-val"><span id="cur-pres">--</span> <span class="stat-unit">hPa</span></div>
</div>
<div class="stat-card">
<div class="stat-label">Visibility</div>
<div class="stat-val"><span id="cur-vis">--</span> <span class="stat-unit vis-unit">mi</span></div>
</div>
</div>
</div>
</details>
<!-- ── MORE DETAILS DRAWER ── -->
<details class="details-drawer">
<summary class="drawer-summary">More Details <span class="drawer-chevron">›</span></summary>
<div class="drawer-body">
<div class="card-group drawer-card-group">
<div class="stat-card">
<div class="stat-label">UV Index</div>
<div class="stat-val"><span id="cur-uv">--</span></div>
</div>
<div class="stat-card">
<div class="stat-label">Cloud Cover</div>
<div class="stat-val"><span id="cur-cloud">--</span><span class="stat-unit">%</span></div>
</div>
<div class="stat-card">
<div class="stat-label">US AQI</div>
<div class="stat-val"><span id="aqi-us">--</span></div>
</div>
<div class="stat-card">
<div class="stat-label">PM2.5</div>
<div class="stat-val"><span id="pm25">--</span></div>
</div>
<div class="stat-card">
<div class="stat-label">Pollen</div>
<div class="stat-val"><span id="pollen">--</span></div>
</div>
</div>
</div>
</details>
<!-- ── WHAT TO BRING ── -->
<div class="section-head" style="margin-top:1.5rem">
<span class="section-label">What to Bring</span>
</div>
<div id="recommendations" class="recs-grid"></div>
<!-- Embedded radar panel -->
<div class="section-head">
<!-- ── RADAR ── -->
<div class="section-head sticky-head">
<span class="section-label">Radar</span>
<span class="section-note">
<button class="link-btn" id="open-map-tab">Open map →</button>
@@ -214,26 +148,71 @@
</div>
</div>
<!-- Hourly -->
<div class="section-head" style="margin-top:1.5rem">
<span class="section-label">Hourly Forecast</span>
<span class="section-note">
<button id="hourly-feels-toggle" class="link-btn">Feels-like</button>
<button id="hourly-extend-btn" class="link-btn">48h</button>
</span>
</div>
<div id="hourly-scroll" class="hourly-scroll">
<div id="hourly-track" class="hourly-track"></div>
</div>
<div id="precip-chart-wrap" class="precip-chart-wrap"></div>
<!-- 7-day -->
<div class="section-head">
<!-- ── 7-DAY ── -->
<div class="section-head sticky-head">
<span id="daily-forecast-label" class="section-label">7-Day Forecast</span>
<button id="daily-toggle-btn" class="link-btn" type="button">Show 16-day</button>
</div>
<div id="daily-list" class="daily-list"></div>
<!-- ── DETAILS TILES ── -->
<div class="section-head sticky-head">
<span class="section-label">Details</span>
<span class="section-note" id="wind-drawer-peek"></span>
</div>
<div class="detail-grid">
<div class="stat-card">
<div class="stat-label">Wind</div>
<div class="stat-val"><span id="cur-wind">--</span> <span class="stat-unit wind-unit">mph</span></div>
<div class="stat-sub" id="cur-wind-dir">--</div>
</div>
<div class="stat-card">
<div class="stat-label">Gusts</div>
<div class="stat-val"><span id="cur-gust">--</span> <span class="stat-unit gust-unit">mph</span></div>
</div>
<div class="stat-card">
<div class="stat-label">Humidity</div>
<div class="stat-val"><span id="cur-hum">--</span><span class="stat-unit">%</span></div>
<div class="stat-sub" id="hum-feel"></div>
</div>
<div class="stat-card">
<div class="stat-label">Dewpoint</div>
<div class="stat-val"><span id="cur-dew">--</span>°</div>
</div>
<div class="stat-card">
<div class="stat-label">Pressure</div>
<div class="stat-val"><span id="cur-pres">--</span> <span class="stat-unit">hPa</span></div>
</div>
<div class="stat-card">
<div class="stat-label">Visibility</div>
<div class="stat-val"><span id="cur-vis">--</span> <span class="stat-unit vis-unit">mi</span></div>
</div>
<div class="stat-card">
<div class="stat-label">UV Index</div>
<div class="stat-val"><span id="cur-uv">--</span></div>
<div class="stat-sub" id="uv-cat"></div>
</div>
<div class="stat-card">
<div class="stat-label">Cloud Cover</div>
<div class="stat-val"><span id="cur-cloud">--</span><span class="stat-unit">%</span></div>
</div>
<div class="stat-card aqi-card">
<div class="stat-label">Air Quality (US AQI)</div>
<div class="stat-val"><span id="aqi-us">--</span> <span class="aqi-cat" id="aqi-cat"></span></div>
<div class="aqi-track"><span class="aqi-marker" id="aqi-marker"></span></div>
<div class="stat-sub">PM2.5 <span id="pm25">--</span> · Pollen <span id="pollen">--</span></div>
</div>
<div class="stat-card sun-tile">
<div class="stat-label">Sun &amp; Moon</div>
<div class="sun-timeline-wrap">
<div class="sun-endpoint"><span class="sun-ep-icon">🌅</span><span id="sunrise-val" class="sun-ep-time">--</span></div>
<div class="sun-track-wrap"><div class="sun-track"><div class="sun-progress" id="sun-progress"></div><div class="sun-dot" id="sun-dot">☀️</div></div></div>
<div class="sun-endpoint"><span class="sun-ep-icon">🌇</span><span id="sunset-val" class="sun-ep-time">--</span></div>
</div>
<div class="moon-row"><span class="moon-emoji">🌙</span><span id="moon-phase-val">--</span><span class="daylight-info" id="daylight-info">--</span></div>
</div>
</div>
<details class="details-drawer discussion-drawer" id="discussion-drawer">
<summary class="drawer-summary">
Forecaster's Discussion
@@ -1296,19 +1275,12 @@
<span class="bottom-label">Alerts</span>
<span class="alert-badge hidden" id="alert-badge-bottom"></span>
</button>
<button class="bottom-tab" data-tab="learn" type="button">
<span class="bottom-icon">📘</span>
<span class="bottom-label">Learn</span>
</button>
<button class="bottom-tab" data-tab="settings" type="button">
<span class="bottom-icon">⚙️</span>
<span class="bottom-label">Settings</span>
</button>
</nav>
<script src="https://unpkg.com/maplibre-gl@4.7.1/dist/maplibre-gl.js"></script>
<script src="app.js"></script>
<script src="station-live.js"></script>
<script src="app-ui.js"></script>
<script>
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('/sw.js');