Skip to content

Stop the project gallery seizing up on a phone - #9

Merged
MorganGodden merged 1 commit into
masterfrom
claude/landing-gallery-scroll-drag-mb9rsp
Aug 10, 2026
Merged

MorganGodden merged 1 commit into
masterfrom
claude/landing-gallery-scroll-drag-mb9rsp

Conversation

@MorganGodden

Copy link
Copy Markdown
Owner

Follow-up to #8, which merged before this landed. The gallery could stop drifting on a phone and never start again.

What was wrong

Three things could hold the drift still with nothing left to let it go again, and all three are reachable with a finger but not with a mouse — which is why the desktop testing on #8 missed them.

The hover hold, most likely the one being hit. A finger cannot rest on anything, and pointerleave after a touch is not something every browser sends — a browser taking a pan over is exactly when it goes missing. If it never arrived, the hold went on and nothing could lift it: a later swipe only sets it again. It is now asked for only where hovering is a thing the device does, ignores touch-typed pointers, and is lifted by a cancelled gesture too.

The focus hold. A tap leaves the card it landed on holding the focus. :focus-visible should be false there, but if a browser disagrees the strip is stopped for good. A press now clears the hold outright — whoever is pressing is not the keyboard visitor it was put there for.

A drag that loses its pointer. If pointerup never arrived the drag was never let go of. lostpointercapture is the one event that always turns up, and it now ends the drag as well.

What else changed

The wait before the drift resumes is back to 1200ms, and it is measured from the strip moving rather than from wheel and touch events. Watching the position covers every way the strip can be moved — a trackpad, a swipe, the momentum that outlives the finger, a card being scrolled into view — and needs nothing to tell it the visitor has finished, so it always runs out. The wheel and touchmove listeners are gone with it.

Honesty about the diagnosis

I could not reproduce the stall itself: synthetic touch in Chromium recovered even on the merged code. What is here is the result of instrumenting the component and driving real touch events through CDP to find every path that can hold the drift permanently, then closing all of them. So this is not "found the line and fixed it" — it is "removed the class of bug the report points at".

The testing on #8 also claimed more than it had earned for touch: it drove the strip with scrollBy from script, which produces no touch events at all. The runs below use real touch.

Testing

Mobile emulation, real touch through CDP, against a production build.

Swipe, then leave it alone 40 px/s by +3s
Three swipes in a row 40 px/s by +3s
Swipe the other way 42 px/s by +3s
Tap, then leave it alone 40 px/s throughout
Touch pointerenter whose pointerleave never arrives 80px over 2s — no longer sticks
pointerenter typed mouse on a device that cannot hover 80px over 2s — no longer sticks
Card left focused, then a press hold clears, drift returns
Off screen, with a spacer so it genuinely leaves 0px, resumes on return

Desktop is unregressed: 40.0 px/s, hover holds indefinitely and releases on leave, drag both ways, a drag over a card does not navigate, a plain click does, a tab-focused card holds it, reduced motion stops it, no console errors.

One number moved on purpose: tabbing off the strip now waits 1200ms before drifting, where it used to resume at once. Tabbing scrolls the strip, and that is the visitor scrolling it — the old wheel and touch listeners simply could not see it.

typecheck and build pass. lint reports the same 41 problems with and without this change, all pre-existing, in app.vue and about.vue.


Generated by Claude Code

Three things could hold the gallery still with nothing left to let it go
again, and all three are reachable with a finger.

The worst is the hold that keeps the strip still under a resting
pointer. A finger cannot rest on anything, and `pointerleave` after a
touch is not something every browser sends — so on a phone the hold went
on and nothing lifted it. It is now asked for only where hovering is a
thing the device does, and a cancelled gesture lifts it as well, since a
browser taking a pan over is the moment a leave is most likely to go
missing.

A tap also leaves the card it landed on holding the focus, and a press
now says that whoever is pressing is not the keyboard visitor the focus
hold was put there for. A drag whose pointer capture is lost some other
way is let go of through `lostpointercapture`, which is the one event
that always arrives.

The wait before the drift resumes is back to 1200ms, and it is now
measured from the strip moving rather than from wheel and touch events.
Watching the position covers every way it can be moved, the momentum
that outlives a finger among them, and needs nothing to tell it the
visitor has finished: it reads the last movement, so it always runs out.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015aCGjKwtBpk8NdVKxQiCRA
@vercel

vercel Bot commented Aug 10, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
godden.dev Ready Ready Preview Aug 10, 2026 3:24pm

@MorganGodden
MorganGodden merged commit 82ed557 into master Aug 10, 2026
2 of 3 checks passed

This branch was successfully deployed

1 active deployment
Preview — 380ada8b Deployed Aug 10, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants