fix(ui): keep date/time pickers inside their dialog

The add and edit surfaces could be scrolled sideways and the time picker
sat past the right edge: measurements, mood, the medication intake editor
and the cycle log sheet all showed it, so the cause is in what they share.

Two mechanisms, both in shared primitives.

DateField and TimeField wrap a text input. A flex item's min-width:auto
resolves to a content-based minimum, and for a text input that is its
intrinsic size attribute — 20 characters, about 180px. Neither field could
render below ~230px whatever width the parent offered, so the date + time
row demanded ~470px inside a 400px dialog body and pushed the clock button
58px past the edge. min-w-0 on the field box and on the overlay input
releases that floor; the row now fits at every width from 360px up.

The scrolling body only declares overflow-y. CSS promotes the other axis
from visible to auto, so every deliberate outward bleed in the tree turned
the form into a pannable surface — SheetSection's trigger row was 8px wider
than its own container for no benefit (a focus ring already draws outside
the box), and Switch's hit-target pseudo reaches 13px past the track. The
bodies and the dialog surface now state overflow-x explicitly, the section
trigger no longer bleeds, and the dialog's grid column is pinned so an
over-wide child cannot drag the surface out with it.

Measured before and after on the four reported surfaces at 360 / 390 / 430
/ 768 / 1280 / 1440: the date/time row spilled 83px on every width from
640px up and none of it remains.

An e2e spec asserts the two harms directly — the surface is not a
horizontal scroll container, and no focusable control lies outside its
visible box — with a canary that proves the reachability check can fail.
This commit is contained in:
Marc-André Bombeck committed 2026-08-07 16:23:31 +02:00
1 parent d5f176ac4d
commit eb5c8a91fb
7 files changed
+333 -15

No files matched your search

+9 -1
View File
@@ -60,7 +60,15 @@ export function SheetSection({
>
<CollapsiblePrimitive.Trigger
data-slot="sheet-section-trigger"
className="group focus-visible:ring-ring/50 -mx-1 flex w-[calc(100%+0.5rem)] items-center gap-2 rounded-md px-1 py-1.5 text-left transition-colors focus-visible:ring-2 focus-visible:outline-none"
// `w-full`, not `-mx-1 w-[calc(100%+0.5rem)] px-1`. The old pair held
// the row's text at the section's own left edge while pushing the box
// 4 px outward on each side, purely to hold the focus ring off the
// label — but a Tailwind ring is drawn OUTSIDE the border box anyway,
// so the bleed bought nothing and made every row 8 px wider than the
// sheet body that scrolls it. In the desktop dialog, whose body has no
// horizontal padding to absorb it, that surfaced as a sideways-
// scrollable form on the cycle and mood sheets.
className="group focus-visible:ring-ring/50 flex w-full items-center gap-2 rounded-md py-1.5 text-left transition-colors focus-visible:ring-2 focus-visible:outline-none"
>
{icon ? (
<span