ecommerce / components / store-navigation
With Navigation And Tiered Featured Categories
04-with-navigation-and-tiered-featured-categories.jsx
'use client'
import { Fragment, useState } from 'react'
import {
Dialog,
DialogBackdrop,
DialogPanel,
Popover,
PopoverButton,
PopoverGroup,
PopoverPanel,
Tab,
TabGroup,
TabList,
TabPanel,
TabPanels,
} from '@headlessui/react'
import { Bars3Icon, MagnifyingGlassIcon, ShoppingBagIcon, UserIcon, XMarkIcon } from '@heroicons/react/24/outline'
const navigation = {
categories: [
{
id: 'women',
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.',
},
],
sections: [
[
{
id: 'shoes',
name: 'Shoes & Accessories',
items: [
{ name: 'Sneakers', href: '#' },
{ name: 'Boots', href: '#' },
{ name: 'Flats', href: '#' },
{ name: 'Sandals', href: '#' },
{ name: 'Heels', href: '#' },
{ name: 'Socks', href: '#' },
],
},
{
id: 'collection',
name: 'Shop Collection',
items: [
{ name: 'Everything', href: '#' },
{ name: 'Core', href: '#' },
{ name: 'New Arrivals', href: '#' },
{ name: 'Sale', href: '#' },
{ name: 'Accessories', href: '#' },
],
},
],
[
{
id: 'clothing',
name: 'All Clothing',
items: [
{ name: 'Basic Tees', href: '#' },
{ name: 'Artwork Tees', href: '#' },
{ name: 'Tops', href: '#' },
{ name: 'Bottoms', href: '#' },
{ name: 'Swimwear', href: '#' },
{ name: 'Underwear', href: '#' },
],
},
{
id: 'accessories',
name: 'All Accessories',
items: [
{ name: 'Watches', href: '#' },
{ name: 'Wallets', href: '#' },
{ name: 'Bags', href: '#' },
{ name: 'Sunglasses', href: '#' },
{ name: 'Hats', href: '#' },
{ name: 'Belts', href: '#' },
],
},
],
[
{
id: 'brands',
name: 'Brands',
items: [
{ name: 'Full Nelson', href: '#' },
{ name: 'My Way', href: '#' },
{ name: 'Re-Arranged', href: '#' },
{ name: 'Counterfeit', href: '#' },
{ name: 'Significant Other', href: '#' },
],
},
],
],
},
{
id: 'men',
name: 'Men',
featured: [
{
name: 'Accessories',
href: '#',
imageSrc: 'https://tailwindcss.com/plus-assets/img/ecommerce-images/home-page-03-category-01.jpg',
imageAlt:
'Wooden shelf with gray and olive drab green baseball caps, next to wooden clothes hanger with sweaters.',
},
{
name: 'New Arrivals',
href: '#',
imageSrc:
'https://tailwindcss.com/plus-assets/img/ecommerce-images/product-page-04-detail-product-shot-01.jpg',
imageAlt: 'Drawstring top with elastic loop closure and textured interior padding.',
},
{
name: 'Artwork Tees',
href: '#',
imageSrc: 'https://tailwindcss.com/plus-assets/img/ecommerce-images/category-page-02-image-card-06.jpg',
imageAlt:
'Three shirts in gray, white, and blue arranged on table with same line drawing of hands and shapes overlapping on front of shirt.',
},
],
sections: [
[
{
id: 'shoes',
name: 'Shoes & Accessories',
items: [
{ name: 'Sneakers', href: '#' },
{ name: 'Boots', href: '#' },
{ name: 'Sandals', href: '#' },
{ name: 'Socks', href: '#' },
],
},
{
id: 'collection',
name: 'Shop Collection',
items: [
{ name: 'Everything', href: '#' },
{ name: 'Core', href: '#' },
{ name: 'New Arrivals', href: '#' },
{ name: 'Sale', href: '#' },
],
},
],
[
{
id: 'clothing',
name: 'All Clothing',
items: [
{ name: 'Basic Tees', href: '#' },
{ name: 'Artwork Tees', href: '#' },
{ name: 'Pants', href: '#' },
{ name: 'Hoodies', href: '#' },
{ name: 'Swimsuits', href: '#' },
],
},
{
id: 'accessories',
name: 'All Accessories',
items: [
{ name: 'Watches', href: '#' },
{ name: 'Wallets', href: '#' },
{ name: 'Bags', href: '#' },
{ name: 'Sunglasses', href: '#' },
{ name: 'Hats', href: '#' },
{ name: 'Belts', href: '#' },
],
},
],
[
{
id: 'brands',
name: 'Brands',
items: [
{ name: 'Re-Arranged', href: '#' },
{ name: 'Counterfeit', href: '#' },
{ name: 'Full Nelson', href: '#' },
{ name: 'My Way', href: '#' },
],
},
],
],
},
],
pages: [
{ name: 'Company', href: '#' },
{ name: 'Stores', href: '#' },
],
}
function classNames(...classes) {
return classes.filter(Boolean).join(' ')
}
export default function Example() {
const [open, setOpen] = useState(false)
return (
<div className="bg-white">
{/* Mobile menu */}
<Dialog open={open} onClose={setOpen} 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={() => setOpen(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-10 px-4 pt-10 pb-8">
<div className="space-y-4">
{category.featured.map((item, itemIdx) => (
<div key={itemIdx} className="group relative overflow-hidden rounded-md bg-gray-100">
<img
alt={item.imageAlt}
src={item.imageSrc}
className="aspect-square w-full object-cover group-hover:opacity-75"
/>
<div className="absolute inset-0 flex flex-col justify-end">
<div className="bg-white/60 p-4 text-base sm:text-sm">
<a href={item.href} className="font-medium text-gray-900">
<span aria-hidden="true" className="absolute inset-0" />
{item.name}
</a>
<p aria-hidden="true" className="mt-0.5 text-gray-700 sm:mt-1">
Shop now
</p>
</div>
</div>
</div>
))}
</div>
{category.sections.map((column, columnIdx) => (
<div key={columnIdx} className="space-y-10">
{column.map((section) => (
<div key={section.name}>
<p id={`${category.id}-${section.id}-heading-mobile`} className="font-medium text-gray-900">
{section.name}
</p>
<ul
role="list"
aria-labelledby={`${category.id}-${section.id}-heading-mobile`}
className="mt-6 flex flex-col space-y-6"
>
{section.items.map((item) => (
<li key={item.name} className="flow-root">
<a href={item.href} className="-m-2 block p-2 text-gray-500">
{item.name}
</a>
</li>
))}
</ul>
</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="border-t border-gray-200 px-4 py-6">
<a href="#" className="-m-2 flex items-center p-2">
<img
alt=""
src="https://tailwindcss.com/plus-assets/img/flags/flag-canada.svg"
className="block h-auto w-5 shrink-0"
/>
<span className="ml-3 block text-base font-medium text-gray-900">CAD</span>
<span className="sr-only">, change currency</span>
</a>
</div>
</DialogPanel>
</div>
</Dialog>
<header className="relative bg-white">
<nav aria-label="Top" className="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8">
<div className="border-b border-gray-200">
<div className="flex h-16 items-center justify-between">
<div className="flex flex-1 items-center lg:hidden">
<button
type="button"
onClick={() => setOpen(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>
<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>
{/* Flyout menus */}
<PopoverGroup className="hidden lg:block lg:flex-1 lg:self-stretch">
<div className="flex h-full 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="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-2 gap-x-8 gap-y-10 py-16">
<div className="grid grid-cols-2 grid-rows-1 gap-8 text-sm">
{category.featured.map((item, itemIdx) => (
<div
key={item.name}
className={classNames(
itemIdx === 0 ? 'col-span-2' : '',
'group relative overflow-hidden rounded-md bg-gray-100',
)}
>
<img
alt={item.imageAlt}
src={item.imageSrc}
className={classNames(
itemIdx === 0 ? 'aspect-2/1' : 'aspect-square',
'w-full object-cover group-hover:opacity-75',
)}
/>
<div className="absolute inset-0 flex flex-col justify-end">
<div className="bg-white/60 p-4 text-sm">
<a href={item.href} className="font-medium text-gray-900">
<span aria-hidden="true" className="absolute inset-0" />
{item.name}
</a>
<p aria-hidden="true" className="mt-0.5 text-gray-700 sm:mt-1">
Shop now
</p>
</div>
</div>
</div>
))}
</div>
<div className="grid grid-cols-3 gap-x-8 gap-y-10 text-sm text-gray-500">
{category.sections.map((column, columnIdx) => (
<div key={columnIdx} className="space-y-10">
{column.map((section) => (
<div key={section.name}>
<p
id={`${category.id}-${section.id}-heading`}
className="font-medium text-gray-900"
>
{section.name}
</p>
<ul
role="list"
aria-labelledby={`${category.id}-${section.id}-heading`}
className="mt-4 space-y-4"
>
{section.items.map((item) => (
<li key={item.name} className="flex">
<a href={item.href} className="hover:text-gray-800">
{item.name}
</a>
</li>
))}
</ul>
</div>
))}
</div>
))}
</div>
</div>
</div>
</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>
{/* Logo */}
<a href="#" className="flex">
<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-gray-700 hover:text-gray-800 lg:flex lg:items-center">
<img
alt=""
src="https://tailwindcss.com/plus-assets/img/flags/flag-canada.svg"
className="block h-auto w-5 shrink-0"
/>
<span className="ml-3 block text-sm font-medium">CAD</span>
<span className="sr-only">, change currency</span>
</a>
{/* Search */}
<a href="#" className="ml-6 hidden p-2 text-gray-400 hover:text-gray-500 lg:block">
<span className="sr-only">Search</span>
<MagnifyingGlassIcon aria-hidden="true" className="size-6" />
</a>
{/* Account */}
<a href="#" className="p-2 text-gray-400 hover:text-gray-500 lg:ml-4">
<span className="sr-only">Account</span>
<UserIcon aria-hidden="true" className="size-6" />
</a>
{/* Cart */}
<div className="ml-4 flow-root lg:ml-6">
<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>
</nav>
</header>
</div>
)
}