fix(detail): portal the fullscreen zoom viewer to body (escape the modal dialog transform)#530
Merged
Merged
Conversation
…the dialog transform In the @modal route the detail view lives inside Radix DialogContent, which is `position:fixed` with `translate(-50%,-50%)` (plus zoom-in/out animations). A transformed ancestor becomes the containing block for `position:fixed` descendants, so the WebGL fullscreen viewer's `fixed inset-0` mapped to the dialog's (shifted/clipped) box instead of the viewport — the zoom came up blank. The full-page /preview route has no DialogContent ancestor, which is why zoom worked there. Render the viewer via `createPortal(..., document.body)` so it escapes the transform and covers the real viewport. Guarded with `typeof document` for SSR. The React tree (and the #510 mount/visibility/destroy lifecycle) is unchanged — only the DOM parent moves to body. The modal already sets `onInteractOutside=preventDefault` + `modal={false}`, so viewer interaction outside DialogContent doesn't dismiss the dialog; z-[100] > dialog z-50 keeps it on top. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
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.
Symptom
Grid → modal → open photo zoom = blank/white, but the direct
/preview/{id}(full-page) URL zooms fine. Environment-independent (full-page renders on the same GPU). Distinct from the LRU mount-gate fix.Root cause
In the
@modalroute the detail view renders inside Radix DialogContent, whose base styles areposition:fixed; top:50%; left:50%; translate-x-[-50%] translate-y-[-50%](+zoom-in/out-95animations). Per CSS, a transformed ancestor becomes the containing block forposition:fixeddescendants — so the WebGL fullscreen viewer (fixed inset-0 z-[100], rendered inside DialogContent) had itsinset-0resolved against the dialog's translated/clipped box instead of the viewport → off-screen/clipped → blank. The full-page route has no DialogContent ancestor, so it worked there.Fix
Render the viewer via
createPortal(viewer, document.body)so it escapes the transformed ancestor and covers the real viewport.body. The fix(viewer): keep zoom viewer mounted + CSS-toggle (restore context reuse/state, follow-up to #509) #510 lifecycle (mount onhasOpenedFullScreen,visibilitytoggle,destroyon unmount) and the GL-context LRU are untouched.typeof document !== 'undefined'(viewer is client-only + only afterhasOpenedFullScreen).modal.tsxalready setsonInteractOutside={(e)=>e.preventDefault()}+modal={false}, so interacting with the portaled viewer (now DOM-outside DialogContent) does not dismiss the dialog.z-[100]> dialogz-50; both at body level → viewer on top.tsc+eslintclean. Headless can't exercise the modal WebGL viewer (no GPU + soft-nav intercept), so post-deploy verification: devtools confirms the viewer'sboundingClientRectis the full viewport (0,0,vw,vh) rather than the dialog box, and Manjusaka confirms zoom renders in the modal on his device.