Mark now on the day-long charts

The hour heatmap and the two actograms all run midnight to midnight, and none
of them said where in that span you currently are. On the heatmap that made the
darkest block ambiguous: a column at 18h is either a habit or simply the hour
you are standing in with the rest of the day not yet lived. On the actograms it
hid the reason today's row stops early — it stops at now, not at bedtime.

One rule serves all three. drawActogram already draws both timelines, so the
line lands there once, and the heatmap appends the same parts after its hit
rects. Ink over a surface-coloured underlay, the same treatment the timing
chart's "since the last one" marker uses, so it stays readable crossing a
shaded sleep window, a dark cell or bare track. A caret hangs above the track
to keep it from reading as one more hour gridline, and the whole thing is
pointer-events: none so it cannot swallow a tap meant for the row underneath.

Deliberately not the accent colour: on these charts accent already means "the
selected day", and now is a different kind of fact.

The captions gain a clause naming the marker, which runs against the last few
commits taking captions away. Those removed lines restated what the picture
already said. This one does not: a thin vertical line has no inherent meaning,
and unlike darker-means-more there is nothing to infer it from.
This commit is contained in:
Alexander Heldt
2026-09-04 15:11:30 +00:00
parent 122897c732
commit 103a5f9937
4 changed files with 38 additions and 4 deletions
+27 -1
View File
@@ -1587,6 +1587,28 @@
// ---------- pattern charts ----------
// Where "now" falls in the day, 0..1 — the shared x for the charts that run
// midnight to midnight.
function nowDayFraction() {
const now = new Date();
return (now.getTime() - startOfDay(now).getTime()) / 86_400_000;
}
// Vertical "now" rule for those charts: ink over a surface-coloured underlay
// so it stays legible whatever bar or cell it crosses, with a caret hung
// above the track so it doesn't read as one more gridline. Append the parts
// last so the rule sits on top; it ignores pointer events, so the rows and
// cells underneath stay tappable.
function nowRuleParts(x, yTop, yBottom) {
const xs = x.toFixed(1);
return [
`<line class="now-rule-ring" x1="${xs}" y1="${yTop}" x2="${xs}" y2="${yBottom}"/>`,
`<line class="now-rule" x1="${xs}" y1="${yTop}" x2="${xs}" y2="${yBottom}"/>`,
`<path class="now-rule-cap" d="M${(x - 3).toFixed(1)} ${yTop - 4} ` +
`L${(x + 3).toFixed(1)} ${yTop - 4} L${xs} ${yTop} Z"/>`,
];
}
// Actogram: one row per day (oldest at top), a midnight-to-midnight track
// with the given windows shaded. Windows are clipped to each day, so one that
// crosses midnight shows correctly split across two rows, and an open one
@@ -1647,6 +1669,10 @@
parts.push(`<rect class="bar stl-hit" data-day="${ymd(day)}" x="${ML}" y="${y.toFixed(1)}" width="${innerW}" height="${rowH.toFixed(1)}"><title>${escapeText(title)}</title></rect>`);
}
// The window always ends today, so one rule marks both the live edge of the
// bottom row and the same time of day on every row above it.
parts.push(...nowRuleParts(xOf(nowDayFraction()), MT, H - MB));
setChartSVG(svg, parts); // wires the .bar[data-day] click → select that day
}
@@ -1894,7 +1920,7 @@
}
parts.push(`<text x="${(ML + innerW).toFixed(1)}" y="${yAxis}" text-anchor="end">24h</text>`);
svg.innerHTML = parts.concat(hits).join("");
svg.innerHTML = parts.concat(hits, nowRuleParts(ML + nowDayFraction() * innerW, MT, H - MB)).join("");
// <title> tooltips only ever show on a pointer, which left phones with no
// way to read a block's count. Tapping (or hovering) a block names it in
+1
View File
@@ -1,4 +1,5 @@
[
{ "date": "2026-09-04", "text": "The three day-long charts — “By hour of day”, “When sleeping” and “When walking” — now mark the current time with a small vertical line and caret. On the sleeping and walking rows it also shows where today's row stops, and it lines the same clock position up across every day above it" },
{ "date": "2026-09-01", "text": "Removed the walking goal from the Walk trend — the dashed target line, its legend chip and the ✓ that marked a day as met. It came from the “five-minute rule” (five minutes per month of age, twice a day), which is a widely repeated rule of thumb rather than veterinary guidance, and the app was stating it more confidently than it deserved. The chart is now just a record of what you walked, against yesterday and the average" },
{ "date": "2026-09-01", "text": "The 7d / 14d / 30d buttons have moved out of the Sleep panel onto their own “Charts cover” row, just under the Log event buttons. They always set the window for every chart on the page — training, timing, sleep, walks, pees/poos/meals — but sitting inside the Sleep panel made them look like a sleep setting" },
{ "date": "2026-09-01", "text": "Dropped the “Darker = more sessions that day” caption under the training consistency grid. Tapping a cell still opens that day" },
+3 -3
View File
@@ -251,7 +251,7 @@
<section class="patterns" data-panel="sleep-timeline">
<h2><span id="sleep-timeline-title">When sleeping</span> <span class="muted-note" data-chart-days-label>(last 7 days)</span></h2>
<svg id="chart-sleep-timeline" class="chart-svg" viewBox="0 0 320 125" role="img" aria-label="Sleep periods per day"></svg>
<p class="muted-note">Each row is a day, midnight to midnight; shaded = asleep. Tap a row to open that day.</p>
<p class="muted-note">Each row is a day, midnight to midnight; shaded = asleep. The marker is now. Tap a row to open that day.</p>
</section>
<section class="patterns" data-panel="sleep-trend">
@@ -288,7 +288,7 @@
<div class="chart-title">By hour of day</div>
<svg id="chart-hour-heatmap" class="chart-svg" viewBox="0 0 320 120" role="img" aria-label="Pee, poo and meal frequency by hour of day"></svg>
<p id="hour-heatmap-info" class="muted-note hour-point-info" aria-live="polite"></p>
<p class="muted-note">Darker = happens more often at that hour.</p>
<p class="muted-note">Darker = happens more often at that hour; the marker is now.</p>
</div>
</section>
@@ -308,7 +308,7 @@
<section class="patterns" data-panel="walk-timeline" hidden>
<h2><span id="walk-timeline-title">When walking</span> <span class="muted-note" data-chart-days-label>(last 7 days)</span></h2>
<svg id="chart-walk-timeline" class="chart-svg" viewBox="0 0 320 125" role="img" aria-label="Walks per day"></svg>
<p class="muted-note">Each row is a day, midnight to midnight; shaded = out on a walk. Tap a row to open that day.</p>
<p class="muted-note">Each row is a day, midnight to midnight; shaded = out on a walk. The marker is now. Tap a row to open that day.</p>
</section>
<section class="patterns" data-panel="walk-trend" hidden>
+7
View File
@@ -1109,6 +1109,13 @@ input.switch:checked::after { transform: translateX(18px); }
.chart-svg .stl-hit { fill: transparent; cursor: pointer; }
.chart-svg .stl-hit:hover { fill: var(--accent); fill-opacity: 0.08; }
/* "Now" rule on the midnight-to-midnight charts (actograms + hour heatmap):
ink over a surface-coloured underlay so it reads over any shaded window or
cell, and never eats a tap meant for the row underneath. */
.chart-svg .now-rule-ring { stroke: var(--surface); stroke-width: 3; pointer-events: none; }
.chart-svg .now-rule { stroke: var(--text); stroke-width: 1; opacity: 0.75; pointer-events: none; }
.chart-svg .now-rule-cap { fill: var(--text); opacity: 0.75; pointer-events: none; }
/* Sleep trend lines: today strongest, the reference curves lighter/dashed. */
.chart-svg .trend-today {
stroke: var(--sleep);