feat: smooth view transitions, no skeleton flash
Deploy to route.crispygoat.com / deploy (push) Successful in 4m21s

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.'
This commit is contained in:
Tyler
2026-06-16 23:37:00 -06:00
parent 9458fd0506
commit 4ebbc6dacf
27 changed files with 304 additions and 1105 deletions
+13 -111
View File
@@ -1,112 +1,14 @@
import { LoadingFade } from "@/components/transitions/LoadingFade";
/**
* Admin shell loading state.
*
* Replaces the previous skeleton (header + 4 stat cards + 2 panels +
* 5-row table) with a 1px-thick animated bar at the top of the page.
* The AdminSidebar and parchment background stay mounted, so the
* transition is a subtle fade — the user never sees an "empty" version
* of the admin while the next page streams in.
*/
export default function AdminLoading() {
return (
<main className="min-h-screen px-4 sm:px-6 md:px-8 py-6" style={{ backgroundColor: "var(--admin-bg)" }}>
{/* Header skeleton */}
<div className="flex items-center justify-between mb-5">
<div className="flex items-center gap-3">
<div className="animate-pulse w-9 h-9 rounded-lg" style={{ backgroundColor: "var(--admin-border)" }} />
<div className="space-y-2">
<div className="animate-pulse h-5 w-36 rounded" style={{ backgroundColor: "var(--admin-border)" }} />
<div className="animate-pulse h-3 w-24 rounded" style={{ backgroundColor: "var(--admin-border)" }} />
</div>
</div>
<div className="animate-pulse h-8 w-28 rounded-lg" style={{ backgroundColor: "var(--admin-border)" }} />
</div>
{/* Stats cards skeleton */}
<div className="grid grid-cols-2 lg:grid-cols-4 gap-3 mb-5">
{[1, 2, 3, 4].map((i) => (
<div key={i} className="rounded-xl bg-white border overflow-hidden">
<div className="flex items-center gap-3 p-4">
<div className="animate-pulse w-9 h-9 rounded-xl flex-shrink-0" style={{ backgroundColor: "var(--admin-border)" }} />
<div className="space-y-1.5 flex-1">
<div className="animate-pulse h-2.5 w-16 rounded" style={{ backgroundColor: "var(--admin-border)" }} />
<div className="animate-pulse h-6 w-12 rounded" style={{ backgroundColor: "var(--admin-border)" }} />
</div>
</div>
</div>
))}
</div>
{/* Quick Actions + Usage skeleton */}
<div className="grid grid-cols-1 lg:grid-cols-3 gap-3 mb-5">
<div className="rounded-xl bg-white border overflow-hidden">
<div className="flex items-center justify-between px-4 py-3 border-b" style={{ borderColor: "var(--admin-border-light)" }}>
<div className="animate-pulse h-3 w-20 rounded" style={{ backgroundColor: "var(--admin-border)" }} />
</div>
<div className="grid grid-cols-2 gap-2 p-3">
{[1, 2, 3, 4].map((i) => (
<div key={i} className="animate-pulse rounded-lg h-10" style={{ backgroundColor: "var(--admin-border)" }} />
))}
</div>
</div>
<div className="lg:col-span-2 rounded-xl bg-white border overflow-hidden">
<div className="flex items-center gap-2 px-4 py-3 border-b" style={{ borderColor: "var(--admin-border-light)" }}>
<div className="animate-pulse h-5 w-16 rounded-full" style={{ backgroundColor: "var(--admin-border)" }} />
<div className="animate-pulse h-3 w-24 rounded" style={{ backgroundColor: "var(--admin-border)" }} />
</div>
<div className="grid grid-cols-1 sm:grid-cols-3 gap-4 p-4">
{[1, 2, 3].map((i) => (
<div key={i} className="space-y-2">
<div className="flex items-center justify-between">
<div className="animate-pulse h-2.5 w-10 rounded" style={{ backgroundColor: "var(--admin-border)" }} />
<div className="animate-pulse h-3 w-12 rounded" style={{ backgroundColor: "var(--admin-border)" }} />
</div>
<div className="animate-pulse h-1.5 rounded-full" style={{ backgroundColor: "var(--admin-border)" }} />
</div>
))}
</div>
</div>
</div>
{/* Recent orders skeleton */}
<div className="rounded-xl bg-white border overflow-hidden mb-5">
<div className="flex items-center justify-between px-4 py-3 border-b" style={{ borderColor: "var(--admin-border-light)" }}>
<div className="animate-pulse h-3 w-20 rounded" style={{ backgroundColor: "var(--admin-border)" }} />
<div className="animate-pulse h-3 w-14 rounded" style={{ backgroundColor: "var(--admin-border)" }} />
</div>
<div className="divide-y" style={{ borderColor: "var(--admin-border-light)" }}>
{[1, 2, 3, 4, 5].map((i) => (
<div key={i} className="flex items-center justify-between px-4 py-3">
<div className="flex items-center gap-3">
<div className="animate-pulse w-8 h-8 rounded-lg flex-shrink-0" style={{ backgroundColor: "var(--admin-border)" }} />
<div className="space-y-1">
<div className="animate-pulse h-3 w-28 rounded" style={{ backgroundColor: "var(--admin-border)" }} />
<div className="animate-pulse h-2.5 w-16 rounded" style={{ backgroundColor: "var(--admin-border)" }} />
</div>
</div>
<div className="flex items-center gap-3">
<div className="animate-pulse h-3 w-14 rounded" style={{ backgroundColor: "var(--admin-border)" }} />
<div className="animate-pulse h-5 w-16 rounded-full" style={{ backgroundColor: "var(--admin-border)" }} />
</div>
</div>
))}
</div>
</div>
{/* Filter tabs skeleton */}
<div className="flex gap-2 mb-4">
{[1, 2, 3, 4].map((i) => (
<div key={i} className="animate-pulse h-9 w-20 rounded-lg" style={{ backgroundColor: "var(--admin-border)" }} />
))}
</div>
{/* Section cards skeleton */}
<div className="grid grid-cols-2 sm:grid-cols-3 xl:grid-cols-4 gap-3">
{[1, 2, 3, 4, 5, 6].map((i) => (
<div key={i} className="rounded-xl bg-white border overflow-hidden">
<div className="flex items-start justify-between p-4">
<div className="animate-pulse w-9 h-9 rounded-xl" style={{ backgroundColor: "var(--admin-border)" }} />
<div className="animate-pulse w-12 h-4 rounded-full" style={{ backgroundColor: "var(--admin-border)" }} />
</div>
<div className="px-4 pb-4 space-y-1.5">
<div className="animate-pulse h-3 w-24 rounded" style={{ backgroundColor: "var(--admin-border)" }} />
<div className="animate-pulse h-2.5 w-full rounded" style={{ backgroundColor: "var(--admin-border)" }} />
<div className="animate-pulse h-2.5 w-3/4 rounded" style={{ backgroundColor: "var(--admin-border)" }} />
</div>
</div>
))}
</div>
</main>
);
}
return <LoadingFade />;
}