Compare commits

...
5 Commits
Author SHA1 Message Date
Alexander Heldt 0ea9374b46 Give asleep a readable ink as well
--sleep was doing the same two jobs --wake had just been split out of: the
pale tint behind a timer and the text on top of it. As text it never worked —
2.57:1 on the day-bar pill, 2.79 on the big card, 2.85 on the list label — and
the dark theme was no better off than the light one, at 4.07 on the pill, which
I had assumed was fine until I measured it.

So --sleep-ink joins --wake-ink, deep blue in the light theme and a lighter one
in the dark, and the four places that painted text with --sleep now use it. The
tints, stripes, chart bars and borders keep --sleep: nothing about the colour of
sleep changes, only the colour of writing about it.

All sixteen text sites the two states cover — pill, big clock, list label and
open-row label, each state, each theme — now clear 4.5:1, the worst being 4.71.

The pedigree's ♂ keeps the raw --sleep. It is the same blue serving an unrelated
meaning, and recolouring it here would be a change to the pedigree made for
reasons that have nothing to do with it.
2026-08-31 22:36:45 +00:00
Alexander Heldt f9becddbc6 Carry the sunshine to the two awake timers
The list said awake in gold while the big card and the day-bar pill still said
it in purple, which left the app describing one state in two colours. Both move
across, so asleep against awake is night-blue against day-gold everywhere.

Text takes --wake-ink, not --wake. A gold light enough to read as sunshine is
not legible as text on a pale ground: the bright value measures 1.71:1 against
the card it would sit on, where the ink makes 4.96, and 4.71 on the pill's
tint — the same split the pee button has always used, dark ink on the yellow
rather than yellow ink.

Worth noting for later: this now makes awake considerably easier to read than
asleep, whose blue-on-blue pill has been sitting at 2.57:1 since long before
any of this. Untouched, being a separate question about the sleep colour.
2026-08-31 22:33:44 +00:00
Alexander Heldt a7608829e9 Paint awake as sunshine, not accent
Awake rows in the sleep & wake list borrowed --accent, which meant purple did
double duty: it was the colour of "awake" and also the colour of "this row is
the one still running", so the two readings overlapped on exactly the row where
both applied. A warm gold takes the first job. Asleep against awake now reads
as night against day, which is the distinction the list exists to draw, and it
agrees with the ☀️ the timer pill already shows.

Two variables rather than one. --wake is the bright gold, for the stripe and
the open row's tint. --wake-ink is that warmth pulled dark, for the small
uppercase label, and it is the value that flips with the theme: a yellow legible
on white is not legible on near-black, and vice versa. Measured on the four
backgrounds it lands on, it clears 4.5:1 in both themes, ordinary rows and the
tinted open one alike — better than the purple it replaces, which only ever made
the large-text bar.

The gold sits near --pee, but the two never share a panel: pee is a dot in the
history log and a bar in the counts chart, awake is a stripe in a list of
windows.

Scoped to the list, as asked. The big timer card and the day-bar pill still
paint awake with the accent.
2026-08-31 22:31:14 +00:00
Alexander Heldt 9d69bac73b Read the walks list newest first too
Needed, on the evidence: the history log, the notes log, the weigh-in list and
now sleep & wake all run newest first, so the walks list was the only one in the
app reading the other way. A short list makes the inconsistency cheap rather
than absent — two panels of the same rows in the same shape should not have to
be checked for direction before they can be read.

Reversed at the point of display rather than in walkWindowsForDay. Its other
two callers — the day's total and the history rails — read the windows as a
set, and giving a shared helper a display order would only make them harder to
follow.
2026-08-31 22:27:36 +00:00
Alexander Heldt 68840847f0 Read the sleep & wake list newest first
The merged list ran oldest first, which put last night's sleep at the top and
pushed whatever is happening now further down with every window the day adds —
so the row you look at most often was the one that moved.

Reversed, it matches the history and notes logs: the open row leads, and the
day reads backwards from where you are. The interleaving is untouched, since
alternation survives either direction.

The changelog entry for the merge is corrected rather than answered with a
second one. It walked through an example day in the old direction, and the
build that shipped it is the same one this lands in — nobody has a version
where the list read the other way.
2026-08-31 22:25:19 +00:00
3 changed files with 44 additions and 18 deletions
+12 -5
View File
@@ -1030,14 +1030,17 @@
// Sleep and wake windows are the same boundaries read two ways — a wake
// window is precisely the gap between two sleeps — so they alternate by
// construction and sorting the two sets by start time interleaves them back
// into the day's actual rhythm. Read in order, "45 minutes asleep after two
// and a half hours up" is a single glance instead of a comparison between
// two lists.
// into the day's actual rhythm, "45 minutes asleep after two and a half hours
// up" in one glance instead of a comparison between two lists.
//
// Newest first, like the history and notes logs: the row still running is
// then the first one you see, and today's list grows downward from where you
// are rather than pushing the current state further away with every window.
function sleepWakeWindowsForDay(events, day) {
return [
...sleepWindowsForDay(events, day).map(w => ({ ...w, cls: "sleep-ww", tag: "Asleep" })),
...wakeWindowsForDay(events, day).map(w => ({ ...w, cls: "wake-ww", tag: "Awake" })),
].sort((a, b) => a.start - b.start);
].sort((a, b) => b.start - a.start);
}
function renderSleepWake(events) {
@@ -1051,9 +1054,13 @@
function renderWalks(events) {
const day = selectedDay();
const windows = walkWindowsForDay(events, day);
// Newest first, like every other list in the app, so a walk in progress is
// the first row. Reversed here rather than in walkWindowsForDay, whose
// other callers — the day's total and the history rails — read the windows
// as a set and would only be made harder to follow by a display order.
renderWindowList(
"walk-list", "walk-empty",
windows,
[...windows].reverse(),
"Walking", "walk-ww",
);
+5 -1
View File
@@ -1,6 +1,10 @@
[
{ "date": "2026-08-31", "text": "The asleep timer text is a deeper blue, so “Asleep for” in the day-bar pill and on the big card is properly readable against its own pale blue background instead of the near-invisible blue-on-blue it was — the same treatment the awake gold just got, and it applies to the Asleep labels in the Sleep & wake list too" },
{ "date": "2026-08-31", "text": "The awake timer is sunshine too now — both the big “Awake for” card and the pill in the day bar — so the whole app tells asleep and awake apart by night-blue against day-gold rather than by blue against purple" },
{ "date": "2026-08-31", "text": "Awake stretches in the Sleep & wake list are sunshine yellow now instead of the app's purple — the same idea as the ☀️ on the timer pill — so asleep and awake read as night and day down the list rather than as two shades of the same accent" },
{ "date": "2026-08-31", "text": "The Walks list reads newest first as well, so a walk in progress is the top row and every list in the app now runs the same way" },
{ "date": "2026-08-31", "text": "The Walks panel and its two charts have moved down the page, below “Pees, poos & meals”" },
{ "date": "2026-08-31", "text": "Sleep windows and Wake windows are one “Sleep & wake” panel now, with the two interleaved into a single list in time order — asleep 22:1006:05, awake until 08:30, asleep 45 min, and so on down the day. A wake window is just the gap between two sleeps, so the two lists were always halves of the same sequence, and reading them in order makes “only a 45-minute nap after two and a half hours up” obvious in a way two separate lists never did. Each row says which it is and carries a stripe in its colour; whichever one is still running keeps the highlight" },
{ "date": "2026-08-31", "text": "Sleep windows and Wake windows are one “Sleep & wake” panel now, with the two interleaved into a single list, newest first — awake since 12:30, asleep 11:4012:30, awake since 09:15, and so on back through the day. A wake window is just the gap between two sleeps, so the two lists were always halves of the same sequence, and reading them together makes “only a 45-minute nap after two and a half hours up” obvious in a way two separate lists never did. Whatever is happening right now is the first row, as in the history and notes logs. Each row says which it is and carries a stripe in its colour; whichever one is still running keeps the highlight" },
{ "date": "2026-08-31", "text": "The list of weigh-ins under the Weight chart folds away on its own now — tap “History” inside the panel to collapse just the rows and keep the latest figure and the curve on screen. It remembers the choice like the panels do, and folding the whole Weight panel still takes everything with it" },
{ "date": "2026-08-31", "text": "Walks now get the same two pattern views sleep has. “When … walks” is a day-per-row grid shaded where a walk was on, so you can see at a glance whether the routine is actually regular or drifts around. “Walk trend” draws the minutes walked so far at each point of the day against yesterday and the average over the picked window, with the age-based goal as a line and a ✓ once the day clears it — the line climbs only while a walk is on, so every step is one walk. Both appear under the Walks panel as soon as you have logged a walk, and stay out of the way until then" },
{ "date": "2026-08-31", "text": "The 7d / 14d / 30d buttons now set the window for the Timing panel too. Until now the typical, shortest and longest gaps between pees, poos and meals were always measured over the last 7 days whatever you picked; switch to 30d and they are measured over 30, which settles down the typical gap once there is a month of history to draw on" },
+27 -12
View File
@@ -12,6 +12,13 @@
--weight: #2bb3a3;
--training: #b04ecf;
--walk: #3f9e63;
/* Asleep and awake each come in two values: the bright one for stripes,
tints and fills, and an -ink for text on those pale grounds. Both inks
flip with the theme; the brights don't. --sleep itself was doing both
jobs and failing the second 2.6:1 on the timer pill it painted. */
--sleep-ink: #4258cc;
--wake: #f2b705;
--wake-ink: #8f6400;
--note: #6f7a90;
--danger: #d64545;
--gain: #2e9e5b;
@@ -32,6 +39,8 @@
--muted: #9c9bab;
--accent: #a690ff;
--accent-soft: #2a2640;
--sleep-ink: #8ba4ff;
--wake-ink: #f5c451;
--border: #2c2a3a;
--shadow: 0 1px 2px rgba(0, 0, 0, 0.3), 0 4px 16px rgba(0, 0, 0, 0.3);
}
@@ -43,6 +52,8 @@
--muted: #9c9bab;
--accent: #a690ff;
--accent-soft: #2a2640;
--sleep-ink: #8ba4ff;
--wake-ink: #f5c451;
--border: #2c2a3a;
--shadow: 0 1px 2px rgba(0, 0, 0, 0.3), 0 4px 16px rgba(0, 0, 0, 0.3);
}
@@ -215,8 +226,10 @@ body::before {
.bar-clock[hidden] { display: none; }
/* Big timer still on screen: keep the pill's slot but show nothing. */
.bar-clock.standby { visibility: hidden; }
.bar-clock.asleep { background: color-mix(in srgb, var(--sleep) 18%, var(--surface)); color: var(--sleep); }
.bar-clock.awake { background: color-mix(in srgb, var(--accent) 18%, var(--surface)); color: var(--accent); }
.bar-clock.asleep { background: color-mix(in srgb, var(--sleep) 18%, var(--surface)); color: var(--sleep-ink); }
/* Dark text on the yellow, the way the pee button already does it: a gold
light enough to read as sunshine is never legible as text on a pale ground. */
.bar-clock.awake { background: color-mix(in srgb, var(--wake) 18%, var(--surface)); color: var(--wake-ink); }
.big-clock {
text-align: center;
@@ -243,9 +256,9 @@ body::before {
font-variant-numeric: tabular-nums;
}
.big-clock.asleep { background: linear-gradient(180deg, var(--surface), color-mix(in srgb, var(--sleep) 10%, var(--surface))); }
.big-clock.asleep .bc-time { color: var(--sleep); }
.big-clock.awake { background: linear-gradient(180deg, var(--surface), color-mix(in srgb, var(--accent) 10%, var(--surface))); }
.big-clock.awake .bc-time { color: var(--accent); }
.big-clock.asleep .bc-time { color: var(--sleep-ink); }
.big-clock.awake { background: linear-gradient(180deg, var(--surface), color-mix(in srgb, var(--wake) 10%, var(--surface))); }
.big-clock.awake .bc-time { color: var(--wake-ink); }
/* Quick actions: a stack of explicit rows (see index.html) instead of one
auto-fit grid, so the grouping is the same at every width. Equal columns
@@ -474,24 +487,26 @@ textarea { resize: vertical; }
/* A stripe in the row's own colour, so an alternating list reads as alternating
before you get to the words. */
.ww.sleep-ww { border-left: 3px solid color-mix(in srgb, var(--sleep) 55%, transparent); }
.ww.wake-ww { border-left: 3px solid color-mix(in srgb, var(--accent) 45%, transparent); }
.ww.wake-ww { border-left: 3px solid color-mix(in srgb, var(--wake) 75%, transparent); }
.ww.walk-ww { border-left: 3px solid color-mix(in srgb, var(--walk) 55%, transparent); }
.ww.sleep-ww .ww-tag { color: var(--sleep); }
.ww.wake-ww .ww-tag { color: var(--accent); }
.ww.sleep-ww .ww-tag { color: var(--sleep-ink); }
.ww.wake-ww .ww-tag { color: var(--wake-ink); }
.ww.walk-ww .ww-tag { color: var(--walk); }
.ww.ongoing .ww-tag { color: var(--accent); }
.ww.sleep-ww.ongoing {
border-color: var(--sleep);
background: color-mix(in srgb, var(--sleep) 14%, var(--surface));
}
.ww.sleep-ww.ongoing .ww-tag { color: var(--sleep); }
.ww.sleep-ww.ongoing .ww-tag { color: var(--sleep-ink); }
/* Spelled out like the other two: the faded stripe above would otherwise win
over the generic .ww.ongoing on specificity ties and leave the open awake
row without its full-strength edge. */
row without its full-strength edge. The tag needs saying too .ww.wake-ww
.ww-tag ties with .ww.ongoing .ww-tag and loses on order. */
.ww.wake-ww.ongoing {
border-color: var(--accent);
background: color-mix(in srgb, var(--accent) 14%, var(--surface));
border-color: var(--wake);
background: color-mix(in srgb, var(--wake) 16%, var(--surface));
}
.ww.wake-ww.ongoing .ww-tag { color: var(--wake-ink); }
.ww.walk-ww.ongoing {
border-color: var(--walk);
background: color-mix(in srgb, var(--walk) 14%, var(--surface));