On a phone the card dialog slides alone, so it slides smoothly #224
No reviewers
Labels
No labels
No milestone
No project
No assignees
3 participants
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
Grandiras/Ponente!224
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "phone-card-dialog-smooth"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
The card dialog's entrance on a phone felt sluggish next to every other animation in the app. A Chrome trace of a tap on an emulated phone shows that the slide itself was never the cost: it is a
transform, and the compositor runs it. The cost was everything happening around it.What the trace showed
box-shadow+transform, column hairlineopacity, the scrim fade on the dialog's own parent, the slidescalepress, the scrim fade (on::before), the slidebox-shadow(compositeFailed,unsupportedProperties: box-shadow)compositeFailed: 0for all three):hoverstuck on it. The hover lift is abox-shadow, so the board repainted on every frame of the slide. The lift and the column hairline's hover are now@media (hover: hover)only. A finger gets ascale: 0.98press instead, which the compositor runs by itself. It comes before.card-chosen, so a long press still grows the card.opacityon.modal-backdrop, the dialog's parent. That puts the whole full-screen sheet into an offscreen surface that has to be blended every frame of the fade. The scrim's colour, blur and fade now live on.modal-backdrop::beforeatz-index: -1. This applies to all three dialogs at every width, and the desktop blur still works (checked by screenshot).slideUpMobile's100%was being measured against its height.max-heightis also the backdrop's100%now instead of100vh, because100vhis taller than the window while the browser's toolbar is showing. The feedback and release-notes dialogs are still content-sized sheets.Tests
mobile.spec.js› Card dialog by touch (Pixel 7, touch) checks these in the browser's own terms rather than by timing frames. Headless Chromium on a desktop GPU would pass a frame-timing test whatever the CSS did.(hover: none)context, a pointer resting on a card must not change its shadow or transform.transitionrun/animationstartfrom the tap on and refuses any board property the compositor can't animate.fadeInmust be on the::before, not the backdrop.Checked against the old stylesheet: the test fails there, first on the hover lift and then on the fade.
The test stack was in use by another session, so I ran
mobile.spec.jsandboard.spec.jsagainst a hostdotnet runwith its own Postgres. 29 of 30 passed. The one failure, the menu opens as a sheet, also fails onmainin that setup: it counts menu rows, and the host app has no feedback repository configured.dotnet test: 560 passed.Docs:
docs/ui.md(card dialog section: why only the sheet and scrim may move, the three rules, and what the test pins) anddocs/frontend.md(.card-chosenand the finger's:activeboth usescale). Changelog entry added in both locales.Not verified on a real phone. The trace shows no main-thread work left during the slide, but the proof is how it feels in your hand.
🤖 Generated with Claude Code
🤖 Reviewing this PR — the review will link the full session transcript.
✅ Verdict: approve — no blocking issues found.
Solid, well-evidenced change. The three fixes are mechanically correct in the stylesheet: the
::beforescrim atz-index: -1layers properly inside the backdrop's own stacking context (and the mobile override retargets it correctly),max-height: 100%resolves against the fixed backdrop's definite height so the full-height card sheet matchesinnerHeight, and the(hover: none)press onscalesits before.card-chosen(which also wins on specificity), so a held press still grows the card. The test pins the behaviour in the browser's own terms — event-based recording with pseudo-element checks rather than frame timing — which is the right call for headless runs. Docs and both changelog locales are consistent with the code. Minor non-blocking note: the hover check relies on(hover: none)matching under Playwright's Pixel 7 emulation, which device descriptors don't set explicitly — but the reported failure of the test against the old stylesheet shows it discriminates in practice.⏱ 1m 42s · 5 steps · $0.0091 · tokens: 44.6k in / 3.1k out (+29.2k cache-read) · Go: 5h 4% · wk 75% · mo 70%
📜 Full session transcript