From 31233c713f8c5ebbeb5570fda805583dece64050 Mon Sep 17 00:00:00 2001 From: fraxle Date: Mon, 18 May 2026 20:38:19 +0100 Subject: [PATCH] Future Idea Comments --- assets/js/app.js | 182 +++++++++++++++++++++++++++++++++++++++++++ assets/js/compute.js | 80 +++++++++++++++++++ assets/js/config.js | 27 +++++++ assets/js/physics.js | 52 +++++++++++++ 4 files changed, 341 insertions(+) diff --git a/assets/js/app.js b/assets/js/app.js index e270bd6..6aa7595 100644 --- a/assets/js/app.js +++ b/assets/js/app.js @@ -143,6 +143,122 @@ export function UTCIForecast() { return localStorage.getItem('sunscope_pro') === '1'; }); + // ── FUTURE FEATURE v2: Dark Mode + Theme System (JSON-driven) ─────────────── + // Add a `theme` state that drives a data-theme attribute on or .utci-app. + // CSS custom properties do all the heavy lifting — no JS colour logic needed. + // + // THE THEME FILE: /assets/themes.json + // All themes live in a single JSON file. Easy to read, easy to edit — + // add a new theme by copying an existing block and changing the values. + // No build step, no JS changes required for new themes. + // + // Each theme has: + // id — unique key used in localStorage and data-theme attribute + // name — human-readable label (shown in theme picker UI) + // description — one-liner explaining the look/feel + // base — which base mode it extends: "light" | "dark" + // trigger — how/when it activates (see trigger types below) + // colors — the CSS custom properties it overrides (only what changes) + // + // Example themes.json: + // [ + // { + // "id": "light", + // "name": "Light (Default)", + // "description": "Classic parchment and brass in full daylight", + // "base": "light", + // "trigger": { "type": "manual" }, + // "colors": { + // "--bg": "#f5edd6", + // "--surface": "#ede0c4", + // "--brass": "#c8922a", + // "--text": "#2a1a08", + // "--text-muted": "#9a7d5a", + // "--accent": "#c8922a" + // } + // }, + // { + // "id": "dark", + // "name": "Dark", + // "description": "Deep walnut and glowing brass — made for night", + // "base": "dark", + // "trigger": { "type": "manual" }, + // "colors": { + // "--bg": "#1a1208", + // "--surface": "#261a0a", + // "--brass": "#c8922a", + // "--text": "#f5edd6", + // "--text-muted": "#9a7d5a", + // "--accent": "#d4a030" + // } + // }, + // { + // "id": "christmas", + // "name": "Christmas", + // "description": "Holly green with crimson brass — Dec 20 to Jan 2", + // "base": "dark", + // "trigger": { "type": "date", "from": "12-20", "to": "01-02" }, + // "colors": { + // "--bg": "#0f1f0f", + // "--surface": "#1a3a1a", + // "--brass": "#b8312f", + // "--text": "#f0ede0", + // "--text-muted": "#7a9a7a", + // "--accent": "#c8922a" + // } + // }, + // { + // "id": "halloween", + // "name": "Halloween", + // "description": "Near-black with amber and burnt orange — Oct 25–31", + // "base": "dark", + // "trigger": { "type": "date", "from": "10-25", "to": "10-31" }, + // "colors": { + // "--bg": "#100a00", + // "--surface": "#1e1000", + // "--brass": "#cc6600", + // "--text": "#f0d080", + // "--text-muted": "#8a6020", + // "--accent": "#e07820" + // } + // }, + // { + // "id": "heatwave", + // "name": "Heatwave Alert", + // "description": "Deep red pulse when a dangerous heat day is forecast", + // "base": "light", + // "trigger": { "type": "alert", "alertType": "heat" }, + // "colors": { + // "--brass": "#b82020", + // "--accent": "#c83030" + // } + // } + // ] + // + // TRIGGER TYPES: + // "manual" — user picks it from the theme switcher in the nav, persisted + // to localStorage ('sunscope_theme') + // "date" — auto-applies between MM-DD dates (wraps year-end correctly) + // user can still override manually; auto resets when out of range + // "alert" — activates when daySummary (compute.js) fires a matching alert; + // subtle, temporary — reverts when alert clears + // "system" — mirrors the OS dark/light preference (prefers-color-scheme) + // + // HOW IT WORKS IN JS: + // 1. Fetch /assets/themes.json once on load (or bundle it as an import) + // 2. Walk the trigger rules to find the highest-priority active theme + // Priority: alert > manual > date > system + // 3. Apply the winning theme's colors as inline CSS variables on + // Object.entries(theme.colors).forEach(([k,v]) => + // document.body.style.setProperty(k, v)) + // 4. Store active theme id in localStorage for manual overrides + // 5. A small theme-picker icon (🎨 or ☀️/🌙) in the top nav lets the + // user browse and manually select any theme + // + // const [theme, setTheme] = useState(() => + // localStorage.getItem('sunscope_theme') || 'system'); + // ───────────────────────────────────────────────────────────────────────────── + // (FREE_DAYS, FILTER_PROFILES and OUTDOORS_VARIANTS now live in ./config.js.) // Current active filter profile @@ -510,6 +626,24 @@ export function UTCIForecast() { glob=${currentRow?.glob ?? 0} activeEvent=${lensEvent} /> + {/* ── FUTURE FEATURE v2: Historical Context Line ───────────────── + Show a subtle single line directly below the dial readout: + e.g. "3.2°C above the May average for this location" + "Near normal for late August" + + Design notes: + • Must be visually elegant — same Fraunces/Manrope type pairing + as the rest of the dial area, small and muted + • Positive delta: warm amber tone; negative delta: cool blue tone + • Source: Open-Meteo has a free /climate endpoint that returns + monthly climate normals (ERA5 reanalysis) for any lat/lon. + Fetch once on location change, cache in a ref. Compare today's + peak air temp (or UTCI) to that month's normal. + • Could also show min/max historical context for the week: + "Warmest day forecast this week" / "Coolest night since March" + • The fetch is separate from the main forecast — handle its own + loading/error state independently so it doesn't block the UI. + ─────────────────────────────────────────────────────────────── */}
@@ -1117,6 +1251,54 @@ ${isPro && (activeProfile === 'custom' || activeCols['air']) && html`