diff --git a/assets/css/table.css b/assets/css/table.css index 1bb8e5a..7e9c6f1 100644 --- a/assets/css/table.css +++ b/assets/css/table.css @@ -94,15 +94,17 @@ background: transparent; border: 1px solid #c9b08a; color: #b09870; - transition: color 0.2s, border-color 0.2s; + transition: color 0.2s, border-color 0.2s, background-color 0.2s, filter 0.15s; } .utci-day-tab + .utci-day-tab { border-left: none; } +/* Each tab's background is an inline per-day weather gradient, so we darken + the whole tab with a filter on hover (!important to beat the inline filter). */ .utci-day-tab:hover { - color: #6b4f2a; + filter: brightness(0.93) !important; } .utci-day-tab.active { diff --git a/assets/js/hooks/useForecast.js b/assets/js/hooks/useForecast.js index f753930..d346e7c 100644 --- a/assets/js/hooks/useForecast.js +++ b/assets/js/hooks/useForecast.js @@ -29,7 +29,7 @@ // fetchedAt - Date the forecast data was last fetched or read from cache // ------------------------------------------------------------------------ -import { useState, useEffect } from '../../vendor/preact-hooks.js'; +import { useState, useEffect, useRef } from '../../vendor/preact-hooks.js'; const FIVE_MIN_MS = 5 * 60 * 1000; const FIFTEEN_MIN_MS = 15 * 60 * 1000; @@ -127,6 +127,11 @@ export function useForecast(location) { const [now, setNow] = useState(new Date()); const [fetchedAt, setFetchedAt] = useState(null); + // Mirrors the latest forecast so the loader's catch block can tell whether + // data is already on screen, even inside stale setInterval closures. + const forecastRef = useRef(null); + useEffect(() => { forecastRef.current = forecast; }, [forecast]); + // ─── FORECAST LOADER ────────────────────────────────────────────── // // Waterfall: @@ -134,7 +139,8 @@ export function useForecast(location) { // 2. Cache stale / missing → fetch main auto API // 3. Main API soil is null → try ICON for soil data // 4. ICON fails → fall back to stale cache (any age) - // 5. No cache at all → show error + // 5. No cache but data shown → keep existing data (don't show error) + // 6. Nothing loaded at all → show error // async function loadForecast(loc) { const key = forecastCacheKey(loc); @@ -178,8 +184,12 @@ export function useForecast(location) { if (staleCache) { setForecast(staleCache.data); setFetchedAt(new Date(staleCache.ts)); // old timestamp signals stale data + } else if (forecastRef.current) { + // Step 5 — no cache (e.g. an auto-refresh cleared it first), but data + // is already on screen. Keep showing it rather than replacing the page + // with a transient network error. } else { - setError(e.message); // Step 5 — nothing to fall back to + setError(e.message); // Step 6 — nothing loaded yet, surface the error } } finally { setLoading(false); }