Replace the date picker with a month grid of our own

The day bar had run out of room: two timers and four day controls came to more
than the bar's width on every phone, so it wrapped onto two rows whenever a walk
was running. Shaving pixels off both groups was not enough — even the most
compact form of it only fitted a 428px iPhone Plus.

The way out was structural. The browser's date picker is a sheet that covers
the screen, which is backwards here: the reason to change day is to see what
the figures did on it, and a modal hides exactly the thing you opened it for.
So the date button now opens a small panel under the bar instead — month name
with arrows, locale-ordered weekday initials, six rows of days — with the
overview still on screen and updating as you move through it.

That also solved the width, because Today belongs inside the grid rather than
beside it. The day controls drop from about 204px to 111px, which is enough for
both timers on one row from 320px up; only a 280px foldable cover still wraps.
The bar keeps its wrapping for that case and for large system font sizes.

The hidden input stays as the value everything reads — selectedDay() and every
caller are untouched, and only the input's own picker is no longer opened.

Six rows always, so the panel does not change height from month to month.
Future days are disabled, matching the bar's → being disabled on today. The
week starts where Intl says it does for the reader's locale, falling back to
Monday. Arrow keys walk the grid and pull the neighbouring month into view at
the edges.

The grid arithmetic is checked rather than eyeballed, both week starts across
every month of a year, leap years and year boundaries — including February
2026, which begins on a Sunday and so needs six leading days from January under
Monday weeks. That is the case a naive `1 - getDay()` gets wrong, and it would
have been invisible until somebody happened to open that month.
This commit is contained in:
Alexander Heldt
2026-09-09 04:03:09 +00:00
parent fba0f73717
commit fbacd97da7
5 changed files with 248 additions and 29 deletions
+70 -3
View File
@@ -293,6 +293,70 @@ body::before {
opacity: 0;
pointer-events: none;
}
/* ---------- the month grid ---------- */
/* Anchored under the date button rather than filling the screen the way the
browser's own picker does: changing day is worth doing *because* of the
figures below, so they have to stay in view while you move. Deliberately
kept short — six rows of small cells — for the same reason. */
.day-cal {
position: absolute;
top: calc(100% + 8px);
left: 50%;
transform: translateX(-50%);
z-index: 70; /* over the day bar itself, which is 60 */
width: 268px;
max-width: calc(100vw - 24px);
padding: 10px;
background: var(--surface);
border: 1px solid var(--border);
border-radius: var(--radius);
box-shadow: var(--shadow);
}
.day-cal-head {
display: flex;
align-items: center;
justify-content: space-between;
gap: 6px;
margin-bottom: 6px;
}
.day-cal-month {
font-weight: 700;
font-size: 0.9rem;
}
.day-cal-weekdays,
.day-cal-grid {
display: grid;
grid-template-columns: repeat(7, 1fr);
gap: 2px;
}
.day-cal-weekdays span {
text-align: center;
font-size: 0.7rem;
color: var(--muted);
padding: 2px 0;
}
button.cal-day {
background: transparent;
color: var(--text);
font-weight: 500;
font-size: 0.85rem;
font-variant-numeric: tabular-nums;
padding: 6px 0;
border-radius: 8px;
}
button.cal-day.other-month { color: var(--muted); opacity: 0.5; }
button.cal-day:disabled { opacity: 0.25; cursor: default; }
button.cal-day:disabled:hover { filter: none; }
/* Today is outlined, the selected day is filled — so "where I am" and "where
now is" stay tellable apart when they are different days. */
button.cal-day.is-today { box-shadow: inset 0 0 0 1.5px var(--accent); }
button.cal-day.is-selected {
background: var(--accent);
color: #fff;
font-weight: 700;
}
.day-cal-today { width: 100%; margin-top: 8px; padding: 7px 10px; font-size: 0.85rem; }
#day-date-face {
font-variant-numeric: tabular-nums;
white-space: nowrap;
@@ -341,11 +405,14 @@ body::before {
background: var(--surface);
color: var(--text);
align-items: center;
gap: 6px;
padding: 7px 10px;
/* Sized so two of these fit beside the day controls on one row. The big card
is where a timer is meant to be read at size; this is a glance, which is
also why the pills count in minutes and the card keeps the seconds. */
gap: 4px;
padding: 7px 9px;
border-radius: 999px;
font-weight: 700;
font-size: 1rem;
font-size: 0.9rem;
font-variant-numeric: tabular-nums;
flex-shrink: 0;
}