Site Motion Log 03: Fast Routes, Local Page Entry
Blog, gallery, calligraphy, and about needed a page entry feel, but not a global loading overlay. Local PageEnter wrappers keep route changes fast.
Once the homepage motion felt right, the secondary pages looked abrupt. Blog, gallery, calligraphy, and about appeared too suddenly.
The fix was not to bring back a global route transition. That had already caused a heavy “loading twice” feeling. Route changes should respond quickly. The page content can still arrive softly.
No Visual Global Route Loading
PageTransition.tsx is now structural only:
export default function PageTransition({ children }: { children: ReactNode }) {
return <div className="min-h-[inherit]">{children}</div>;
}It does not use Framer Motion. It does not have AnimatePresence, initial, or exit.
RouteLoading.tsx also stays non-visual:
<div className="sr-only" role="status" aria-live="polite">
{loadingLabel}
</div>The site is mostly static content. Adding a visible loader to every route change makes it feel slower than it is.
Local Page Entry
The secondary pages use a small wrapper in PageEnter.tsx:
<PageEnterGroup>
<PageEnterItem>{children}</PageEnterItem>
</PageEnterGroup>The group adds a small stagger:
staggerChildren: reduceMotion ? 0 : 0.06;Each item enters from a simple hidden state:
hidden: { opacity: 0, y: 18, filter: "blur(4px)" }
visible: { opacity: 1, y: 0, filter: "blur(0px)" }The duration is about 0.4s. It is enough to avoid a flash, but not enough to make the visitor wait.
Where It Applies
The animation is applied to page-level blocks, not every card:
- Blog: page header, filter sidebar, post list.
- Gallery: title block and photo wall.
- Calligraphy: title block and gallery.
- About: left, center, and right columns.
This matters. Animating every item in a long grid would look busy and slow. The page only needs a clear entrance.
One small layout detail came up in the blog page. The filter sidebar is sticky on desktop. The sticky classes had to live on the animated wrapper so the sidebar still behaves correctly after wrapping it.
Why This Is Separate From the Homepage Intro
Secondary pages do not depend on the homepage first-visit state. They only need a local entrance when they mount.
The boundaries are now simple:
- The homepage intro owns only the homepage entrance.
- The route loading fallback stays non-visual.
- Secondary pages handle their own page blocks.
Guarding the Boundary
motion-boundaries.test.ts now checks two things:
PageTransitionandRouteLoadingstill have no visual route animation.- Blog, gallery, calligraphy, and about all use
PageEnter.
The test is not about taste. It protects the architecture from drifting back into a global loading layer.
Takeaway
Routes should feel fast. Page content can still feel placed.
Removing global route motion and adding local page entry solved both sides: no extra loading layer, no sudden flash of content.

