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).
The site had a lot of aggressive motion that compounded into a
vertigo-inducing experience. Visual design (colors, type, layout) is
unchanged — only the movement has been calmed.
Single-commit overview:
- Route transitions: 90/140ms pure-opacity crossfade (was 220ms with
4-6px Y-shift on enter/exit).
- atelier-* modal animations: 180ms opacity-only, 4px max (was 420ms
with 20px slide + scale(0.96→1) and 80-440ms cumulative stagger).
- Hover transforms: -1px lift or no lift (was -2px + scale(0.98)).
- CTA shimmer: 400ms (was 700ms).
- Toggle thumb: ease-out (was cubic-bezier(0.34, 1.56, 0.64, 1) bouncy
overshoot).
- GSAP ScrollAnimations: capped at 12-16px translation, 320ms duration,
power1.out, reduced-motion guards at the top of every effect.
ParallaxLayer no longer scrolls content; only the data-parallax
attribute can opt in, and only to 24px.
- TuxedoVideoHero: killed 80px scroll-driven Y-shift on hero, killed
video 1.15 scale-on-scroll, killed parallax-float scroll effect, cut
hero-reveal to 8px/320ms (was 40px/1s/power3.out), removed the
motion.scale on the logo and CTA buttons, slowed the bouncing
scroll indicator from 1.5s to 2.4s.
- CinematicShowcase: killed morphing product cards (rotateY ±5°,
scale 0.95→1.02), killed parallax background HARVEST text (-100px),
killed translateX carousel, killed scale(0.9→1) back.out(1.4) reveal
in favor of opacity-only 8px/320ms entrance, removed progress-dot
scale, removed progress-bar transition lag.
- OnboardingFlow: removed scale(0.9→1) and y:20→0 entrance animations.
- Global MotionConfig: caps every framer-motion animation in the tree
to 0.2s easeOut, and sets reducedMotion='user' so framer-motion
automatically strips x/y/scale/rotate from all 71 motion.div reveals
across the public site when the OS prefers-reduced-motion is set.
- globals.css prefers-reduced-motion block: comprehensive kill switch
that disables animation/transition duration app-wide, wipes the
route view-transition, and clears the .parallax-float / .hero-reveal
transforms.
How to test:
- Default: motion is calmer, ~10x faster, with no parallax
- OS-level 'reduce motion' on: zero positional movement, opacity fades
only.
Files changed: 7 (no new files)
User pain point: skeleton loading.tsx files made the app feel like
a sequence of page reloads, exposing backend latency. Replaced with
a single 1px shimmer bar + crossfade via React's <ViewTransition>.
Changes:
- Enable experimental.viewTransition in next.config.ts
- Add SmoothViewTransition wrapper (ViewTransition name=page-content)
- Add LoadingFade component: thin animated bar instead of skeleton
- Add RouteAnnouncer for a11y (screen readers + focus reset)
- Add ::view-transition-old/new CSS for the crossfade (220ms, no
jarring slide, respects prefers-reduced-motion)
- Wrap admin/tuxedo/IRD layout children in SmoothViewTransition
(sidebar/header/footer stay mounted; only the body fades)
- Replace 19 skeleton loading.tsx files with the fade component
Result: navigation now feels like a single app, not a series of
preload-and-render events. The user never sees a 'skeleton of the
page they're about to load.'