fix: say what the bars and colours mean

The meter encoded two things at once and neither was guessable from looking
at it — obvious to whoever built it, opaque to everyone else.

Every row now carries a plain caption under the bar ("Aug 10 – Aug 17 · 3 of
7 days left", "started Apr 22 · no end date announced"), which explains the
bar and doubles as the exact dates people wanted anyway. A legend above the
list explains the colour ramp, which no per-row text can — shown once,
because a legend repeated fifty times is noise.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
This commit is contained in:
Lucas Winther
2026-08-15 01:09:54 +02:00
co-authored by Claude Opus 5
parent ad45b52c84
commit f4b133274a
4 changed files with 109 additions and 2 deletions
+36
View File
@@ -140,3 +140,39 @@ export function endingSoonestFirst(
if (b.clock.msRemaining === null) return -1;
return a.clock.msRemaining - b.clock.msRemaining;
}
/**
* A plain-language caption for an event's window.
*
* The meter shows a proportion; this says what the proportion is *of*. Without
* it a reader has to infer that ticks mean remaining time, which is exactly the
* kind of "obvious to the author" encoding that leaves everyone else guessing.
*/
export function windowCaption(clock: EventClock, now: number): string {
const on = (ms: number) =>
new Date(ms).toLocaleDateString(undefined, { day: "numeric", month: "short" });
if (clock.upcoming) {
return clock.endsMs === null
? `starts ${on(clock.startsMs)}`
: `${on(clock.startsMs)} ${on(clock.endsMs)} · not started yet`;
}
if (clock.endsMs === null) {
return `started ${on(clock.startsMs)} · no end date announced`;
}
const totalDays = Math.max(
1,
Math.round((clock.endsMs - clock.startsMs) / DAY),
);
const leftMs = Math.max(0, clock.endsMs - now);
const leftDays = Math.ceil(leftMs / DAY);
const left =
leftMs < DAY
? `${Math.max(1, Math.floor(leftMs / HOUR))} of ${totalDays * 24} hours left`
: `${leftDays} of ${totalDays} days left`;
return `${on(clock.startsMs)} ${on(clock.endsMs)} · ${left}`;
}