Frontend Component Structure
This document outlines the component structure for the e-commerce frontend, inspired by turntupfashion.com.
Core Components
Layout Components
Layout.jsx
- Main layout wrapper with header and footerHeader.jsx
- Site header with navigation, search, and cartFooter.jsx
- Site footer with links and newsletter signupMobileMenu.jsx
- Responsive navigation for mobile devicesBreadcrumbs.jsx
- Navigation breadcrumbs component
Navigation Components
MainNav.jsx
- Primary navigation menuCategoryMenu.jsx
- Category dropdown navigationCountrySelector.jsx
- Country and currency selector componentPromoBanner.jsx
- Promotional announcement banner (e.g., "A24" discount code)
Product Components
ProductCard.jsx
- Card display for product in listingsProductGrid.jsx
- Grid layout for product listingsProductGallery.jsx
- Image gallery for product detail pageProductDetails.jsx
- Product information displayProductVariants.jsx
- Size/color selection componentAddToCartButton.jsx
- Button with quantity selector
Cart Components
CartIcon.jsx
- Cart icon with item count indicatorCartDrawer.jsx
- Slide-out cart drawerCartItem.jsx
- Individual item in cartCartSummary.jsx
- Order summary with subtotal and discounts
Checkout Components
CheckoutForm.jsx
- Multi-step checkout formShippingForm.jsx
- Shipping information formPaymentForm.jsx
- Payment information with Stripe integrationOrderSummary.jsx
- Order review component
User Account Components
LoginForm.jsx
- User login formRegisterForm.jsx
- New user registrationAccountDashboard.jsx
- User account overviewOrderHistory.jsx
- Past order display
UI Components
Button.jsx
- Styled button componentInput.jsx
- Form input fieldSelect.jsx
- Dropdown select componentModal.jsx
- Popup modal componentSpinner.jsx
- Loading spinnerToast.jsx
- Notification toast
Page Structure
Home Page (pages/index.js
)
<Layout>
<PromoBanner code="A24" message="Special discount on all items!" />
<HeroSection />
<FeaturedProducts />
<NewArrivals />
<CollectionShowcase />
<Newsletter />
</Layout>
Product Listing Page (pages/products/index.js
)
<Layout>
<Breadcrumbs />
<div className="grid grid-cols-12 gap-6">
<div className="col-span-3">
<FilterSidebar />
</div>
<div className="col-span-9">
<ProductSorting />
<ProductGrid products={products} />
<Pagination />
</div>
</div>
</Layout>
Product Detail Page (pages/products/[slug].js
)
<Layout>
<Breadcrumbs />
<div className="grid grid-cols-1 md:grid-cols-2 gap-8">
<ProductGallery images={product.images} />
<div>
<ProductDetails product={product} />
<ProductVariants variants={product.variants} />
<AddToCartButton product={product} />
<ProductTabs tabs={['Description', 'Details', 'Reviews']} />
</div>
</div>
<RelatedProducts products={relatedProducts} />
</Layout>
Cart Page (pages/cart.js
)
<Layout>
<Breadcrumbs />
<h1>Your Cart</h1>
{cartItems.length > 0 ? (
<div className="grid grid-cols-1 lg:grid-cols-3 gap-8">
<div className="lg:col-span-2">
{cartItems.map(item => (
<CartItem key={item.id} item={item} />
))}
</div>
<div>
<CartSummary />
<PromoCodeInput />
<CheckoutButton />
</div>
</div>
) : (
<EmptyCart />
)}
</Layout>
Checkout Page (pages/checkout.js
)
<Layout>
<Breadcrumbs />
<CheckoutSteps currentStep={currentStep} />
<div className="grid grid-cols-1 lg:grid-cols-3 gap-8">
<div className="lg:col-span-2">
{currentStep === 'information' && <ShippingForm />}
{currentStep === 'shipping' && <ShippingMethodForm />}
{currentStep === 'payment' && <PaymentForm />}
</div>
<div>
<OrderSummary />
</div>
</div>
</Layout>
Component Examples
Promo Banner Component
// components/PromoBanner.jsx
import { useState } from 'react';
export default function PromoBanner({ code, message }) {
const [isVisible, setIsVisible] = useState(true);
if (!isVisible) return null;
return (
<div className="bg-indigo-600 text-white text-center py-2 px-4 relative">
<span>{message} Use code <strong>{code}</strong> at checkout</span>
<button
onClick={() => setIsVisible(false)}
className="absolute right-4 top-1/2 transform -translate-y-1/2"
aria-label="Dismiss banner"
>
<svg className="h-4 w-4" viewBox="0 0 20 20" fill="currentColor">
<path fillRule="evenodd" d="M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z" clipRule="evenodd" />
</svg>
</button>
</div>
);
}
Product Card Component
// components/ProductCard.jsx
import Image from 'next/image';
import Link from 'next/link';
export default function ProductCard({ product }) {
return (
<div className="group">
<div className="relative overflow-hidden rounded-lg">
<Link href={`/products/${product.slug}`}>
<div className="aspect-w-1 aspect-h-1 w-full">
<Image
src={product.images[0].url}
alt={product.name}
layout="fill"
objectFit="cover"
className="group-hover:scale-105 transition-transform duration-300"
/>
</div>
{product.compareAtPrice && (
<span className="absolute top-2 left-2 bg-red-500 text-white px-2 py-1 text-xs font-medium rounded">
Sale
</span>
)}
</Link>
</div>
<div className="mt-4 flex justify-between">
<div>
<h3 className="text-sm font-medium text-gray-700">
<Link href={`/products/${product.slug}`}>
{product.name}
</Link>
</h3>
</div>
<div>
{product.compareAtPrice ? (
<div className="flex space-x-2">
<span className="text-sm font-medium text-red-600">
${product.price.toFixed(2)}
</span>
<span className="text-sm text-gray-500 line-through">
${product.compareAtPrice.toFixed(2)}
</span>
</div>
) : (
<span className="text-sm font-medium text-gray-900">
${product.price.toFixed(2)}
</span>
)}
</div>
</div>
</div>
);
}
Add to Cart Button
// components/AddToCartButton.jsx
import { useState } from 'react';
import { useCart } from '@/contexts/CartContext';
export default function AddToCartButton({ product, selectedVariant }) {
const [quantity, setQuantity] = useState(1);
const { addItem } = useCart();
const handleAddToCart = () => {
if (!selectedVariant) {
// Show error - need to select size/color
return;
}
addItem(product, quantity, selectedVariant);
// Show success toast
};
return (
<div className="mt-6">
<div className="flex items-center mb-4">
<button
onClick={() => setQuantity(Math.max(1, quantity - 1))}
className="w-10 h-10 border border-gray-300 flex items-center justify-center"
>
-
</button>
<input
type="number"
value={quantity}
onChange={(e) => setQuantity(Math.max(1, parseInt(e.target.value) || 1))}
className="w-16 h-10 border-t border-b border-gray-300 text-center"
/>
<button
onClick={() => setQuantity(quantity + 1)}
className="w-10 h-10 border border-gray-300 flex items-center justify-center"
>
+
</button>
</div>
<button
onClick={handleAddToCart}
className="w-full bg-black text-white py-3 px-6 hover:bg-gray-800 transition"
>
Add to Cart
</button>
</div>
);
}
CSS and Styling
The project uses Tailwind CSS for styling with some additional custom styles where needed. Key style considerations:
-
Color Scheme - Primary: Black (#000000) - Accent: Indigo (#4F46E5) - Backgrounds: White/light grays - Text: Dark gray for body, black for headings
-
Typography - Primary font: Inter (sans-serif) - Heading sizes: h1 (2rem), h2 (1.5rem), h3 (1.25rem) - Body text: 1rem (16px)
-
Spacing System - Based on Tailwind's default spacing scale - Consistent padding/margins for sections (py-16, px-4)
-
Responsive Breakpoints - Mobile first approach - Key breakpoints: sm (640px), md (768px), lg (1024px), xl (1280px)
Component State Management
The application uses React Context for global state management:
- CartContext - Manages shopping cart items and operations
- AuthContext - Handles user authentication state
- UIContext - Controls UI elements like modals and notifications
For complex state logic, we use the reducer pattern with the useReducer
hook.