Repository navigation
Fix the timeline background on mobile: parallax gap, dead renderer after rotation, stale canvas size - #16
Merged
Conversation
Three defects in the fixed background canvas, all reported as "the About page looks broken on mobile". Parallax ran off the page. The canvas is translated up by scrollY * 0.02 while its container reserves a flat 10vh to cover the shift - a budget of five viewport heights. The About page is ~26 viewports on a phone, so past ~4,200px of scroll the canvas slid out from under the viewport and left a hard-edged #f5f5f5 band, reaching ~430px of an 844px screen at full scroll. Clamp the offset to the reserved buffer, and drive both the buffer and the factor from shared constants so they cannot drift apart again. The renderer died on rotation. A threadCount change resets threads to [], which unmounts the canvas; the init effect did not depend on threads, so the replacement element was never handed to a worker and nothing drew on it again. A phone rotation crosses three threadCount thresholds, so rotating once blanked the background until reload. Depend on threads.length, and key the canvas so every re-init gets a virgin element - transferControlToOffscreen only works once per canvas. The canvas was never resized. Its backing store is sized once, at transfer time, and neither renderer reads the box again, so any resize that left the performance profile alone stretched a stale bitmap across the new box. Watch the box with a ResizeObserver and rebuild when it diverges from the size the renderer was built at, settling for 250ms so a window drag does not respawn a worker every frame. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
course-opportunities.test.ts imports from "bun:test", which had no types installed, leaving one error in bunx tsc --noEmit. TypeScript 6 in this project does not auto-include node_modules/@types, so naming the package explicitly is required on top of installing it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
This branch was successfully deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Reported by Tomás: the About page looks half broken on an iPhone 16, "creo que es por el background, el parallax hace algo raro". That read was right. Reproduced on production, and found two more defects in the same component while verifying.
1. The parallax slid the canvas off the bottom of the page
timeline-threads.tsxtranslates the fixed background canvas up byscrollY * 0.02, and the container reserved a flat10vhfor that shift, with this comment:That budget is five viewport heights. The About page is 21,700–29,200px tall at 390px wide — 26 to 34 viewports. Past
scrollY > 5 × innerHeight(~4,200px, the first 15% of the page) the canvas moved further than its buffer and a flat#f5f5f5band crept up from the bottom of the screen. That hard line is what Tomás screenshotted.Measured on baish.com.ar, blank band at the bottom of the viewport:
Fixed by clamping the offset to the reserved buffer.
PARALLAX_FACTORandPARALLAX_BUFFER_VHnow live at module scope so the scroll handler and the container height cannot drift apart.2. Rotating the phone killed the background permanently
A
threadCountchange callssetThreads([]), which unmounts the canvas. The renderer-init effect depended on[shouldAnimate, blurStdDeviation, frameInterval]— not on threads — so when the replacement canvas mounted, the effect never re-ran and the element was never transferred to a worker. Nothing drew on it again.threadCountchanges at widths 480 / 640 / 820 / 1440 / 1920, so a rotation (390 → 844) crosses three of them.Fixed by depending on
threads.lengthand keying the canvas, so each re-init gets a fresh element —transferControlToOffscreen()only works once per canvas.3. The canvas was never resized
The backing store is sized once at transfer time and neither renderer reads the box again, so any resize that left the performance profile alone (a plain window drag, or one staying inside a thread-count bucket) stretched a stale bitmap across the new box. A
ResizeObservernow rebuilds the renderer when the box diverges from the size it was built at, with a 250ms settle so a drag doesn't respawn a worker every frame.Verified no spurious rebuilds: idle and heavy scrolling both leave the canvas untouched. This matters on iOS, where the URL bar collapsing changes
innerHeight— the container is sized invh, which doesn't track that, so the box stays stable.Also
bunx tsc --noEmithad one pre-existing error:course-opportunities.test.tsimportsbun:testwith no types installed. Added@types/bun. TypeScript 6 here does not auto-includenode_modules/@types(confirmed with--traceResolution), so"types": ["bun"]in tsconfig is needed on top of the install.Checks
bunx tsc --noEmit— 0 errors (was 1)bun run lint— cleanbun test— 6 passbun run build— succeeds/espage at 390pxtypesarray didn't mask real checking — a deliberateCSSPropertieserror was still caughtKnown, not addressed
The parallax now saturates after ~4,200px of scroll and holds. The alternatives are worse: scaling the offset to scroll progress makes the effect strength depend on page length (4× stronger on a two-screen page than on About), and growing the buffer costs mobile fill-rate on every page, which this component is tuned hard to avoid.
🤖 Generated with Claude Code