From b78f921a3d46c9b1d2a67277055558bbf78a1357 Mon Sep 17 00:00:00 2001 From: Davide Scaini Date: Mon, 30 Mar 2026 09:22:23 +0200 Subject: [PATCH] fix heatmap in stats for bright theme --- site/src/components/StatsView.svelte | 39 ++++++++++++++++++++-------- 1 file changed, 28 insertions(+), 11 deletions(-) diff --git a/site/src/components/StatsView.svelte b/site/src/components/StatsView.svelte index 0510bc0..e51f6ad 100644 --- a/site/src/components/StatsView.svelte +++ b/site/src/components/StatsView.svelte @@ -6,12 +6,20 @@ let all: ActivitySummary[] = []; let sport: Sport | 'all' = 'all'; let loading = true; + let theme = 'dark'; onMount(async () => { const res = await fetch(`${import.meta.env.BASE_URL}data/index.json`); const index: BASIndex = await res.json(); all = index.activities.filter(a => a.privacy !== 'private' && a.distance_m); loading = false; + + theme = document.documentElement.getAttribute('data-theme') ?? 'dark'; + const obs = new MutationObserver(() => { + theme = document.documentElement.getAttribute('data-theme') ?? 'dark'; + }); + obs.observe(document.documentElement, { attributes: true, attributeFilter: ['data-theme'] }); + return () => obs.disconnect(); }); $: activities = sport === 'all' ? all : all.filter(a => a.sport === sport); @@ -143,20 +151,29 @@ ]; } - // Lerp from zinc-800 bg (#27272a = 39,39,42) toward target color - function applyIntensity(hex: string, intensity: number): string { + // Base cell color: zinc-800 dark (#27272a=39,39,42) or zinc-200 light (#e4e4e7=228,228,231) + $: emptyColor = theme === 'light' ? '#e4e4e7' : '#27272a'; + $: baseRgb = theme === 'light' + ? [228, 228, 231] as [number, number, number] + : [39, 39, 42] as [number, number, number]; + + // Lerp from base bg color toward target sport color + function applyIntensity(hex: string, intensity: number, base: [number, number, number]): string { const [tr, tg, tb] = hexToRgb(hex); - return `rgb(${Math.round(39 + (tr - 39) * intensity)},${Math.round(39 + (tg - 39) * intensity)},${Math.round(42 + (tb - 42) * intensity)})`; + const [br, bg, bb] = base; + return `rgb(${Math.round(br + (tr - br) * intensity)},${Math.round(bg + (tg - bg) * intensity)},${Math.round(bb + (tb - bb) * intensity)})`; } // Precompute date→color as a reactive Map so Svelte tracks it directly in // the template. (Calling a plain function with a static string arg won't // re-trigger when byDate/maxDailyKm change — the Map reference does.) $: cellColors = (() => { + const base = baseRgb; + const empty = emptyColor; const m = new Map(); for (const [date, sportMap] of byDateBySport) { const total = byDate.get(date) ?? 0; - if (total === 0) { m.set(date, '#27272a'); continue; } + if (total === 0) { m.set(date, empty); continue; } const intensity = 0.12 + pctRank(total, sortedDaily) * 0.88; let tr = 0, tg = 0, tb = 0; for (const [sp, dist] of sportMap) { @@ -165,15 +182,15 @@ tr += cr * w; tg += cg * w; tb += cb * w; } const blended = `#${Math.round(tr).toString(16).padStart(2,'0')}${Math.round(tg).toString(16).padStart(2,'0')}${Math.round(tb).toString(16).padStart(2,'0')}`; - m.set(date, applyIntensity(blended, intensity)); + m.set(date, applyIntensity(blended, intensity, base)); } return m; })(); - // Legend: 6 swatches from dark to full sport color (or neutral for 'all') - $: legendColor = sport !== 'all' ? sportColor(sport) : '#00c8ff'; + // Legend: 6 swatches from base bg to full sport color (or neutral for 'all') + $: legendColor = sport !== 'all' ? sportColor(sport) : (theme === 'light' ? '#0284c7' : '#00c8ff'); $: legendSwatches = [0, 0.18, 0.38, 0.58, 0.78, 1.0].map(t => - t === 0 ? '#27272a' : applyIntensity(legendColor, t) + t === 0 ? emptyColor : applyIntensity(legendColor, t, baseRgb) ); // Sport chips present in filtered data (for 'all' color key) @@ -301,12 +318,12 @@ {#each week as date}
onCellEnter(date, e)} on:mouseleave={onCellLeave} on:click={e => onCellClick(date, e)} - /> + >
{/each} {/each}