From 0ebaa11c934ecfa60eb238ebe1a8a7a2c8478982 Mon Sep 17 00:00:00 2001 From: Alexander Heldt Date: Wed, 15 Jul 2026 19:06:16 +0000 Subject: [PATCH] Stretch the Sleep trend y-axis above 10h MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The interesting detail sits near the goal, in the upper teens of hours, so the scale is piecewise: 0–10h shares a compressed 26% of the height with 2h gridlines, and everything above gets the rest with 1h gridlines. Falls back to linear while the axis is too short to split. The caption notes the stretched axis. --- src/app.js | 39 ++++++++++++++++++++++++++++----------- src/changelog.json | 1 + src/index.html | 2 +- 3 files changed, 30 insertions(+), 12 deletions(-) diff --git a/src/app.js b/src/app.js index bc4d68a..9aec655 100644 --- a/src/app.js +++ b/src/app.js @@ -1379,14 +1379,33 @@ target ? target.hi : 0, ...series.flatMap(s => s.pts.map(p => p.y)), ); - const { yMax, steps: ySteps } = niceAxisSleepHours(rawMax); + const { yMax } = niceAxisSleepHours(rawMax); + // The interesting detail sits near the goal, in the upper teens of hours, + // so the y scale is piecewise: everything up to BREAK shares a compressed + // slice of the height and the hours above it get all the rest. Linear + // again while the axis is still too short for a split to mean anything. + const BREAK = 10, LOW_FRAC = 0.26; + const split = yMax > BREAK + 2; + const frac = (v) => !split + ? v / yMax + : v <= BREAK + ? (v / BREAK) * LOW_FRAC + : LOW_FRAC + ((v - BREAK) / (yMax - BREAK)) * (1 - LOW_FRAC); const xOf = (hour) => ML + (hour / 24) * innerW; - const yOf = (v) => MT + innerH * (1 - v / yMax); + const yOf = (v) => MT + innerH * (1 - frac(v)); - // Label every 1h gridline; smaller text once the axis is dense so the - // labels stay apart. - const yLabelCls = ySteps > 16 ? "y-dense" : ""; + // Gridlines: every 1h in the stretched region above the break, every 2h + // in the compressed region below it (1h everywhere when linear). + const yTicks = []; + if (split) { + for (let v = 0; v < BREAK; v += 2) yTicks.push(v); + for (let v = BREAK; v <= yMax; v++) yTicks.push(v); + } else { + for (let v = 0; v <= yMax; v++) yTicks.push(v); + } + // Smaller text once the axis is dense so the labels stay apart. + const yLabelCls = yTicks.length > 16 ? "y-dense" : ""; const parts = []; // Age-based goal band, behind everything: today's projection should land @@ -1400,12 +1419,10 @@ `${escapeText(`Goal ${target.lo}–${target.hi}h (${target.label})`)}` ); } - for (let i = 0; i <= ySteps; i++) { - const y = MT + innerH * (1 - i / ySteps); - const v = Math.round(yMax * i / ySteps * 10) / 10; - const vText = v % 1 === 0 ? v : v.toFixed(1); - parts.push(``); - parts.push(`${vText}h`); + for (const v of yTicks) { + const y = yOf(v); + parts.push(``); + parts.push(`${v}h`); } for (const hr of [0, 6, 12, 18, 24]) { const x = xOf(hr); diff --git a/src/changelog.json b/src/changelog.json index 1230fee..a3620e4 100644 --- a/src/changelog.json +++ b/src/changelog.json @@ -1,4 +1,5 @@ [ + { "date": "2026-07-15", "text": "The Sleep trend y-axis is stretched above 10h, giving the hours around the sleep goal most of the chart" }, { "date": "2026-07-15", "text": "The Sleep trend chart shows the sleep goal for your puppy's age as a shaded band — the projection chip gets a ✓ when today is on track" }, { "date": "2026-07-15", "text": "Logging a training session while viewing another day puts it on that day (the snackbar tells you where it went)" }, { "date": "2026-07-15", "text": "The Sleep trend yesterday line is orange instead of gray, which was hard to see" }, diff --git a/src/index.html b/src/index.html index ae2f7b5..4245eaf 100644 --- a/src/index.html +++ b/src/index.html @@ -230,7 +230,7 @@ 7-day avg -

Hours slept so far at each point of the day, against yesterday and the average over the picked chart window. The dashed tail continues today's line the way the average day usually plays out.

+

Hours slept so far at each point of the day, against yesterday and the average over the picked chart window. The dashed tail continues today's line the way the average day usually plays out. The axis is stretched above 10h to give the hours around the goal more room.