From 17cf8df10997cb3263c294953559f32e24b1e2e0 Mon Sep 17 00:00:00 2001 From: Vincent van der Wal Date: Sat, 1 Aug 2026 15:58:19 +0200 Subject: [PATCH] crossfade --- gaps.mjs | 25 +++++++ src/lib/charts/CanvasChart.svelte | 13 +++- .../components/navigation/weather-nav.svelte | 18 ++++- src/lib/stores/page-transition.svelte.ts | 24 +++++-- src/routes/+layout.svelte | 66 +++++++++++++++---- src/routes/layout.css | 24 ++++++- .../weather/14-day/[location]/+page.svelte | 3 +- .../weather/compare/[location]/+page.svelte | 3 +- .../historical/[location]/+page.svelte | 3 +- .../weather/seasonal/[location]/+page.svelte | 3 +- .../weather/week/[location]/+page.svelte | 26 ++++++-- .../week/[location]/DailyStripSticky.svelte | 22 ++++++- .../weather/week/[location]/DaySummary.svelte | 5 +- .../week/[location]/HourlyTable.svelte | 31 +++++++-- .../week/[location]/MeteogramCharts.svelte | 15 ++--- tiles.mjs | 28 ++++++++ 16 files changed, 258 insertions(+), 51 deletions(-) create mode 100644 gaps.mjs create mode 100644 tiles.mjs diff --git a/gaps.mjs b/gaps.mjs new file mode 100644 index 0000000..2240484 --- /dev/null +++ b/gaps.mjs @@ -0,0 +1,25 @@ +import { chromium } from 'playwright'; +const browser = await chromium.launch(); +const out = {}; +for (const [label, vp, mobile] of [['mobile', { width: 390, height: 844 }, true], ['desktop', { width: 1280, height: 950 }, false]]) { + const ctx = await browser.newContext({ viewport: vp, isMobile: mobile, hasTouch: mobile }); + const page = await ctx.newPage(); + await page.goto('http://localhost:5185/en/weather/week/berlin/', { waitUntil: 'load', timeout: 120000 }); + await page.waitForTimeout(14000); + await page.locator('canvas').first().scrollIntoViewIfNeeded(); + await page.waitForTimeout(1500); + out[label] = await page.evaluate(() => { + const c = document.querySelector('.strip-cell').getBoundingClientRect(); + const icon = document.querySelector('.strip-cell .day-icon').getBoundingClientRect(); + const temps = document.querySelector('.strip-cell .temp-max').getBoundingClientRect(); + return { + cell: { w: Math.round(c.width), h: Math.round(c.height) }, + iconOffCentre: +(icon.top + icon.height / 2 - (c.top + c.height / 2)).toFixed(1), + gapBelowTemps: +(c.bottom - temps.bottom).toFixed(1) + }; + }); + await page.screenshot({ path: `/tmp/gap-${label}.png`, clip: { x: 0, y: 0, width: vp.width, height: 200 } }); + await ctx.close(); +} +console.log(JSON.stringify(out, null, 1)); +await browser.close(); diff --git a/src/lib/charts/CanvasChart.svelte b/src/lib/charts/CanvasChart.svelte index d746fb3..3ec6a20 100644 --- a/src/lib/charts/CanvasChart.svelte +++ b/src/lib/charts/CanvasChart.svelte @@ -388,7 +388,18 @@ // Minimal gutters on narrow screens so the plot uses nearly the full width // (just enough to keep the axis tick labels legible). - let isNarrow = $derived(width > 0 && width < 520); + // Compact gutters (small axis padding, short icon rows) apply to phones AND + // tablets: below `lg` the page is edge-to-edge, so wide desktop-style axis + // margins would waste most of the width. Desktop keeps its roomier metrics. + let belowDesktop = $state(false); + onMount(() => { + const mq = window.matchMedia('(max-width: 1023px)'); + const apply = () => (belowDesktop = mq.matches); + apply(); + mq.addEventListener('change', apply); + return () => mq.removeEventListener('change', apply); + }); + let isNarrow = $derived(belowDesktop || (width > 0 && width < 520)); let padLeft = $derived(isNarrow ? 26 : 60); // Reserve the right gutter when this chart (or a sibling, via reserveRightAxis) // has a right axis, so a stacked row of charts share the same plot width. On diff --git a/src/lib/components/navigation/weather-nav.svelte b/src/lib/components/navigation/weather-nav.svelte index 44bfc3a..d09f374 100644 --- a/src/lib/components/navigation/weather-nav.svelte +++ b/src/lib/components/navigation/weather-nav.svelte @@ -1,6 +1,10 @@
-
+ +

{formatZoned(selectedDay, timezone, 'EEEE')} diff --git a/src/routes/weather/week/[location]/HourlyTable.svelte b/src/routes/weather/week/[location]/HourlyTable.svelte index a7c385b..abaef82 100644 --- a/src/routes/weather/week/[location]/HourlyTable.svelte +++ b/src/routes/weather/week/[location]/HourlyTable.svelte @@ -458,7 +458,9 @@ 1h needs far more room than 3h (24 vs 8 columns) -->
@@ -885,13 +887,34 @@ } /* ── Responsive ─────────────────────────────────────────────── */ + /* Phones: the table is the tallest thing on the page, so every cell is taken + down to roughly 0.7 of its desktop size - padding, type and the icons that + actually set the row height. Desktop keeps its original metrics. */ @media (max-width: 768px) { .hdr { - padding: 3px 2px; - font-size: 10px; + padding: 2px 1px; + font-size: 9px; } .cell { - font-size: 11px; + padding: 1px; + font-size: 10px; + } + /* The row-header icon and the fixed cell height are what actually set the + row height, so both come down explicitly. */ + .cell :global(svg), + .hdr :global(svg) { + width: 22px; + height: 22px; + } + .cell { + height: 34px; + } + .hdr :global(span) { + font-size: 9px; + line-height: 1.15; + } + .precip-bar { + min-height: 2px; } } diff --git a/src/routes/weather/week/[location]/MeteogramCharts.svelte b/src/routes/weather/week/[location]/MeteogramCharts.svelte index 71df6cd..abeac7e 100644 --- a/src/routes/weather/week/[location]/MeteogramCharts.svelte +++ b/src/routes/weather/week/[location]/MeteogramCharts.svelte @@ -25,14 +25,15 @@ selectedDay: Date; units: WeatherUnits; loading: boolean; + /** Canvas height in px; the page shrinks it on phones. */ + chartHeight?: number; onResetZoom?: () => void; } - let { data, selectedDay, units, loading, onResetZoom }: Props = $props(); + let { data, selectedDay, units, loading, chartHeight = 300, onResetZoom }: Props = $props(); const CHART_GROUP = 'week-meteogram'; const SECONDS_PER_DAY = 24 * 3600; - const CHART_HEIGHT = 300; let customizerOpen = $state(false); let downloadingPng = $state(false); @@ -351,13 +352,7 @@ {panel.titleShort} - + diff --git a/tiles.mjs b/tiles.mjs new file mode 100644 index 0000000..830b577 --- /dev/null +++ b/tiles.mjs @@ -0,0 +1,28 @@ +import { chromium } from 'playwright'; +const browser = await chromium.launch(); +const out = {}; +for (const [label, vp] of [['desktop', { width: 1280, height: 950 }], ['tablet', { width: 900, height: 800 }]]) { + const ctx = await browser.newContext({ viewport: vp }); + const page = await ctx.newPage(); + await page.goto('http://localhost:5185/en/weather/week/berlin/', { waitUntil: 'load', timeout: 120000 }); + await page.waitForTimeout(14000); + await page.locator('canvas').first().scrollIntoViewIfNeeded(); + await page.waitForTimeout(1500); + out[label] = await page.evaluate(() => { + const c = document.querySelector('.strip-cell').getBoundingClientRect(); + const icon = document.querySelector('.strip-cell .day-icon').getBoundingClientRect(); + const temps = document.querySelector('.strip-cell .temp-max').getBoundingClientRect(); + const canvas = document.querySelector('canvas'); + const cw = canvas ? canvas.getBoundingClientRect() : null; + return { + cell: { w: Math.round(c.width), h: Math.round(c.height) }, + iconOffCentre: +(icon.top + icon.height / 2 - (c.top + c.height / 2)).toFixed(1), + gapBelowTemps: +(c.bottom - temps.bottom).toFixed(1), + canvasWidth: cw ? Math.round(cw.width) : null + }; + }); + await page.screenshot({ path: `/tmp/tiles-${label}.png`, clip: { x: 0, y: 0, width: vp.width, height: 220 } }); + await ctx.close(); +} +console.log(JSON.stringify(out, null, 1)); +await browser.close();