thermograph/static
Emi Griffith 33912efa6c Default climate pages to the city's own temperature unit (#189)
Search Console shows impressions skewing hard to °C countries — India, South
Africa, New Zealand, the UK, Australia — while every climate page rendered °F
into the HTML. Search snippets quote the server-rendered text, so a reader in
Delhi saw a temperature they had to convert in their head before the page was
worth a click.

Climate pages now render temperatures in the city's country convention: °F for
the US and the handful of other Fahrenheit countries, °C everywhere else. This
happens server-side, so it holds with no JS and shows up in the snippet.

data-temp-f keeps the raw Fahrenheit in every case — it stays the conversion
source of truth, and climate.js repaints from it on toggle exactly as before.
The visitor's stored choice still wins over everything; the page default only
sits between that and the locale guess, so a first-time visitor stops seeing a
°F→°C repaint on load for the majority of our traffic.

The unit rides on a ContextVar rather than a parameter because _temp() is
reached from both directions — templates call it as a Jinja global, and the
context builders call it directly to pre-render the records tables into strings.
Threading an argument would mean touching ~20 call sites across three levels of
nesting, where missing one yields a silently wrong unit instead of an error. The
scope wraps the context builder as well as the render, since the builder is
evaluated as an argument and runs first, and it resets in a finally: the page
handlers are sync `def`, so Starlette runs them on a recycled threadpool thread
and a value left set would leak into the next request that thread serves.

Conversion rounds half-up to match JS's Math.round rather than Python's
round-half-to-even, so the server prints the number climate.js would and there
is no flicker on load for a visitor whose unit already matched.

Pages with no single city — the homepage strip, the hub, the interactive views —
set no scope, emit no data-unit-default, and keep today's locale behaviour.

Precipitation, wind and humidity stay imperial; that inconsistency is worth a
follow-up but is outside this change.

Two existing assertions were passing for the wrong reason: base.html.j2 mentions
"°F/°C" in an HTML comment, so a bare `"°F" in html` check succeeded even on a
page whose every temperature was Celsius. They now read the rendered spans.

Claude-Session: https://claude.ai/code/session_013dRZmX9D3JEntfMKWMTWZ8
2026-07-19 18:25:55 +00:00
..
account.js Give climate/city/record pages full header parity + working °F/°C (#124) 2026-07-16 17:48:30 +00:00
app.js Render every percentile through one rule (#186) 2026-07-18 09:08:28 +00:00
apple-touch-icon.png New brand mark: C1 step-curve logo wired through favicons, PWA icons, and header (#169) 2026-07-18 04:26:42 +00:00
cache.js Warm neighbor cells server-side (/cell?neighbors=1) (#51) 2026-07-11 20:47:31 +00:00
calendar.html Make the brand lockup a home link on every page (#182) 2026-07-18 08:06:14 +00:00
calendar.js Render every percentile through one rule (#186) 2026-07-18 09:08:28 +00:00
chart.js Render every percentile through one rule (#186) 2026-07-18 09:08:28 +00:00
climate.js Give climate/city/record pages full header parity + working °F/°C (#124) 2026-07-16 17:48:30 +00:00
compare.html Make the brand lockup a home link on every page (#182) 2026-07-18 08:06:14 +00:00
compare.js Account system with weather-notification subscriptions (#89) 2026-07-15 18:46:46 +00:00
day.html Make the brand lockup a home link on every page (#182) 2026-07-18 08:06:14 +00:00
day.js Render every percentile through one rule (#186) 2026-07-18 09:08:28 +00:00
digest.js Rebuild the homepage as a distribution landing; add an SMTP seam (#178) 2026-07-18 07:39:47 +00:00
favicon-16.png New brand mark: C1 step-curve logo wired through favicons, PWA icons, and header (#169) 2026-07-18 04:26:42 +00:00
favicon-32.png New brand mark: C1 step-curve logo wired through favicons, PWA icons, and header (#169) 2026-07-18 04:26:42 +00:00
favicon-48.png New brand mark: C1 step-curve logo wired through favicons, PWA icons, and header (#169) 2026-07-18 04:26:42 +00:00
favicon.svg New brand mark: C1 step-curve logo wired through favicons, PWA icons, and header (#169) 2026-07-18 04:26:42 +00:00
filtersheet.js Compare basis inline; synced inline + sheet metric selector (both pages) (#82) 2026-07-12 07:45:12 +00:00
google03ab95daa090e8d9.html Add Google Search Console site-verification file (#128) 2026-07-16 18:56:42 +00:00
legend.html Make the brand lockup a home link on every page (#182) 2026-07-18 08:06:14 +00:00
logo-192.png New brand mark: C1 step-curve logo wired through favicons, PWA icons, and header (#169) 2026-07-18 04:26:42 +00:00
logo-maskable-192.png New brand mark: C1 step-curve logo wired through favicons, PWA icons, and header (#169) 2026-07-18 04:26:42 +00:00
logo-maskable-512.png New brand mark: C1 step-curve logo wired through favicons, PWA icons, and header (#169) 2026-07-18 04:26:42 +00:00
logo.png New brand mark: C1 step-curve logo wired through favicons, PWA icons, and header (#169) 2026-07-18 04:26:42 +00:00
manifest.webmanifest New brand mark: C1 step-curve logo wired through favicons, PWA icons, and header (#169) 2026-07-18 04:26:42 +00:00
mappicker.js Convert the frontend to ES modules; split nav.js by concern (#48) 2026-07-11 20:28:33 +00:00
nav.js Add PWA + Web Push delivery for weather alerts (#95) 2026-07-15 23:21:06 +00:00
package.json Convert the frontend to ES modules; split nav.js by concern (#48) 2026-07-11 20:28:33 +00:00
push-client.js Add PWA + Web Push delivery for weather alerts (#95) 2026-07-15 23:21:06 +00:00
shared.js Render every percentile through one rule (#186) 2026-07-18 09:08:28 +00:00
style.css Give the records strip real context, and colour the explore cards (#183) 2026-07-18 08:19:27 +00:00
subscriptions.html Make the brand lockup a home link on every page (#182) 2026-07-18 08:06:14 +00:00
subscriptions.js Push: surface delivery failures (no longer a silent success) (#157) 2026-07-17 00:12:14 +00:00
sw.js Add PWA + Web Push delivery for weather alerts (#95) 2026-07-15 23:21:06 +00:00
units.js Default climate pages to the city's own temperature unit (#189) 2026-07-19 18:25:55 +00:00