4ebbc6dacf
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.'
112 lines
4.0 KiB
TypeScript
112 lines
4.0 KiB
TypeScript
import type { Metadata, Viewport } from "next";
|
|
import { SmoothViewTransition } from "@/components/transitions/SmoothViewTransition";
|
|
|
|
const BASE_URL = process.env.NEXT_PUBLIC_SITE_URL ?? "https://routecommerce.com";
|
|
|
|
// Structured data for the Indian River Direct storefront.
|
|
// LocalBusiness + BreadcrumbList for the family farm brand.
|
|
const indianRiverStructuredData = {
|
|
"@context": "https://schema.org",
|
|
"@graph": [
|
|
{
|
|
"@type": "BreadcrumbList",
|
|
itemListElement: [
|
|
{ "@type": "ListItem", position: 1, name: "Home", item: BASE_URL },
|
|
{ "@type": "ListItem", position: 2, name: "Indian River Direct", item: `${BASE_URL}/indian-river-direct` },
|
|
],
|
|
},
|
|
{
|
|
"@type": "LocalBusiness",
|
|
"@id": `${BASE_URL}/indian-river-direct/#localbusiness`,
|
|
name: "Indian River Direct",
|
|
description:
|
|
"Fresh peaches and citrus from our Florida groves to truckload sales in your neighborhood. Family-owned since 1985.",
|
|
url: `${BASE_URL}/indian-river-direct`,
|
|
image: `${BASE_URL}/og-default.svg`,
|
|
priceRange: "$$",
|
|
address: {
|
|
"@type": "PostalAddress",
|
|
addressRegion: "FL",
|
|
addressCountry: "US",
|
|
},
|
|
foundingDate: "1985",
|
|
},
|
|
],
|
|
};
|
|
|
|
export const metadata: Metadata = {
|
|
title: {
|
|
default: "Indian River Direct | Peach & Citrus Truckload",
|
|
template: "%s | Indian River Direct",
|
|
},
|
|
description: "Fresh peaches and citrus from our Florida groves to truckload sales in your neighborhood. Family-owned since 1985. Pre-order now for 2026 season.",
|
|
keywords: ["peaches", "citrus", "Florida produce", "truckload sales", "fresh fruit", "Indian River", "wholesale peaches"],
|
|
authors: [{ name: "Indian River Direct" }],
|
|
creator: "Indian River Direct",
|
|
publisher: "Indian River Direct",
|
|
openGraph: {
|
|
title: "Indian River Direct | Fresh Peaches & Citrus",
|
|
description: "Fresh peaches and citrus from our Florida groves to truckload sales in your neighborhood. Family-owned since 1985.",
|
|
url: `${BASE_URL}/indian-river-direct`,
|
|
siteName: "Indian River Direct",
|
|
locale: "en_US",
|
|
type: "website",
|
|
images: [
|
|
{
|
|
url: "/og-default.svg",
|
|
width: 1200,
|
|
height: 630,
|
|
alt: "Indian River Direct - Fresh Peaches & Citrus",
|
|
},
|
|
],
|
|
},
|
|
twitter: {
|
|
card: "summary_large_image",
|
|
title: "Indian River Direct | Peach & Citrus Truckload",
|
|
description: "Fresh peaches and citrus from our Florida groves. Family-owned since 1985. Pre-order for 2026 season.",
|
|
site: "@IndianRiverDirect",
|
|
creator: "@IndianRiverDirect",
|
|
images: ["/og-default.svg"],
|
|
},
|
|
alternates: {
|
|
canonical: `${BASE_URL}/indian-river-direct`,
|
|
},
|
|
robots: {
|
|
index: true,
|
|
follow: true,
|
|
googleBot: {
|
|
index: true,
|
|
follow: true,
|
|
},
|
|
},
|
|
other: {
|
|
"application/ld+json": JSON.stringify(indianRiverStructuredData),
|
|
},
|
|
};
|
|
|
|
export const viewport: Viewport = {
|
|
width: "device-width",
|
|
initialScale: 1,
|
|
maximumScale: 5,
|
|
};
|
|
|
|
export default function IndianRiverLayout({ children }: { children: React.ReactNode }) {
|
|
return (
|
|
<div className="min-h-screen relative">
|
|
{/* Glass morphism gradient background */}
|
|
<div className="fixed inset-0 bg-gradient-to-br from-blue-100/40 via-white/60 to-blue-50/30" />
|
|
|
|
{/* Decorative glass orbs */}
|
|
<div className="fixed inset-0 pointer-events-none overflow-hidden">
|
|
<div className="absolute -top-40 -right-40 w-96 h-96 bg-gradient-to-br from-blue-200/30 to-transparent rounded-full blur-3xl" />
|
|
<div className="absolute top-1/3 -left-20 w-72 h-72 bg-gradient-to-br from-blue-100/25 to-transparent rounded-full blur-2xl" />
|
|
<div className="absolute bottom-0 right-1/4 w-80 h-80 bg-gradient-to-br from-blue-50/40 to-transparent rounded-full blur-3xl" />
|
|
</div>
|
|
|
|
{/* Content wrapper — crossfades on navigation. */}
|
|
<div id="page-content" className="relative outline-none">
|
|
<SmoothViewTransition>{children}</SmoothViewTransition>
|
|
</div>
|
|
</div>
|
|
);
|
|
} |