The frontend was classic scripts sharing one global scope, with a
load-order contract enforced only by comments (leaflet -> nav ->
shared -> mappicker -> page) and hand-rolled window.Thermograph /
window.LocationPicker namespaces. Page scripts now import what they use;
the dependency graph replaces the ordering contract, and no app globals
remain (Leaflet stays a classic script / global L, loaded first).
nav.js had grown four concerns; it's now three single-purpose modules:
- nav.js: last-location memory + header view-links + locHash.
- units.js: the °F/°C toggle and unit-aware formatting. The compare
special case is gone — pages that want the toggle import units.js;
compare simply doesn't.
- cache.js: the IndexedDB response cache, SWR getJSON, bundle-seeded
view prefetch and neighbor warming. prefetchViews(lat, lon, ownViews)
now takes the calling page's own view names instead of a page-identity
map (VIEW_OWN) — adding a page no longer means editing this module.
The slice->URL map stays here as bundle-contract knowledge.
frontend/package.json ({"type": "module"}) makes CI's node --check
parse the files as modules.
Verified: node --check on all files as modules; 108 backend tests;
headless-Chromium smoke across all five pages against live data — zero
console/page errors, all render assertions pass (cards, chart, calendar
grid + metric switch, ladders, compare ranking, legend scales).
73 lines
3.3 KiB
HTML
73 lines
3.3 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="utf-8" />
|
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
<title>Thermograph — how unusual is your weather?</title>
|
|
<meta name="description" content="Pick any point on Earth and see how its recent weather stacks up against ~45 years of local climate history — graded days, calendars, forecasts, and side-by-side comparisons." />
|
|
<meta name="theme-color" content="#f0803c" />
|
|
<!-- Link previews (Discord, Slack, iMessage…). The server fills the og:url /
|
|
og:image origins in from this request's scheme://host + base path —
|
|
crawlers need absolute URLs and don't run JS. -->
|
|
<meta property="og:type" content="website" />
|
|
<meta property="og:site_name" content="Thermograph" />
|
|
<meta property="og:title" content="Thermograph — how unusual is your weather?" />
|
|
<meta property="og:description" content="Pick any point on Earth and see how its recent weather stacks up against ~45 years of local climate history — graded days, calendars, forecasts, and side-by-side comparisons." />
|
|
<meta property="og:url" content="__ORIGIN__/" />
|
|
<meta property="og:image" content="__ORIGIN__/logo.png" />
|
|
<meta property="og:image:width" content="512" />
|
|
<meta property="og:image:height" content="512" />
|
|
<meta property="og:image:alt" content="Thermograph logo" />
|
|
<meta name="twitter:card" content="summary" />
|
|
<link rel="icon" href="logo.svg" type="image/svg+xml" />
|
|
<link rel="apple-touch-icon" href="logo.png" />
|
|
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css" />
|
|
<link rel="stylesheet" href="style.css" />
|
|
</head>
|
|
<body>
|
|
<header>
|
|
<div class="brand">
|
|
<span class="logo">▚</span>
|
|
<div>
|
|
<h1>Thermograph</h1>
|
|
<p class="tag">Grading today's weather against ~45 years of local climate history</p>
|
|
</div>
|
|
<nav class="view-nav" aria-label="Views">
|
|
<a href="./" data-view="map" class="active">Weekly</a>
|
|
<a href="calendar" data-view="calendar">Calendar</a>
|
|
<a href="day" data-view="day">Day</a>
|
|
<a href="compare" data-view="compare">Compare</a>
|
|
</nav>
|
|
</div>
|
|
</header>
|
|
|
|
<main>
|
|
<section class="controls">
|
|
<div class="find-bar">
|
|
<button type="button" id="find-btn" class="find-btn"></button>
|
|
<span id="loc-label" class="loc-label" hidden></span>
|
|
</div>
|
|
<div class="date-row">
|
|
<label>Target date
|
|
<input id="date-input" type="date" />
|
|
</label>
|
|
<button type="button" id="today-btn" class="today-chip" title="Reset the target date to today">Today</button>
|
|
<span class="hint"><a href="legend">What do the grades mean? →</a></span>
|
|
</div>
|
|
</section>
|
|
|
|
<section id="panel" class="panel panel-full">
|
|
<div class="placeholder" id="placeholder">
|
|
<p>Find a location to begin.</p>
|
|
<p class="muted">Thermograph fetches decades of daily highs, lows, and precipitation
|
|
for your ~4 sq mi cell, builds a ±7-day seasonal distribution for each
|
|
day of the year, and grades recent weather by where it falls on that distribution.</p>
|
|
</div>
|
|
<div id="results" hidden></div>
|
|
</section>
|
|
</main>
|
|
|
|
<script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"></script>
|
|
<script type="module" src="app.js"></script>
|
|
</body>
|
|
</html>
|