Slide 1 of 4
New inShips in 3 days
Northwind hoodie
£38.00
Heavyweight organic cotton with a brushed lining. Part of the new-starter welcome kit, and available to every Northwind employee with their annual swag allowance.
- Unisex fit, sizes XS to 3XL
- Machine wash at 30 °C
- Made in Portugal
product-gallery.tsx
"use client";
import { useState, type ReactNode } from "react";
import { LuMaximize2, LuShirt, LuShoppingBag, LuTag, LuZoomIn } from "react-icons/lu";
import {
Badge,
Button,
Carousel,
CarouselControls,
CarouselSlide,
Image,
ImageTile,
Modal,
} from "@nexera-ui/react";
const money = new Intl.NumberFormat("en-GB", { style: "currency", currency: "GBP" });
/**
* Product shots. Swap `art` for real photos (`src`); the token-coloured stand-ins keep the sample offline and theme-aware.
*/
const PHOTOS = [
{
id: "front",
alt: "Navy Northwind hoodie, front view with the logo on the chest",
tone: "bg-chart-1",
icon: <LuShirt />,
},
{
id: "back",
alt: "Hoodie back view with “Built at Northwind” printed across the shoulders",
tone: "bg-chart-2",
icon: <LuShirt />,
},
{
id: "detail",
alt: "Close-up of the embroidered logo and the brushed cotton fabric",
tone: "bg-chart-5",
icon: <LuZoomIn />,
},
{
id: "label",
alt: "Care label: 80% organic cotton, 20% recycled polyester",
tone: "bg-chart-3",
icon: <LuTag />,
},
] as const;
function Art({ tone, icon }: { tone: string; icon: ReactNode }) {
return (
<div
aria-hidden="true"
className={`grid size-full place-items-center text-brand-on-primary ${tone} [&>svg]:size-1/3`}
>
{icon}
</div>
);
}
/** Product gallery: photos you can swipe, thumbnails to jump between them and a full-size view. */
export default function ProductGallery() {
const [index, setIndex] = useState(0);
const [zoomOpen, setZoomOpen] = useState(false);
const photo = PHOTOS[index] ?? PHOTOS[0];
const zoom = (i: number) => {
setIndex(i);
setZoomOpen(true);
};
return (
<div className="@container bg-page px-4 py-8 @3xl:px-8">
<div className="mx-auto grid w-full max-w-5xl gap-6 @3xl:grid-cols-2 @3xl:items-start">
<div className="grid min-w-0 gap-3">
<Carousel
aria-label="Northwind hoodie photos"
index={index}
onIndexChange={setIndex}
indicator="counter"
>
{PHOTOS.map((p) => (
<CarouselSlide key={p.id}>
<Image alt={p.alt} ratio="4-3" fallback={<Art tone={p.tone} icon={p.icon} />} />
</CarouselSlide>
))}
</Carousel>
<ul aria-label="Choose a photo" className="m-0 grid list-none grid-cols-4 gap-2 p-0">
{PHOTOS.map((p, i) => (
<li key={p.id}>
<ImageTile
alt={p.alt}
fallback={<Art tone={p.tone} icon={p.icon} />}
isSelected={i === index}
onChange={() => {
setIndex(i);
}}
actions={[
{
id: `${p.id}-zoom`,
label: "View full size",
icon: <LuZoomIn />,
onPress: () => {
zoom(i);
},
},
]}
/>
</li>
))}
</ul>
</div>
<div className="grid gap-4">
<div className="flex flex-wrap gap-2">
<Badge tone="brand" label="New in" />
<Badge tone="neutral" label="Ships in 3 days" dot={false} />
</div>
<div className="grid gap-1">
<h2 className="text-heading-h2 text-primary">Northwind hoodie</h2>
<p className="text-heading-h3 text-primary">{money.format(38)}</p>
</div>
<p className="text-body-default text-secondary">
Heavyweight organic cotton with a brushed lining. Part of the new-starter welcome kit,
and available to every Northwind employee with their annual swag allowance.
</p>
<ul className="grid list-disc gap-1 ps-5 text-body-small text-secondary">
<li>Unisex fit, sizes XS to 3XL</li>
<li>Machine wash at 30 °C</li>
<li>Made in Portugal</li>
</ul>
<div className="flex flex-wrap gap-2">
<Button size="lg" leadingIcon={<LuShoppingBag />}>
Add to order
</Button>
<Modal
title="Northwind hoodie"
subtitle={photo.alt}
size="lg"
isDismissable
isOpen={zoomOpen}
onOpenChange={setZoomOpen}
trigger={
<Button variant="secondary" size="lg" leadingIcon={<LuMaximize2 />}>
View full size
</Button>
}
footerNote={`Photo ${String(index + 1)} of ${String(PHOTOS.length)}`}
>
<div className="grid gap-3">
<Image
alt={photo.alt}
ratio="1-1"
fallback={<Art tone={photo.tone} icon={photo.icon} />}
/>
<CarouselControls
count={PHOTOS.length}
index={index}
onIndexChange={setIndex}
isLooping
className="justify-self-center"
/>
</div>
</Modal>
</div>
</div>
</div>
</div>
);
}
More commerce and payments blocks
- Pricing plansMonthly or yearly plans with a recommended one.
- CheckoutCard details, billing address and order total.
- Saved payment methodsChoose, add or remove a card.
- Order summaryItems, discounts and totals.
- PayslipEarnings and deductions with totals.
- Reviews and ratingsAverage score, breakdown and written reviews.
- Order trackingWhere an order is, step by step.