Disable named view transition on admin page-content wrapper
Deploy to route.crispygoat.com / deploy (push) Successful in 4m12s
Deploy to route.crispygoat.com / deploy (push) Successful in 4m12s
The <ViewTransition name="page-content"> wrapper around the admin main content was triggering a 'shared element' morph on every page load and navigation: the browser tries to animate the bounding box of the named element across the old and new pages, and since each admin page has a different height, the browser was scaling the snapshot from the top-left corner. That read as the page 'loading from the corner with keyframes' and pushed the bottom of the content to the top during the transition. Setting update="none" opts out of the named transition entirely. The children just swap on navigation — no fade, no scale, no morph. The AdminSidebar + parchment background stay mounted across navigations so the cut reads as a content swap inside a stable shell, which is what the eye expects for a logged-in admin app. The ::view-transition-old(page-content) / ::view-transition-new CSS rules in globals.css are left in place in case we want to re-enable with a different shape later (pure-opacity rules on the root pseudo-elements instead of a named wrapper).
This commit is contained in:
@@ -4,48 +4,29 @@ import { ViewTransition } from "react";
|
|||||||
import type { ReactNode } from "react";
|
import type { ReactNode } from "react";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Smooth view-transition wrapper for any page-section content.
|
* Page-content wrapper. The named `page-content` view transition used
|
||||||
|
* to apply a CSS-driven crossfade (::view-transition-old/new rules in
|
||||||
|
* globals.css) on every navigation. The opacity fade itself was fine,
|
||||||
|
* but because the named transition attaches `view-transition-name:
|
||||||
|
* page-content` to the wrapper, the browser also tries to morph the
|
||||||
|
* wrapper's bounding box across pages — and since each admin page has
|
||||||
|
* a different height, the browser was scaling the snapshot from the
|
||||||
|
* top-left corner, which read as the page "loading from the corner"
|
||||||
|
* and pushed the bottom of the content to the top during the
|
||||||
|
* transition.
|
||||||
*
|
*
|
||||||
* Drop this around the main content area of a route (or around an
|
* `update="none"` opts out of the named transition entirely. The
|
||||||
* individual <Suspense> chunk) to opt that subtree into a soft
|
* children just swap on navigation (no fade, no scale, no morph) —
|
||||||
* crossfade on navigation. The browser's native View Transitions API
|
* the AdminSidebar + parchment background stay mounted across
|
||||||
* handles the actual animation — when the browser doesn't support it,
|
* navigations so the cut reads as a content swap inside a stable
|
||||||
* the children just swap, no error.
|
* shell, which is what the eye expects for a logged-in admin app.
|
||||||
*
|
*
|
||||||
* `name` groups elements so the browser can morph the same element
|
* If we ever want a subtle crossfade back, the right shape is a
|
||||||
* across pages. Use the same name on the source and destination (e.g.
|
* pure-opacity ::view-transition-old/new pair with NO named wrapper
|
||||||
* the page header) for shared-element morphing. Use the default
|
* (i.e. drop the `name` prop and add the rules to
|
||||||
* `"page-content"` for plain crossfades.
|
* ::view-transition-old(root) / ::view-transition-new(root) so the
|
||||||
*
|
* browser doesn't morph the bounding box).
|
||||||
* Motion note: the CSS-side ::view-transition-old/new rules are a
|
|
||||||
* 90/140ms pure-opacity fade with NO translate. Earlier versions
|
|
||||||
* shifted the page 4-6px on enter/exit which compounded with the route
|
|
||||||
* content settling into place and read as a small "drop" — bad for
|
|
||||||
* vestibular comfort. A flat opacity crossfade softens the cut
|
|
||||||
* without any positional movement.
|
|
||||||
*
|
|
||||||
* Usage:
|
|
||||||
* <main>
|
|
||||||
* <SmoothViewTransition>{children}</SmoothViewTransition>
|
|
||||||
* </main>
|
|
||||||
*/
|
*/
|
||||||
export function SmoothViewTransition({
|
export function SmoothViewTransition({ children }: { children: ReactNode }) {
|
||||||
children,
|
return <ViewTransition update="none">{children}</ViewTransition>;
|
||||||
name = "page-content",
|
|
||||||
}: {
|
|
||||||
children: ReactNode;
|
|
||||||
name?: string;
|
|
||||||
}) {
|
|
||||||
return (
|
|
||||||
<ViewTransition
|
|
||||||
name={name}
|
|
||||||
// `default` runs a CSS-driven crossfade using the
|
|
||||||
// ::view-transition-* pseudo-elements defined in globals.css.
|
|
||||||
// Other options: "none" (instant cut), or a custom string your
|
|
||||||
// CSS can match.
|
|
||||||
update="default"
|
|
||||||
>
|
|
||||||
{children}
|
|
||||||
</ViewTransition>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user