ecommerce / page-examples / category-pages
With Text Header And Image Product Grid
01-with-text-header-and-image-product-grid.jsx
'use client'
import { Fragment, useState } from 'react'
import {
Dialog,
DialogBackdrop,
DialogPanel,
Disclosure,
DisclosureButton,
DisclosurePanel,
Menu,
MenuButton,
MenuItem,
MenuItems,
Popover,
PopoverButton,
PopoverGroup,
PopoverPanel,
Tab,
TabGroup,
TabList,
TabPanel,
TabPanels,
} from '@headlessui/react'
import {
Bars3Icon,
MagnifyingGlassIcon,
QuestionMarkCircleIcon,
ShoppingBagIcon,
XMarkIcon,
} from '@heroicons/react/24/outline'
import { ChevronDownIcon } from '@heroicons/react/20/solid'
const currencies = ['CAD', 'USD', 'AUD', 'EUR', 'GBP']
const navigation = {
categories: [
{
name: 'Women',
featured: [
{
name: 'New Arrivals',
href: '#',
imageSrc: 'https://tailwindcss.com/plus-assets/img/ecommerce-images/mega-menu-category-01.jpg',
imageAlt: 'Models sitting back to back, wearing Basic Tee in black and bone.',
},
{
name: 'Basic Tees',
href: '#',
imageSrc: 'https://tailwindcss.com/plus-assets/img/ecommerce-images/mega-menu-category-02.jpg',
imageAlt: 'Close up of Basic Tee fall bundle with off-white, ochre, olive, and black tees.',
},
{
name: 'Accessories',
href: '#',
imageSrc: 'https://tailwindcss.com/plus-assets/img/ecommerce-images/mega-menu-category-03.jpg',
imageAlt: 'Model wearing minimalist watch with black wristband and white watch face.',
},
{
name: 'Carry',
href: '#',
imageSrc: 'https://tailwindcss.com/plus-assets/img/ecommerce-images/mega-menu-category-04.jpg',
imageAlt: 'Model opening tan leather long wallet with credit card pockets and cash pouch.',
},
],
},
{
name: 'Men',
featured: [
{
name: 'New Arrivals',
href: '#',
imageSrc: 'https://tailwindcss.com/plus-assets/img/ecommerce-images/mega-menu-01-men-category-01.jpg',
imageAlt: 'Hats and sweaters on wood shelves next to various colors of t-shirts on hangers.',
},
{
name: 'Basic Tees',
href: '#',
imageSrc: 'https://tailwindcss.com/plus-assets/img/ecommerce-images/mega-menu-01-men-category-02.jpg',
imageAlt: 'Model wearing light heather gray t-shirt.',
},
{
name: 'Accessories',
href: '#',
imageSrc: 'https://tailwindcss.com/plus-assets/img/ecommerce-images/mega-menu-01-men-category-03.jpg',
imageAlt:
'Grey 6-panel baseball hat with black brim, black mountain graphic on front, and light heather gray body.',
},
{
name: 'Carry',
href: '#',
imageSrc: 'https://tailwindcss.com/plus-assets/img/ecommerce-images/mega-menu-01-men-category-04.jpg',
imageAlt: 'Model putting folded cash into slim card holder olive leather wallet with hand stitching.',
},
],
},
],
pages: [
{ name: 'Company', href: '#' },
{ name: 'Stores', href: '#' },
],
}
const sortOptions = [
{ name: 'Most Popular', href: '#' },
{ name: 'Best Rating', href: '#' },
{ name: 'Newest', href: '#' },
{ name: 'Price: Low to High', href: '#' },
{ name: 'Price: High to Low', href: '#' },
]
const filters = [
{
id: 'category',
name: 'Category',
options: [
{ value: 'tees', label: 'Tees' },
{ value: 'crewnecks', label: 'Crewnecks' },
{ value: 'hats', label: 'Hats' },
{ value: 'bundles', label: 'Bundles' },
{ value: 'carry', label: 'Carry' },
{ value: 'objects', label: 'Objects' },
],
},
{
id: 'brand',
name: 'Brand',
options: [
{ value: 'clothing-company', label: 'Clothing Company' },
{ value: 'fashion-inc', label: 'Fashion Inc.' },
{ value: 'shoes-n-more', label: "Shoes 'n More" },
{ value: 'supplies-n-stuff', label: "Supplies 'n Stuff" },
],
},
{
id: 'color',
name: 'Color',
options: [
{ value: 'white', label: 'White' },
{ value: 'black', label: 'Black' },
{ value: 'grey', label: 'Grey' },
{ value: 'blue', label: 'Blue' },
{ value: 'olive', label: 'Olive' },
{ value: 'tan', label: 'Tan' },
],
},
{
id: 'sizes',
name: 'Sizes',
options: [
{ value: 'xs', label: 'XS' },
{ value: 's', label: 'S' },
{ value: 'm', label: 'M' },
{ value: 'l', label: 'L' },
{ value: 'xl', label: 'XL' },
{ value: '2xl', label: '2XL' },
],
},
]
const products1 = [
{
id: 1,
name: 'Focus Paper Refill',
href: '#',
price: '$13',
description: '3 sizes available',
imageSrc: 'https://tailwindcss.com/plus-assets/img/ecommerce-images/category-page-01-image-card-01.jpg',
imageAlt: 'Person using a pen to cross a task off a productivity paper card.',
},
{
id: 2,
name: 'Focus Card Holder',
href: '#',
price: '$64',
description: 'Walnut',
imageSrc: 'https://tailwindcss.com/plus-assets/img/ecommerce-images/category-page-01-image-card-02.jpg',
imageAlt: 'Paper card sitting upright in walnut card holder on desk.',
},
{
id: 3,
name: 'Focus Carry Pouch',
href: '#',
price: '$32',
description: 'Heather Gray',
imageSrc: 'https://tailwindcss.com/plus-assets/img/ecommerce-images/category-page-01-image-card-03.jpg',
imageAlt: 'Textured gray felt pouch for paper cards with snap button flap and elastic pen holder loop.',
},
{
id: 4,
name: 'Focus Multi-Pack',
href: '#',
price: '$39',
description: '3 refill packs',
imageSrc: 'https://tailwindcss.com/plus-assets/img/ecommerce-images/category-page-01-image-card-04.jpg',
imageAlt: 'Stack of 3 small drab green cardboard paper card refill boxes with white text.',
},
{
id: 5,
name: 'Machined Mechanical Pencil',
href: '#',
price: '$35',
description: 'Black and brass',
imageSrc: 'https://tailwindcss.com/plus-assets/img/ecommerce-images/category-page-01-image-card-05.jpg',
imageAlt: 'Hand holding black machined steel mechanical pencil with brass tip and top.',
},
{
id: 6,
name: 'Brass Scissors',
href: '#',
price: '$50',
description: 'Includes brass stand',
imageSrc: 'https://tailwindcss.com/plus-assets/img/ecommerce-images/category-page-01-image-card-06.jpg',
imageAlt: 'Brass scissors with geometric design, black steel finger holes, and included upright brass stand.',
},
]
const products2 = [
{
id: 7,
name: 'Electric Kettle',
href: '#',
price: '$149',
description: 'Black',
imageSrc: 'https://tailwindcss.com/plus-assets/img/ecommerce-images/category-page-01-image-card-07.jpg',
imageAlt: 'Close up of long kettle spout pouring boiling water into pour-over coffee mug with frothy coffee.',
},
{
id: 8,
name: 'Leather Workspace Pad',
href: '#',
price: '$165',
description: 'Black',
imageSrc: 'https://tailwindcss.com/plus-assets/img/ecommerce-images/category-page-01-image-card-08.jpg',
imageAlt:
'Extra large black leather workspace pad on desk with computer, wooden shelf, desk organizer, and computer peripherals.',
},
{
id: 9,
name: 'Leather Long Wallet',
href: '#',
price: '$118',
description: 'Natural',
imageSrc: 'https://tailwindcss.com/plus-assets/img/ecommerce-images/category-page-01-image-card-09.jpg',
imageAlt:
'Leather long wallet held open with hand-stitched card dividers, full-length bill pocket, and simple tab closure.',
},
{
id: 10,
name: 'Machined Sphere Puzzle',
href: '#',
price: '$95',
description: 'Includes brass stand',
imageSrc: 'https://tailwindcss.com/plus-assets/img/ecommerce-images/category-page-01-image-card-10.jpg',
imageAlt:
'Machined steel sphere puzzle with smooth finish, geometric seams, and included brass stand on wood desk.',
},
{
id: 11,
name: 'Minimalist Wristwatch',
href: '#',
price: '$149',
description: '3 wrist band options',
imageSrc: 'https://tailwindcss.com/plus-assets/img/ecommerce-images/category-page-01-image-card-11.jpg',
imageAlt:
'Arm modeling wristwatch with black leather band, white watch face, thin watch hands, and fine time markings.',
},
{
id: 12,
name: 'Motto Leather Coaster Set',
href: '#',
price: '$18',
description: 'Natural',
imageSrc: 'https://tailwindcss.com/plus-assets/img/ecommerce-images/category-page-01-image-card-12.jpg',
imageAlt: 'Circular leather coaster set with natural color and "Enjoy the Journey" embossed type.',
},
]
const footerNavigation = {
products: [
{ name: 'Bags', href: '#' },
{ name: 'Tees', href: '#' },
{ name: 'Objects', href: '#' },
{ name: 'Home Goods', href: '#' },
{ name: 'Accessories', href: '#' },
],
company: [
{ name: 'Who we are', href: '#' },
{ name: 'Sustainability', href: '#' },
{ name: 'Press', href: '#' },
{ name: 'Careers', href: '#' },
{ name: 'Terms & Conditions', href: '#' },
{ name: 'Privacy', href: '#' },
],
customerService: [
{ name: 'Contact', href: '#' },
{ name: 'Shipping', href: '#' },
{ name: 'Returns', href: '#' },
{ name: 'Warranty', href: '#' },
{ name: 'Secure Payments', href: '#' },
{ name: 'FAQ', href: '#' },
{ name: 'Find a store', href: '#' },
],
}
export default function Example() {
const [mobileMenuOpen, setMobileMenuOpen] = useState(false)
const [mobileFiltersOpen, setMobileFiltersOpen] = useState(false)
return (
<div className="bg-gray-50">
<div>
{/* Mobile menu */}
<Dialog open={mobileMenuOpen} onClose={setMobileMenuOpen} className="relative z-40 lg:hidden">
<DialogBackdrop
transition
className="fixed inset-0 bg-black/25 transition-opacity duration-300 ease-linear data-closed:opacity-0"
/>
<div className="fixed inset-0 z-40 flex">
<DialogPanel
transition
className="relative flex w-full max-w-xs transform flex-col overflow-y-auto bg-white pb-12 shadow-xl transition duration-300 ease-in-out data-closed:-translate-x-full"
>
<div className="flex px-4 pt-5 pb-2">
<button
type="button"
onClick={() => setMobileMenuOpen(false)}
className="relative -m-2 inline-flex items-center justify-center rounded-md p-2 text-gray-400"
>
<span className="absolute -inset-0.5" />
<span className="sr-only">Close menu</span>
<XMarkIcon aria-hidden="true" className="size-6" />
</button>
</div>
{/* Links */}
<TabGroup className="mt-2">
<div className="border-b border-gray-200">
<TabList className="-mb-px flex space-x-8 px-4">
{navigation.categories.map((category) => (
<Tab
key={category.name}
className="flex-1 border-b-2 border-transparent px-1 py-4 text-base font-medium whitespace-nowrap text-gray-900 data-selected:border-indigo-600 data-selected:text-indigo-600"
>
{category.name}
</Tab>
))}
</TabList>
</div>
<TabPanels as={Fragment}>
{navigation.categories.map((category) => (
<TabPanel key={category.name} className="space-y-12 px-4 py-6">
<div className="grid grid-cols-2 gap-x-4 gap-y-10">
{category.featured.map((item) => (
<div key={item.name} className="group relative">
<img
alt={item.imageAlt}
src={item.imageSrc}
className="aspect-square w-full rounded-md bg-gray-100 object-cover group-hover:opacity-75"
/>
<a href={item.href} className="mt-6 block text-sm font-medium text-gray-900">
<span aria-hidden="true" className="absolute inset-0 z-10" />
{item.name}
</a>
<p aria-hidden="true" className="mt-1 text-sm text-gray-500">
Shop now
</p>
</div>
))}
</div>
</TabPanel>
))}
</TabPanels>
</TabGroup>
<div className="space-y-6 border-t border-gray-200 px-4 py-6">
{navigation.pages.map((page) => (
<div key={page.name} className="flow-root">
<a href={page.href} className="-m-2 block p-2 font-medium text-gray-900">
{page.name}
</a>
</div>
))}
</div>
<div className="space-y-6 border-t border-gray-200 px-4 py-6">
<div className="flow-root">
<a href="#" className="-m-2 block p-2 font-medium text-gray-900">
Create an account
</a>
</div>
<div className="flow-root">
<a href="#" className="-m-2 block p-2 font-medium text-gray-900">
Sign in
</a>
</div>
</div>
<div className="space-y-6 border-t border-gray-200 px-4 py-6">
{/* Currency selector */}
<form>
<div className="-ml-2 inline-grid grid-cols-1">
<select
id="mobile-currency"
name="currency"
aria-label="Currency"
className="col-start-1 row-start-1 w-full appearance-none rounded-md bg-white py-0.5 pr-7 pl-2 text-base font-medium text-gray-700 group-hover:text-gray-800 focus:outline-2 sm:text-sm/6"
>
{currencies.map((currency) => (
<option key={currency}>{currency}</option>
))}
</select>
<ChevronDownIcon
aria-hidden="true"
className="pointer-events-none col-start-1 row-start-1 mr-1 size-5 self-center justify-self-end fill-gray-500"
/>
</div>
</form>
</div>
</DialogPanel>
</div>
</Dialog>
<header className="relative">
<nav aria-label="Top">
{/* Top navigation */}
<div className="bg-gray-900">
<div className="mx-auto flex h-10 max-w-7xl items-center justify-between px-4 sm:px-6 lg:px-8">
{/* Currency selector */}
<form>
<div className="-ml-2 inline-grid grid-cols-1">
<select
id="desktop-currency"
name="currency"
aria-label="Currency"
className="col-start-1 row-start-1 w-full appearance-none rounded-md bg-gray-900 py-0.5 pr-7 pl-2 text-left text-base font-medium text-white focus:outline-2 focus:-outline-offset-1 focus:outline-white sm:text-sm/6"
>
{currencies.map((currency) => (
<option key={currency}>{currency}</option>
))}
</select>
<ChevronDownIcon
aria-hidden="true"
className="pointer-events-none col-start-1 row-start-1 mr-1 size-5 self-center justify-self-end fill-gray-300"
/>
</div>
</form>
<div className="flex items-center space-x-6">
<a href="#" className="text-sm font-medium text-white hover:text-gray-100">
Sign in
</a>
<a href="#" className="text-sm font-medium text-white hover:text-gray-100">
Create an account
</a>
</div>
</div>
</div>
{/* Secondary navigation */}
<div className="bg-white shadow-xs">
<div className="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8">
<div className="flex h-16 items-center justify-between">
{/* Logo (lg+) */}
<div className="hidden lg:flex lg:flex-1 lg:items-center">
<a href="#">
<span className="sr-only">Your Company</span>
<img
alt=""
src="https://tailwindcss.com/plus-assets/img/logos/mark.svg?color=indigo&shade=600"
className="h-8 w-auto"
/>
</a>
</div>
<div className="hidden h-full lg:flex">
{/* Flyout menus */}
<PopoverGroup className="inset-x-0 bottom-0 px-4">
<div className="flex h-full justify-center space-x-8">
{navigation.categories.map((category) => (
<Popover key={category.name} className="flex">
<div className="relative flex">
<PopoverButton className="group relative flex items-center justify-center text-sm font-medium text-gray-700 transition-colors duration-200 ease-out hover:text-gray-800 data-open:text-indigo-600">
{category.name}
<span
aria-hidden="true"
className="absolute inset-x-0 -bottom-px z-30 h-0.5 transition duration-200 ease-out group-data-open:bg-indigo-600"
/>
</PopoverButton>
</div>
<PopoverPanel
transition
className="group/popover-panel absolute inset-x-0 top-full z-20 w-full bg-white text-sm text-gray-500 transition data-closed:opacity-0 data-enter:duration-200 data-enter:ease-out data-leave:duration-150 data-leave:ease-in"
>
{/* Presentational element used to render the bottom shadow, if we put the shadow on the actual panel it pokes out the top, so we use this shorter element to hide the top of the shadow */}
<div aria-hidden="true" className="absolute inset-0 top-1/2 bg-white shadow-sm" />
<div className="relative bg-white">
<div className="mx-auto max-w-7xl px-8">
<div className="grid grid-cols-4 gap-x-8 gap-y-10 py-16">
{category.featured.map((item) => (
<div key={item.name} className="group relative">
<img
alt={item.imageAlt}
src={item.imageSrc}
className="aspect-square w-full rounded-md bg-gray-100 object-cover group-hover:opacity-75"
/>
<a href={item.href} className="mt-4 block font-medium text-gray-900">
<span aria-hidden="true" className="absolute inset-0 z-10" />
{item.name}
</a>
<p aria-hidden="true" className="mt-1">
Shop now
</p>
</div>
))}
</div>
</div>
</div>
{/* Presentational element to emulate a border that sits on top of the popover */}
<div
aria-hidden="true"
className="absolute inset-0 top-0 z-10 mx-auto h-px max-w-7xl px-8"
>
<div className="h-px w-full bg-transparent transition-colors duration-200 ease-out group-data-open/popover-panel:bg-gray-200" />
</div>
</PopoverPanel>
</Popover>
))}
{navigation.pages.map((page) => (
<a
key={page.name}
href={page.href}
className="flex items-center text-sm font-medium text-gray-700 hover:text-gray-800"
>
{page.name}
</a>
))}
</div>
</PopoverGroup>
</div>
{/* Mobile menu and search (lg-) */}
<div className="flex flex-1 items-center lg:hidden">
<button
type="button"
onClick={() => setMobileMenuOpen(true)}
className="-ml-2 rounded-md bg-white p-2 text-gray-400"
>
<span className="sr-only">Open menu</span>
<Bars3Icon aria-hidden="true" className="size-6" />
</button>
{/* Search */}
<a href="#" className="ml-2 p-2 text-gray-400 hover:text-gray-500">
<span className="sr-only">Search</span>
<MagnifyingGlassIcon aria-hidden="true" className="size-6" />
</a>
</div>
{/* Logo (lg-) */}
<a href="#" className="lg:hidden">
<span className="sr-only">Your Company</span>
<img
alt=""
src="https://tailwindcss.com/plus-assets/img/logos/mark.svg?color=indigo&shade=600"
className="h-8 w-auto"
/>
</a>
<div className="flex flex-1 items-center justify-end">
<a href="#" className="hidden text-sm font-medium text-gray-700 hover:text-gray-800 lg:block">
Search
</a>
<div className="flex items-center lg:ml-8">
{/* Help */}
<a href="#" className="p-2 text-gray-400 hover:text-gray-500 lg:hidden">
<span className="sr-only">Help</span>
<QuestionMarkCircleIcon aria-hidden="true" className="size-6" />
</a>
<a href="#" className="hidden text-sm font-medium text-gray-700 hover:text-gray-800 lg:block">
Help
</a>
{/* Cart */}
<div className="ml-4 flow-root lg:ml-8">
<a href="#" className="group -m-2 flex items-center p-2">
<ShoppingBagIcon
aria-hidden="true"
className="size-6 shrink-0 text-gray-400 group-hover:text-gray-500"
/>
<span className="ml-2 text-sm font-medium text-gray-700 group-hover:text-gray-800">0</span>
<span className="sr-only">items in cart, view bag</span>
</a>
</div>
</div>
</div>
</div>
</div>
</div>
</nav>
</header>
</div>
<div>
{/* Mobile filter dialog */}
<Dialog open={mobileFiltersOpen} onClose={setMobileFiltersOpen} className="relative z-40 sm:hidden">
<DialogBackdrop
transition
className="fixed inset-0 bg-black/25 transition-opacity duration-300 ease-linear data-closed:opacity-0"
/>
<div className="fixed inset-0 z-40 flex">
<DialogPanel
transition
className="relative ml-auto flex size-full max-w-xs transform flex-col overflow-y-auto bg-white pt-4 pb-6 shadow-xl transition duration-300 ease-in-out data-closed:translate-x-full"
>
<div className="flex items-center justify-between px-4">
<h2 className="text-lg font-medium text-gray-900">Filters</h2>
<button
type="button"
onClick={() => setMobileFiltersOpen(false)}
className="relative -mr-2 flex size-10 items-center justify-center rounded-md bg-white p-2 text-gray-400 hover:bg-gray-50 focus:ring-2 focus:ring-indigo-500 focus:outline-hidden"
>
<span className="absolute -inset-0.5" />
<span className="sr-only">Close menu</span>
<XMarkIcon aria-hidden="true" className="size-6" />
</button>
</div>
{/* Filters */}
<form className="mt-4">
{filters.map((section) => (
<Disclosure key={section.name} as="div" className="border-t border-gray-200 px-4 py-6">
<h3 className="-mx-2 -my-3 flow-root">
<DisclosureButton className="group flex w-full items-center justify-between bg-white px-2 py-3 text-sm text-gray-400">
<span className="font-medium text-gray-900">{section.name}</span>
<span className="ml-6 flex items-center">
<ChevronDownIcon
aria-hidden="true"
className="size-5 rotate-0 transform group-data-open:-rotate-180"
/>
</span>
</DisclosureButton>
</h3>
<DisclosurePanel className="pt-6">
<div className="space-y-6">
{section.options.map((option, optionIdx) => (
<div key={option.value} className="flex gap-3">
<div className="flex h-5 shrink-0 items-center">
<div className="group grid size-4 grid-cols-1">
<input
defaultValue={option.value}
defaultChecked={option.checked}
id={`filter-mobile-${section.id}-${optionIdx}`}
name={`${section.id}[]`}
type="checkbox"
className="col-start-1 row-start-1 appearance-none rounded-sm border border-gray-300 bg-white checked:border-indigo-600 checked:bg-indigo-600 indeterminate:border-indigo-600 indeterminate:bg-indigo-600 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600 disabled:border-gray-300 disabled:bg-gray-100 disabled:checked:bg-gray-100 forced-colors:appearance-auto"
/>
<svg
fill="none"
viewBox="0 0 14 14"
className="pointer-events-none col-start-1 row-start-1 size-3.5 self-center justify-self-center stroke-white group-has-disabled:stroke-gray-950/25"
>
<path
d="M3 8L6 11L11 3.5"
strokeWidth={2}
strokeLinecap="round"
strokeLinejoin="round"
className="opacity-0 group-has-checked:opacity-100"
/>
<path
d="M3 7H11"
strokeWidth={2}
strokeLinecap="round"
strokeLinejoin="round"
className="opacity-0 group-has-indeterminate:opacity-100"
/>
</svg>
</div>
</div>
<label
htmlFor={`filter-mobile-${section.id}-${optionIdx}`}
className="ml-3 text-sm text-gray-500"
>
{option.label}
</label>
</div>
))}
</div>
</DisclosurePanel>
</Disclosure>
))}
</form>
</DialogPanel>
</div>
</Dialog>
<main>
<div className="mx-auto max-w-3xl px-4 sm:px-6 lg:max-w-7xl lg:px-8">
<div className="py-24 text-center">
<h1 className="text-4xl font-bold tracking-tight text-gray-900">New Arrivals</h1>
<p className="mx-auto mt-4 max-w-3xl text-base text-gray-500">
Thoughtfully designed objects for the workspace, home, and travel.
</p>
</div>
{/* Filters */}
<section aria-labelledby="filter-heading" className="border-t border-gray-200 pt-6">
<h2 id="filter-heading" className="sr-only">
Product filters
</h2>
<div className="flex items-center justify-between">
<Menu as="div" className="relative inline-block text-left">
<MenuButton className="group inline-flex justify-center text-sm font-medium text-gray-700 hover:text-gray-900">
Sort
<ChevronDownIcon
aria-hidden="true"
className="-mr-1 ml-1 size-5 shrink-0 text-gray-400 group-hover:text-gray-500"
/>
</MenuButton>
<MenuItems
transition
className="absolute left-0 z-10 mt-2 w-40 origin-top-left rounded-md bg-white shadow-2xl ring-1 ring-black/5 transition focus:outline-hidden data-closed:scale-95 data-closed:transform data-closed:opacity-0 data-enter:duration-100 data-enter:ease-out data-leave:duration-75 data-leave:ease-in"
>
<div className="py-1">
{sortOptions.map((option) => (
<MenuItem key={option}>
<a
href={option.href}
className="block px-4 py-2 text-sm font-medium text-gray-900 data-focus:bg-gray-100 data-focus:outline-hidden"
>
{option.name}
</a>
</MenuItem>
))}
</div>
</MenuItems>
</Menu>
<button
type="button"
onClick={() => setMobileFiltersOpen(true)}
className="inline-block text-sm font-medium text-gray-700 hover:text-gray-900 sm:hidden"
>
Filters
</button>
<PopoverGroup className="hidden sm:flex sm:items-baseline sm:space-x-8">
{filters.map((section, sectionIdx) => (
<Popover key={section.name} className="relative inline-block text-left">
<div>
<PopoverButton className="group inline-flex items-center justify-center text-sm font-medium text-gray-700 hover:text-gray-900">
<span>{section.name}</span>
{sectionIdx === 0 ? (
<span className="ml-1.5 rounded-sm bg-gray-200 px-1.5 py-0.5 text-xs font-semibold text-gray-700 tabular-nums">
1
</span>
) : null}
<ChevronDownIcon
aria-hidden="true"
className="-mr-1 ml-1 size-5 shrink-0 text-gray-400 group-hover:text-gray-500"
/>
</PopoverButton>
</div>
<PopoverPanel
transition
className="absolute right-0 z-10 mt-2 origin-top-right rounded-md bg-white p-4 shadow-2xl ring-1 ring-black/5 transition focus:outline-hidden data-closed:scale-95 data-closed:transform data-closed:opacity-0 data-enter:duration-100 data-enter:ease-out data-leave:duration-75 data-leave:ease-in"
>
<form className="space-y-4">
{section.options.map((option, optionIdx) => (
<div key={option.value} className="flex gap-3">
<div className="flex h-5 shrink-0 items-center">
<div className="group grid size-4 grid-cols-1">
<input
defaultValue={option.value}
defaultChecked={option.checked}
id={`filter-${section.id}-${optionIdx}`}
name={`${section.id}[]`}
type="checkbox"
className="col-start-1 row-start-1 appearance-none rounded-sm border border-gray-300 bg-white checked:border-indigo-600 checked:bg-indigo-600 indeterminate:border-indigo-600 indeterminate:bg-indigo-600 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600 disabled:border-gray-300 disabled:bg-gray-100 disabled:checked:bg-gray-100 forced-colors:appearance-auto"
/>
<svg
fill="none"
viewBox="0 0 14 14"
className="pointer-events-none col-start-1 row-start-1 size-3.5 self-center justify-self-center stroke-white group-has-disabled:stroke-gray-950/25"
>
<path
d="M3 8L6 11L11 3.5"
strokeWidth={2}
strokeLinecap="round"
strokeLinejoin="round"
className="opacity-0 group-has-checked:opacity-100"
/>
<path
d="M3 7H11"
strokeWidth={2}
strokeLinecap="round"
strokeLinejoin="round"
className="opacity-0 group-has-indeterminate:opacity-100"
/>
</svg>
</div>
</div>
<label
htmlFor={`filter-${section.id}-${optionIdx}`}
className="pr-6 text-sm font-medium whitespace-nowrap text-gray-900"
>
{option.label}
</label>
</div>
))}
</form>
</PopoverPanel>
</Popover>
))}
</PopoverGroup>
</div>
</section>
{/* Product grid */}
<section aria-labelledby="products-heading" className="mt-8">
<h2 id="products-heading" className="sr-only">
Products
</h2>
<div className="grid grid-cols-1 gap-x-6 gap-y-10 sm:grid-cols-2 lg:grid-cols-3 xl:gap-x-8">
{products1.map((product) => (
<a key={product.id} href={product.href} className="group">
<img
alt={product.imageAlt}
src={product.imageSrc}
className="aspect-square w-full rounded-lg object-cover group-hover:opacity-75 sm:aspect-2/3"
/>
<div className="mt-4 flex items-center justify-between text-base font-medium text-gray-900">
<h3>{product.name}</h3>
<p>{product.price}</p>
</div>
<p className="mt-1 text-sm text-gray-500 italic">{product.description}</p>
</a>
))}
</div>
</section>
<section aria-labelledby="featured-heading" className="relative mt-16 overflow-hidden rounded-lg lg:h-96">
<div className="absolute inset-0">
<img
alt=""
src="https://tailwindcss.com/plus-assets/img/ecommerce-images/category-page-01-featured-collection.jpg"
className="size-full object-cover"
/>
</div>
<div aria-hidden="true" className="relative h-96 w-full lg:hidden" />
<div aria-hidden="true" className="relative h-32 w-full lg:hidden" />
<div className="absolute inset-x-0 bottom-0 rounded-br-lg rounded-bl-lg bg-black/75 p-6 backdrop-blur-sm backdrop-filter sm:flex sm:items-center sm:justify-between lg:inset-x-auto lg:inset-y-0 lg:w-96 lg:flex-col lg:items-start lg:rounded-tl-lg lg:rounded-br-none">
<div>
<h2 id="featured-heading" className="text-xl font-bold text-white">
Workspace Collection
</h2>
<p className="mt-1 text-sm text-gray-300">
Upgrade your desk with objects that keep you organized and clear-minded.
</p>
</div>
<a
href="#"
className="mt-6 flex shrink-0 items-center justify-center rounded-md border border-white/25 px-4 py-3 text-base font-medium text-white hover:bg-white/10 sm:mt-0 sm:ml-8 lg:ml-0 lg:w-full"
>
View the collection
</a>
</div>
</section>
<section aria-labelledby="more-products-heading" className="mt-16 pb-24">
<h2 id="more-products-heading" className="sr-only">
More products
</h2>
<div className="grid grid-cols-1 gap-x-6 gap-y-10 sm:grid-cols-2 lg:grid-cols-3 xl:gap-x-8">
{products2.map((product) => (
<a key={product.id} href={product.href} className="group">
<img
alt={product.imageAlt}
src={product.imageSrc}
className="aspect-square w-full rounded-lg object-cover group-hover:opacity-75 sm:aspect-2/3"
/>
<div className="mt-4 flex items-center justify-between text-base font-medium text-gray-900">
<h3>{product.name}</h3>
<p>{product.price}</p>
</div>
<p className="mt-1 text-sm text-gray-500 italic">{product.description}</p>
</a>
))}
</div>
</section>
</div>
</main>
<footer aria-labelledby="footer-heading" className="border-t border-gray-200 bg-white">
<h2 id="footer-heading" className="sr-only">
Footer
</h2>
<div className="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8">
<div className="py-20">
<div className="grid grid-cols-1 md:grid-flow-col md:auto-rows-min md:grid-cols-12 md:gap-x-8 md:gap-y-16">
{/* Image section */}
<div className="col-span-1 md:col-span-2 lg:col-start-1 lg:row-start-1">
<img
alt=""
src="https://tailwindcss.com/plus-assets/img/logos/mark.svg?color=indigo&shade=600"
className="h-8 w-auto"
/>
</div>
{/* Sitemap sections */}
<div className="col-span-6 mt-10 grid grid-cols-2 gap-8 sm:grid-cols-3 md:col-span-8 md:col-start-3 md:row-start-1 md:mt-0 lg:col-span-6 lg:col-start-2">
<div className="grid grid-cols-1 gap-y-12 sm:col-span-2 sm:grid-cols-2 sm:gap-x-8">
<div>
<h3 className="text-sm font-medium text-gray-900">Products</h3>
<ul role="list" className="mt-6 space-y-6">
{footerNavigation.products.map((item) => (
<li key={item.name} className="text-sm">
<a href={item.href} className="text-gray-500 hover:text-gray-600">
{item.name}
</a>
</li>
))}
</ul>
</div>
<div>
<h3 className="text-sm font-medium text-gray-900">Company</h3>
<ul role="list" className="mt-6 space-y-6">
{footerNavigation.company.map((item) => (
<li key={item.name} className="text-sm">
<a href={item.href} className="text-gray-500 hover:text-gray-600">
{item.name}
</a>
</li>
))}
</ul>
</div>
</div>
<div>
<h3 className="text-sm font-medium text-gray-900">Customer Service</h3>
<ul role="list" className="mt-6 space-y-6">
{footerNavigation.customerService.map((item) => (
<li key={item.name} className="text-sm">
<a href={item.href} className="text-gray-500 hover:text-gray-600">
{item.name}
</a>
</li>
))}
</ul>
</div>
</div>
{/* Newsletter section */}
<div className="mt-12 md:col-span-8 md:col-start-3 md:row-start-2 md:mt-0 lg:col-span-4 lg:col-start-9 lg:row-start-1">
<h3 className="text-sm font-medium text-gray-900">Sign up for our newsletter</h3>
<p className="mt-6 text-sm text-gray-500">The latest deals and savings, sent to your inbox weekly.</p>
<form className="mt-2 flex sm:max-w-md">
<input
id="email-address"
type="text"
required
autoComplete="email"
aria-label="Email address"
className="block w-full rounded-md bg-white px-4 py-2 text-base text-gray-900 outline-1 -outline-offset-1 outline-gray-300 placeholder:text-gray-400 focus:outline-2 focus:-outline-offset-2 focus:outline-indigo-600"
/>
<div className="ml-4 shrink-0">
<button
type="submit"
className="flex w-full items-center justify-center rounded-md border border-transparent bg-indigo-600 px-4 py-2 text-base font-medium text-white shadow-xs hover:bg-indigo-700 focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2 focus:outline-hidden"
>
Sign up
</button>
</div>
</form>
</div>
</div>
</div>
<div className="border-t border-gray-100 py-10 text-center">
<p className="text-sm text-gray-500">© 2021 Your Company, Inc. All rights reserved.</p>
</div>
</div>
</footer>
</div>
</div>
)
}