Template
Initial commit: Shopify storefront Next.js template
Next.js 16 + React 19 storefront template with Shopify Storefront API integration, Tailwind v4, and shadcn/ui components. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0111qSr3KopyGRsJ6LznR1xZ
This commit is contained in:
@@ -0,0 +1,26 @@
|
||||
import Header from '@/components/shopify/header';
|
||||
import Footer from '@/components/shopify/footer';
|
||||
import CollectionDetail from '@/components/shopify/collection-detail';
|
||||
|
||||
export default function Page() {
|
||||
return (
|
||||
<>
|
||||
<Header
|
||||
storeName="Stride"
|
||||
logoUrl=""
|
||||
links={[
|
||||
{ label: 'Products', url: '/' },
|
||||
{ label: 'Collections', url: '/collections' },
|
||||
]}
|
||||
/>
|
||||
<CollectionDetail />
|
||||
<Footer
|
||||
storeName="Stride"
|
||||
logoUrl=""
|
||||
tagline="Your premium shopping destination"
|
||||
copyright="© 2026 Stride. All rights reserved."
|
||||
links={[]}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
import Header from '@/components/shopify/header';
|
||||
import Footer from '@/components/shopify/footer';
|
||||
import Collections from '@/components/shopify/collections';
|
||||
|
||||
export default function Page() {
|
||||
return (
|
||||
<>
|
||||
<Header
|
||||
storeName="Stride"
|
||||
logoUrl=""
|
||||
links={[
|
||||
{ label: 'Products', url: '/' },
|
||||
{ label: 'Collections', url: '/collections' },
|
||||
]}
|
||||
/>
|
||||
<Collections title="Our Collections" />
|
||||
<Footer
|
||||
storeName="Stride"
|
||||
logoUrl=""
|
||||
tagline="Your premium shopping destination"
|
||||
copyright="© 2026 Stride. All rights reserved."
|
||||
links={[]}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
import { cn } from "@/lib/utils";
|
||||
import type { HTMLAttributes } from "react";
|
||||
|
||||
interface LoaderIconProps {
|
||||
size?: number;
|
||||
}
|
||||
|
||||
const LoaderIcon = ({ size = 16 }: LoaderIconProps) => (
|
||||
<svg
|
||||
height={size}
|
||||
strokeLinejoin="round"
|
||||
style={{ color: "currentcolor" }}
|
||||
viewBox="0 0 16 16"
|
||||
width={size}
|
||||
>
|
||||
<title>Loader</title>
|
||||
<g clipPath="url(#clip0_2393_1490)">
|
||||
<path d="M8 0V4" stroke="currentColor" strokeWidth="1.5" />
|
||||
<path
|
||||
d="M8 16V12"
|
||||
opacity="0.5"
|
||||
stroke="currentColor"
|
||||
strokeWidth="1.5"
|
||||
/>
|
||||
<path
|
||||
d="M3.29773 1.52783L5.64887 4.7639"
|
||||
opacity="0.9"
|
||||
stroke="currentColor"
|
||||
strokeWidth="1.5"
|
||||
/>
|
||||
<path
|
||||
d="M12.7023 1.52783L10.3511 4.7639"
|
||||
opacity="0.1"
|
||||
stroke="currentColor"
|
||||
strokeWidth="1.5"
|
||||
/>
|
||||
<path
|
||||
d="M12.7023 14.472L10.3511 11.236"
|
||||
opacity="0.4"
|
||||
stroke="currentColor"
|
||||
strokeWidth="1.5"
|
||||
/>
|
||||
<path
|
||||
d="M3.29773 14.472L5.64887 11.236"
|
||||
opacity="0.6"
|
||||
stroke="currentColor"
|
||||
strokeWidth="1.5"
|
||||
/>
|
||||
<path
|
||||
d="M15.6085 5.52783L11.8043 6.7639"
|
||||
opacity="0.2"
|
||||
stroke="currentColor"
|
||||
strokeWidth="1.5"
|
||||
/>
|
||||
<path
|
||||
d="M0.391602 10.472L4.19583 9.23598"
|
||||
opacity="0.7"
|
||||
stroke="currentColor"
|
||||
strokeWidth="1.5"
|
||||
/>
|
||||
<path
|
||||
d="M15.6085 10.4722L11.8043 9.2361"
|
||||
opacity="0.3"
|
||||
stroke="currentColor"
|
||||
strokeWidth="1.5"
|
||||
/>
|
||||
<path
|
||||
d="M0.391602 5.52783L4.19583 6.7639"
|
||||
opacity="0.8"
|
||||
stroke="currentColor"
|
||||
strokeWidth="1.5"
|
||||
/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_2393_1490">
|
||||
<rect fill="white" height="16" width="16" />
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
);
|
||||
|
||||
export type LoaderProps = HTMLAttributes<HTMLDivElement> & {
|
||||
size?: number;
|
||||
};
|
||||
|
||||
export const Loader = ({ className, size = 16, ...props }: LoaderProps) => (
|
||||
<div
|
||||
className={cn(
|
||||
"inline-flex animate-spin items-center justify-center",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
<LoaderIcon size={size} />
|
||||
</div>
|
||||
);
|
||||
@@ -0,0 +1,25 @@
|
||||
"use client";
|
||||
|
||||
import React from 'react';
|
||||
|
||||
export default function Error({
|
||||
error,
|
||||
reset,
|
||||
}: {
|
||||
error: Error & { digest?: string };
|
||||
reset: () => void;
|
||||
}) {
|
||||
return (
|
||||
<div className="flex items-center justify-center min-h-screen">
|
||||
<div className="flex items-center gap-6">
|
||||
<div className="text-lg font-medium text-black font-sans whitespace-nowrap">
|
||||
Something went wrong
|
||||
</div>
|
||||
<div className="border-l border-gray-300 h-6"></div>
|
||||
<div className="text-sm text-gray-700 font-sans max-w-lg">
|
||||
{error.message}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
+112
@@ -0,0 +1,112 @@
|
||||
@import 'tailwindcss';
|
||||
|
||||
@theme {
|
||||
/* Refined neutral modern palette */
|
||||
--color-background: hsl(0 0% 100%);
|
||||
--color-foreground: hsl(240 10% 3.9%);
|
||||
|
||||
/* Card */
|
||||
--color-card: hsl(0 0% 100%);
|
||||
--color-card-foreground: hsl(240 10% 3.9%);
|
||||
|
||||
/* Popover */
|
||||
--color-popover: hsl(0 0% 100%);
|
||||
--color-popover-foreground: hsl(240 10% 3.9%);
|
||||
|
||||
/* Primary - Deep neutral slate for professional look */
|
||||
--color-primary: hsl(240 5.9% 10%);
|
||||
--color-primary-foreground: hsl(0 0% 98%);
|
||||
|
||||
/* Secondary */
|
||||
--color-secondary: hsl(240 4.8% 95.9%);
|
||||
--color-secondary-foreground: hsl(240 5.9% 10%);
|
||||
|
||||
/* Muted */
|
||||
--color-muted: hsl(240 4.8% 95.9%);
|
||||
--color-muted-foreground: hsl(240 3.8% 46.1%);
|
||||
|
||||
/* Accent - Subtle warm gray */
|
||||
--color-accent: hsl(30 6.7% 95%);
|
||||
--color-accent-foreground: hsl(240 5.9% 10%);
|
||||
|
||||
/* Destructive */
|
||||
--color-destructive: hsl(0 84.2% 60.2%);
|
||||
--color-destructive-foreground: hsl(0 0% 98%);
|
||||
|
||||
/* Border */
|
||||
--color-border: hsl(240 5.9% 90%);
|
||||
--color-input: hsl(240 5.9% 90%);
|
||||
--color-ring: hsl(240 5.9% 10%);
|
||||
|
||||
/* Radius - Modern larger corners */
|
||||
--radius-sm: 0.5rem;
|
||||
--radius-md: 0.75rem;
|
||||
--radius-lg: 1rem;
|
||||
--radius-xl: 1.25rem;
|
||||
|
||||
/* Enhanced typography scale */
|
||||
--font-heading: 'Space Grotesk', system-ui, sans-serif;
|
||||
--font-body: 'Inter', system-ui, sans-serif;
|
||||
--font-poppins: 'Poppins', system-ui, sans-serif;
|
||||
}
|
||||
|
||||
/* Base styles for light, modern Shopify storefront */
|
||||
body {
|
||||
font-feature-settings:
|
||||
'kern' 1,
|
||||
'tnum' 1;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
/* Modern card design for products and collections */
|
||||
.card-modern {
|
||||
transition: all 0.4s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
border: 1px solid var(--color-border);
|
||||
}
|
||||
|
||||
.card-modern:hover {
|
||||
transform: translateY(-8px);
|
||||
box-shadow: 0 25px 50px -12px rgb(0 0 0 / 0.1);
|
||||
border-color: hsl(240 5.9% 85%);
|
||||
}
|
||||
|
||||
/* Bold heading styles */
|
||||
h1,
|
||||
h2,
|
||||
.font-heading {
|
||||
font-weight: 700;
|
||||
letter-spacing: -0.025em;
|
||||
}
|
||||
|
||||
/* Enhanced button styles */
|
||||
button,
|
||||
.btn-modern {
|
||||
transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
}
|
||||
|
||||
.btn-modern:hover {
|
||||
transform: translateY(-1px);
|
||||
box-shadow:
|
||||
0 10px 15px -3px rgb(0 0 0 / 0.1),
|
||||
0 4px 6px -4px rgb(0 0 0 / 0.1);
|
||||
}
|
||||
|
||||
/* Hero section styles */
|
||||
.hero-bg {
|
||||
background: linear-gradient(135deg, #f8f9fa 0%, #f1f3f5 100%);
|
||||
}
|
||||
|
||||
/* Product gallery improvements */
|
||||
.product-gallery-main {
|
||||
transition: box-shadow 0.3s ease;
|
||||
}
|
||||
|
||||
.product-gallery-main:hover {
|
||||
box-shadow: 0 0 0 1px hsl(240 5.9% 80%) inset;
|
||||
}
|
||||
|
||||
/* Clean price display */
|
||||
.price-display {
|
||||
font-feature-settings: 'tnum';
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
'use client';
|
||||
|
||||
import React from 'react';
|
||||
import './globals.css';
|
||||
import { Space_Grotesk, Inter, Poppins } from 'next/font/google';
|
||||
|
||||
const poppins = Poppins({
|
||||
subsets: ['latin'],
|
||||
weight: ['400', '500', '600', '700'],
|
||||
variable: '--font-poppins',
|
||||
});
|
||||
|
||||
const spaceGrotesk = Space_Grotesk({
|
||||
subsets: ['latin'],
|
||||
weight: ['400', '500', '600', '700'],
|
||||
variable: '--font-heading',
|
||||
});
|
||||
|
||||
const inter = Inter({
|
||||
subsets: ['latin'],
|
||||
weight: ['400', '500', '600'],
|
||||
variable: '--font-body',
|
||||
});
|
||||
|
||||
export default function RootLayout({
|
||||
children,
|
||||
}: {
|
||||
children: React.ReactNode;
|
||||
}) {
|
||||
return (
|
||||
<html
|
||||
lang="en"
|
||||
className={`${poppins.variable} ${spaceGrotesk.variable} ${inter.variable}`}
|
||||
>
|
||||
<body className="font-body antialiased bg-background text-foreground m-0 p-0">
|
||||
{children}
|
||||
</body>
|
||||
</html>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
"use client";
|
||||
|
||||
import React from 'react';
|
||||
import Link from 'next/link';
|
||||
|
||||
export default function NotFound() {
|
||||
return (
|
||||
<div className="flex items-center justify-center min-h-screen">
|
||||
<div className="flex items-center gap-6">
|
||||
<div className="text-lg font-medium text-black font-sans whitespace-nowrap">
|
||||
404
|
||||
</div>
|
||||
<div className="border-l border-gray-300 h-6"></div>
|
||||
<div className="text-sm text-gray-700 font-sans max-w-lg">
|
||||
This page could not be found.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
import Header from '@/components/shopify/header';
|
||||
import Footer from '@/components/shopify/footer';
|
||||
import Products from '@/components/shopify/products';
|
||||
|
||||
export default function Page() {
|
||||
return (
|
||||
<>
|
||||
<Header
|
||||
storeName="Stride"
|
||||
logoUrl=""
|
||||
links={[
|
||||
{ label: 'Products', url: '/' },
|
||||
{ label: 'Collections', url: '/collections' },
|
||||
]}
|
||||
/>
|
||||
<Products title="Shop All" />
|
||||
<Footer
|
||||
storeName="Stride"
|
||||
logoUrl=""
|
||||
tagline="Your premium shopping destination"
|
||||
copyright="© 2026 Stride. All rights reserved."
|
||||
links={[]}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
import Header from '@/components/shopify/header';
|
||||
import Footer from '@/components/shopify/footer';
|
||||
import ProductDetail from '@/components/shopify/product-detail';
|
||||
import ProductRecommendations from '@/components/shopify/product-recommendations';
|
||||
|
||||
export default function Page() {
|
||||
return (
|
||||
<>
|
||||
<Header
|
||||
storeName="Stride"
|
||||
logoUrl=""
|
||||
links={[
|
||||
{ label: 'Products', url: '/' },
|
||||
{ label: 'Collections', url: '/collections' },
|
||||
]}
|
||||
/>
|
||||
<ProductDetail
|
||||
addToCartLabel="Add to Cart"
|
||||
features={[
|
||||
{ icon: 'RiTruckLine', label: 'Free shipping on orders over $100' },
|
||||
{ icon: 'RiArrowGoBackLine', label: '30-day return policy' },
|
||||
{ icon: 'RiSecurePaymentLine', label: 'Secure payment' },
|
||||
]}
|
||||
/>
|
||||
<ProductRecommendations title="You Might Also Like" />
|
||||
<Footer
|
||||
storeName="Stride"
|
||||
logoUrl=""
|
||||
tagline="Your premium shopping destination"
|
||||
copyright="© 2026 Stride. All rights reserved."
|
||||
links={[]}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
import Header from '@/components/shopify/header';
|
||||
import Footer from '@/components/shopify/footer';
|
||||
import Collections from '@/components/shopify/collections';
|
||||
|
||||
export default function Page() {
|
||||
return (
|
||||
<>
|
||||
<Header
|
||||
storeName="Stride"
|
||||
logoUrl=""
|
||||
links={[
|
||||
{ label: 'Products', url: '/' },
|
||||
{ label: 'Collections', url: '/shop/collections' },
|
||||
]}
|
||||
/>
|
||||
<Collections title="Our Collections" />
|
||||
<Footer
|
||||
storeName="Stride"
|
||||
logoUrl=""
|
||||
tagline="Your premium shopping destination"
|
||||
copyright="© 2026 Stride. All rights reserved."
|
||||
links={[]}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user