10.1k
New Components: Field, Input Group, Item and more

Building Blocks for the Web

Clean, modern building blocks. Copy and paste into your apps. Works with all Vue frameworks. Open Source. Free forever.

Files
components/HeaderCentered.vue
<script setup lang="ts">
import { ref } from 'vue'
import { Badge } from '@/components/ui/badge'
import { Button } from '@/components/ui/button'

const navItems = [
  { label: 'New Arrivals', href: '#' },
  { label: 'Women', href: '#' },
  { label: 'Men', href: '#' },
  { label: 'Accessories', href: '#' },
  { label: 'Sale', href: '#', highlight: true },
]

const cartCount = ref(2)
</script>

<template>
  <header class="sticky top-0 z-50 w-full border-b bg-background">
    <!-- Top row: actions + logo + actions -->
    <div class="container mx-auto flex h-16 items-center justify-between px-4 md:px-6">
      <!-- Left: Search -->
      <div class="flex items-center gap-1">
        <Button variant="ghost" icon aria-label="Search">
          <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="8" /><path d="m21 21-4.3-4.3" /></svg>
        </Button>
        <!-- Mobile Menu -->
        <Button variant="ghost" icon aria-label="Menu" class="md:hidden">
          <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="4" x2="20" y1="12" y2="12" /><line x1="4" x2="20" y1="6" y2="6" /><line x1="4" x2="20" y1="18" y2="18" /></svg>
        </Button>
      </div>

      <!-- Center: Logo -->
      <a href="#" class="text-2xl font-bold tracking-tight">
        STORE
      </a>

      <!-- Right: Account + Cart -->
      <div class="flex items-center gap-1">
        <Button variant="ghost" icon aria-label="Account" class="hidden sm:flex">
          <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M19 21v-2a4 4 0 0 0-4-4H9a4 4 0 0 0-4 4v2" /><circle cx="12" cy="7" r="4" /></svg>
        </Button>
        <Button variant="ghost" icon aria-label="Cart" class="relative">
          <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="8" cy="21" r="1" /><circle cx="19" cy="21" r="1" /><path d="M2.05 2.05h2l2.66 12.42a2 2 0 0 0 2 1.58h9.78a2 2 0 0 0 1.95-1.57l1.65-7.43H5.12" /></svg>
          <Badge
            v-if="cartCount > 0"
            variant="default"
            size="sm"
            class="absolute -right-1 -top-1 flex size-5 items-center justify-center rounded-full p-0 text-[10px]"
          >
            {{ cartCount }}
          </Badge>
        </Button>
      </div>
    </div>

    <!-- Bottom row: Navigation links (desktop) -->
    <nav class="hidden border-t md:block">
      <div class="container mx-auto flex items-center justify-center gap-8 px-4 py-2 md:px-6">
        <a
          v-for="item in navItems"
          :key="item.label"
          :href="item.href"
          class="text-sm font-medium transition-colors hover:text-foreground/80"
          :class="item.highlight ? 'text-destructive' : 'text-muted-foreground'"
        >
          {{ item.label }}
        </a>
      </div>
    </nav>
  </header>
</template>
Centered header with logo above navigation links.
header-centered
Files
components/HeaderCheckout.vue
<script setup lang="ts">
import { ref } from 'vue'

const steps = [
  { label: 'Information', value: 1 },
  { label: 'Shipping', value: 2 },
  { label: 'Payment', value: 3 },
]

const currentStep = ref(2)
</script>

<template>
  <header class="w-full border-b bg-background">
    <div class="container mx-auto flex h-16 items-center justify-between px-4 md:px-6">
      <!-- Logo -->
      <a href="#" class="text-xl font-bold tracking-tight">
        STORE
      </a>

      <!-- Step Indicator (desktop) -->
      <nav class="hidden items-center gap-2 md:flex" aria-label="Checkout steps">
        <template v-for="(step, index) in steps" :key="step.value">
          <!-- Connector -->
          <div
            v-if="index > 0"
            class="h-px w-8"
            :class="step.value <= currentStep ? 'bg-foreground' : 'bg-border'"
          />
          <!-- Step -->
          <div class="flex items-center gap-2">
            <div
              class="flex size-6 items-center justify-center rounded-full text-xs font-medium"
              :class="
                step.value < currentStep
                  ? 'bg-foreground text-background'
                  : step.value === currentStep
                    ? 'border-2 border-foreground text-foreground'
                    : 'border border-muted-foreground/40 text-muted-foreground'
              "
            >
              <svg
                v-if="step.value < currentStep"
                xmlns="http://www.w3.org/2000/svg"
                width="14"
                height="14"
                viewBox="0 0 24 24"
                fill="none"
                stroke="currentColor"
                stroke-width="2.5"
                stroke-linecap="round"
                stroke-linejoin="round"
              ><path d="M20 6 9 17l-5-5" /></svg>
              <span v-else>{{ step.value }}</span>
            </div>
            <span
              class="text-sm"
              :class="step.value <= currentStep ? 'font-medium text-foreground' : 'text-muted-foreground'"
            >
              {{ step.label }}
            </span>
          </div>
        </template>
      </nav>

      <!-- Secure badge -->
      <div class="flex items-center gap-1.5 text-sm text-muted-foreground">
        <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="18" height="11" x="3" y="11" rx="2" ry="2" /><path d="M7 11V7a5 5 0 0 1 10 0v4" /></svg>
        <span class="hidden sm:inline">Secure Checkout</span>
      </div>
    </div>

    <!-- Step indicator (mobile) -->
    <div class="border-t px-4 py-2 md:hidden">
      <div class="flex items-center justify-between text-xs">
        <span class="font-medium">Step {{ currentStep }} of {{ steps.length }}</span>
        <span class="text-muted-foreground">{{ steps[currentStep - 1]?.label }}</span>
      </div>
      <div class="mt-1.5 flex gap-1">
        <div
          v-for="step in steps"
          :key="step.value"
          class="h-1 flex-1 rounded-full"
          :class="step.value <= currentStep ? 'bg-foreground' : 'bg-muted'"
        />
      </div>
    </div>
  </header>
</template>
Minimal checkout header with logo and step indicator.
header-checkout
Files
components/HeaderShop.vue
<script setup lang="ts">
import { ref } from 'vue'
import { Badge } from '@/components/ui/badge'
import { Button } from '@/components/ui/button'

const navItems = [
  { label: 'Women', href: '#' },
  { label: 'Men', href: '#' },
  { label: 'Kids', href: '#' },
  { label: 'Sale', href: '#', highlight: true },
]

const cartCount = ref(3)
</script>

<template>
  <header class="sticky top-0 z-50 w-full">
    <!-- Utility Bar -->
    <div class="bg-foreground text-background">
      <div class="container mx-auto flex h-8 items-center justify-center px-4 text-xs tracking-wide md:px-6">
        Free shipping on orders over 50 € — Shop now
      </div>
    </div>

    <!-- Main Header -->
    <div class="border-b bg-background">
      <div class="container mx-auto flex h-14 items-center justify-between px-4 md:px-6">
        <!-- Logo -->
        <a href="#" class="text-xl font-bold tracking-tight">
          STORE
        </a>

        <!-- Desktop Navigation -->
        <nav class="hidden items-center gap-6 md:flex">
          <a
            v-for="item in navItems"
            :key="item.label"
            :href="item.href"
            class="text-sm font-medium transition-colors hover:text-foreground/80"
            :class="item.highlight ? 'text-destructive' : 'text-foreground'"
          >
            {{ item.label }}
          </a>
        </nav>

        <!-- Actions -->
        <div class="flex items-center gap-1">
          <!-- Search -->
          <Button variant="ghost" icon aria-label="Search">
            <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="8" /><path d="m21 21-4.3-4.3" /></svg>
          </Button>

          <!-- Account -->
          <Button variant="ghost" icon aria-label="Account" class="hidden sm:flex">
            <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M19 21v-2a4 4 0 0 0-4-4H9a4 4 0 0 0-4 4v2" /><circle cx="12" cy="7" r="4" /></svg>
          </Button>

          <!-- Cart -->
          <Button variant="ghost" icon aria-label="Cart" class="relative">
            <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="8" cy="21" r="1" /><circle cx="19" cy="21" r="1" /><path d="M2.05 2.05h2l2.66 12.42a2 2 0 0 0 2 1.58h9.78a2 2 0 0 0 1.95-1.57l1.65-7.43H5.12" /></svg>
            <Badge
              v-if="cartCount > 0"
              variant="default"
              size="sm"
              class="absolute -right-1 -top-1 flex size-5 items-center justify-center rounded-full p-0 text-[10px]"
            >
              {{ cartCount > 9 ? '9+' : cartCount }}
            </Badge>
          </Button>

          <!-- Mobile Menu -->
          <Button variant="ghost" icon aria-label="Menu" class="md:hidden">
            <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="4" x2="20" y1="12" y2="12" /><line x1="4" x2="20" y1="6" y2="6" /><line x1="4" x2="20" y1="18" y2="18" /></svg>
          </Button>
        </div>
      </div>
    </div>
  </header>
</template>
Standard e-commerce header with utility bar, logo, navigation, and action icons.
header-shop
Files
components/StoreNavMega.vue
<script lang="ts">
interface NavCategory {
  name: string
  featured: { name: string; href: string; imageSrc: string; imageAlt: string }[]
  sections: { name: string; items: { name: string; href: string }[] }[]
}

interface NavPage {
  name: string
  href: string
}
</script>

<script setup lang="ts">
import { ref } from 'vue'
import { ChevronDown } from 'lucide-vue-next'
import { Badge } from '@/components/ui/badge'
import { Button } from '@/components/ui/button'
import {
  MegaMenu,
  MegaMenuContent,
  MegaMenuItem,
  MegaMenuLink,
  MegaMenuList,
  MegaMenuTrigger,
  MegaMenuViewport,
} from '@/components/ui/mega-menu'
import {
  Sheet,
  SheetContent,
  SheetTitle,
  SheetTrigger,
} from '@/components/ui/sheet'
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'

const props = withDefaults(defineProps<{
  /** Announcement bar text. Set to empty string to hide. */
  announcement?: string
  /** Logo text or brand name */
  logoText?: string
  /** Logo image src. When set, overrides logoText. */
  logoSrc?: string
  /** Top-level categories with mega-menu content */
  categories?: NavCategory[]
  /** Simple navigation links (no mega-menu) */
  pages?: NavPage[]
  /** Cart item count */
  cartCount?: number
  /** Currency label */
  currency?: string
  /** Currency flag image src */
  currencyFlag?: string
}>(), {
  announcement: 'Free shipping on orders over 50 € — Shop now',
  logoText: 'STORE',
  logoSrc: '',
  categories: () => [
    {
      name: 'Women',
      featured: [
        {
          name: 'New Arrivals',
          href: '#',
          imageSrc: '/placeholder.svg',
          imageAlt: 'New arrivals collection.',
        },
        {
          name: 'Basic Tees',
          href: '#',
          imageSrc: '/placeholder.svg',
          imageAlt: 'Basic tees collection.',
        },
      ],
      sections: [
        {
          name: 'Clothing',
          items: [
            { name: 'Tops', href: '#' },
            { name: 'Dresses', href: '#' },
            { name: 'Pants', href: '#' },
            { name: 'Denim', href: '#' },
            { name: 'Sweaters', href: '#' },
            { name: 'T-Shirts', href: '#' },
            { name: 'Jackets', href: '#' },
            { name: 'Activewear', href: '#' },
            { name: 'Browse All', href: '#' },
          ],
        },
        {
          name: 'Accessories',
          items: [
            { name: 'Watches', href: '#' },
            { name: 'Wallets', href: '#' },
            { name: 'Bags', href: '#' },
            { name: 'Sunglasses', href: '#' },
            { name: 'Hats', href: '#' },
            { name: 'Belts', href: '#' },
          ],
        },
        {
          name: 'Brands',
          items: [
            { name: 'Full Nelson', href: '#' },
            { name: 'My Way', href: '#' },
            { name: 'Re-Arranged', href: '#' },
            { name: 'Counterfeit', href: '#' },
            { name: 'Significant Other', href: '#' },
          ],
        },
      ],
    },
    {
      name: 'Men',
      featured: [
        {
          name: 'New Arrivals',
          href: '#',
          imageSrc: '/placeholder.svg',
          imageAlt: 'New arrivals collection.',
        },
        {
          name: 'Artwork Tees',
          href: '#',
          imageSrc: '/placeholder.svg',
          imageAlt: 'Artwork tees collection.',
        },
      ],
      sections: [
        {
          name: 'Clothing',
          items: [
            { name: 'Tops', href: '#' },
            { name: 'Pants', href: '#' },
            { name: 'Sweaters', href: '#' },
            { name: 'T-Shirts', href: '#' },
            { name: 'Jackets', href: '#' },
            { name: 'Activewear', href: '#' },
            { name: 'Browse All', href: '#' },
          ],
        },
        {
          name: 'Accessories',
          items: [
            { name: 'Watches', href: '#' },
            { name: 'Wallets', href: '#' },
            { name: 'Bags', href: '#' },
            { name: 'Sunglasses', href: '#' },
            { name: 'Hats', href: '#' },
            { name: 'Belts', href: '#' },
          ],
        },
        {
          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: '#' },
  ],
  cartCount: 0,
  currency: 'EUR',
  currencyFlag: '',
})

const mobileOpen = ref(false)
</script>

<template>
  <header class="sticky top-0 z-50 w-full bg-background">
    <!-- Announcement Bar -->
    <div v-if="announcement" class="flex h-10 items-center justify-center bg-primary px-4 text-sm font-medium text-primary-foreground sm:px-6 lg:px-8">
      {{ announcement }}
    </div>

    <MegaMenu>
      <nav aria-label="Top" class="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8">
        <div class="border-b">
          <div class="flex h-16 items-center">
            <!-- Mobile Menu Trigger -->
            <Sheet v-model:open="mobileOpen">
              <SheetTrigger as-child>
                <Button variant="ghost" icon class="lg:hidden" aria-label="Open menu">
                  <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="4" x2="20" y1="12" y2="12" /><line x1="4" x2="20" y1="6" y2="6" /><line x1="4" x2="20" y1="18" y2="18" /></svg>
                </Button>
              </SheetTrigger>

              <SheetContent side="left" class="w-full max-w-xs overflow-y-auto p-0">
                <SheetTitle class="sr-only">Navigation menu</SheetTitle>

                <!-- Mobile Tabs -->
                <Tabs :default-value="categories[0]?.name" class="mt-2">
                  <TabsList variant="line" class="w-full border-b px-4">
                    <TabsTrigger
                      v-for="cat in categories"
                      :key="cat.name"
                      :value="cat.name"
                      variant="line"
                      class="flex-1"
                    >
                      {{ cat.name }}
                    </TabsTrigger>
                  </TabsList>

                  <TabsContent
                    v-for="cat in categories"
                    :key="cat.name"
                    :value="cat.name"
                    class="space-y-10 px-4 pt-10 pb-8"
                  >
                    <!-- Featured Images -->
                    <div class="grid grid-cols-2 gap-x-4">
                      <div v-for="item in cat.featured" :key="item.name" class="group relative text-sm">
                        <img
                          :src="item.imageSrc"
                          :alt="item.imageAlt"
                          class="aspect-square w-full rounded-lg bg-muted object-cover group-hover:opacity-75"
                        />
                        <a :href="item.href" class="mt-6 block font-medium" @click="mobileOpen = false">
                          <span aria-hidden="true" class="absolute inset-0 z-10" />
                          {{ item.name }}
                        </a>
                        <p aria-hidden="true" class="mt-1 text-muted-foreground">Shop now</p>
                      </div>
                    </div>

                    <!-- Link Sections -->
                    <div v-for="section in cat.sections" :key="section.name">
                      <p class="font-medium">{{ section.name }}</p>
                      <ul role="list" class="mt-6 flex flex-col space-y-6">
                        <li v-for="link in section.items" :key="link.name" class="flow-root">
                          <a :href="link.href" class="-m-2 block p-2 text-muted-foreground" @click="mobileOpen = false">
                            {{ link.name }}
                          </a>
                        </li>
                      </ul>
                    </div>
                  </TabsContent>
                </Tabs>

                <!-- Mobile Pages -->
                <div class="space-y-6 border-t px-4 py-6">
                  <div v-for="page in pages" :key="page.name" class="flow-root">
                    <a :href="page.href" class="-m-2 block p-2 font-medium" @click="mobileOpen = false">
                      {{ page.name }}
                    </a>
                  </div>
                </div>

                <!-- Mobile Auth -->
                <div class="space-y-6 border-t px-4 py-6">
                  <div class="flow-root">
                    <a href="#" class="-m-2 block p-2 font-medium" @click="mobileOpen = false">Sign in</a>
                  </div>
                  <div class="flow-root">
                    <a href="#" class="-m-2 block p-2 font-medium" @click="mobileOpen = false">Create account</a>
                  </div>
                </div>

                <!-- Mobile Currency -->
                <div v-if="currency" class="border-t px-4 py-6">
                  <a href="#" class="-m-2 flex items-center p-2">
                    <img v-if="currencyFlag" :src="currencyFlag" alt="" class="block h-auto w-5 shrink-0" />
                    <span class="ml-3 block text-sm font-medium">{{ currency }}</span>
                    <span class="sr-only">, change currency</span>
                  </a>
                </div>
              </SheetContent>
            </Sheet>

            <!-- Logo -->
            <div class="ml-4 flex lg:ml-0">
              <a href="#">
                <span v-if="!logoSrc" class="text-xl font-bold tracking-tight">{{ logoText }}</span>
                <img v-else :src="logoSrc" alt="" class="h-8 w-auto" />
              </a>
            </div>

            <!-- Desktop Mega Menu Triggers -->
            <div class="group/mega-menu hidden lg:ml-8 lg:flex lg:self-stretch">
              <MegaMenuList>
                <MegaMenuItem v-for="cat in categories" :key="cat.name">
                  <MegaMenuTrigger
                    class="h-9 rounded-md bg-transparent px-4 py-2 hover:bg-accent hover:text-accent-foreground data-[state=open]:bg-accent/50"
                  >
                    {{ cat.name }}
                    <ChevronDown class="relative top-px ml-1 size-3 transition duration-300 group-data-[state=open]:rotate-180" aria-hidden="true" />
                  </MegaMenuTrigger>

                  <MegaMenuContent class="border-t shadow-lg">
                    <div class="relative">
                      <div class="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8">
                        <div class="grid grid-cols-2 gap-x-8 gap-y-10 py-16">
                          <!-- Featured Images -->
                          <div class="col-start-2 grid grid-cols-2 gap-x-8">
                            <div v-for="item in cat.featured" :key="item.name" class="group relative text-sm">
                              <img
                                :src="item.imageSrc"
                                :alt="item.imageAlt"
                                class="aspect-square w-full rounded-lg bg-muted object-cover group-hover:opacity-75"
                              />
                              <MegaMenuLink as-child>
                                <a :href="item.href" class="mt-6 block font-medium">
                                  <span aria-hidden="true" class="absolute inset-0 z-10" />
                                  {{ item.name }}
                                </a>
                              </MegaMenuLink>
                              <p aria-hidden="true" class="mt-1 text-muted-foreground">Shop now</p>
                            </div>
                          </div>

                          <!-- Link Sections -->
                          <div class="row-start-1 grid gap-x-8 gap-y-10 text-sm" :class="[cat.sections.length === 2 ? 'grid-cols-2' : cat.sections.length >= 3 ? 'grid-cols-3' : 'grid-cols-1']">
                            <div v-for="section in cat.sections" :key="section.name">
                              <p class="font-medium">{{ section.name }}</p>
                              <ul role="list" class="mt-6 space-y-6 sm:mt-4 sm:space-y-4">
                                <li v-for="link in section.items" :key="link.name" class="flex">
                                  <MegaMenuLink as-child>
                                    <a :href="link.href" class="text-muted-foreground hover:text-foreground">
                                      {{ link.name }}
                                    </a>
                                  </MegaMenuLink>
                                </li>
                              </ul>
                            </div>
                          </div>
                        </div>
                      </div>
                    </div>
                  </MegaMenuContent>
                </MegaMenuItem>

                <!-- Simple Pages -->
                <MegaMenuItem v-for="page in pages" :key="page.name" class="flex">
                  <MegaMenuLink as-child>
                    <a
                      :href="page.href"
                      class="inline-flex h-9 items-center justify-center rounded-md bg-transparent px-4 py-2 text-sm font-medium hover:bg-accent hover:text-accent-foreground"
                    >
                      {{ page.name }}
                    </a>
                  </MegaMenuLink>
                </MegaMenuItem>
              </MegaMenuList>
            </div>

            <!-- Right Actions -->
            <div class="ml-auto flex items-center">
              <!-- Auth Links (Desktop) -->
              <div class="hidden lg:flex lg:flex-1 lg:items-center lg:justify-end lg:space-x-6">
                <a href="#" class="text-sm font-medium hover:text-foreground/80">Sign in</a>
                <span aria-hidden="true" class="h-6 w-px bg-border" />
                <a href="#" class="text-sm font-medium hover:text-foreground/80">Create account</a>
              </div>

              <!-- Currency (Desktop) -->
              <div v-if="currency" class="hidden lg:ml-8 lg:flex">
                <a href="#" class="flex items-center hover:text-foreground/80">
                  <img v-if="currencyFlag" :src="currencyFlag" alt="" class="block h-auto w-5 shrink-0" />
                  <span class="ml-3 block text-sm font-medium">{{ currency }}</span>
                  <span class="sr-only">, change currency</span>
                </a>
              </div>

              <!-- Search -->
              <div class="flex lg:ml-6">
                <Button variant="ghost" icon aria-label="Search">
                  <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="8" /><path d="m21 21-4.3-4.3" /></svg>
                </Button>
              </div>

              <!-- Cart -->
              <div class="ml-4 flow-root lg:ml-6">
                <a href="#" class="group -m-2 flex items-center p-2">
                  <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="shrink-0 text-muted-foreground group-hover:text-foreground/80"><path d="M6 2 3 6v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2V6l-3-4Z" /><path d="M3 6h18" /><path d="M16 10a4 4 0 0 1-8 0" /></svg>
                  <Badge
                    v-if="cartCount > 0"
                    variant="default"
                    size="sm"
                    class="ml-2 flex size-5 items-center justify-center rounded-full p-0 text-[10px]"
                  >
                    {{ cartCount > 9 ? '9+' : cartCount }}
                  </Badge>
                  <span v-else class="ml-2 text-sm font-medium text-muted-foreground group-hover:text-foreground/80">0</span>
                  <span class="sr-only">items in cart, view bag</span>
                </a>
              </div>
            </div>
          </div>
        </div>
      </nav>

      <!-- Full-width flyout viewport -->
      <MegaMenuViewport class="z-50" />
    </MegaMenu>
  </header>
</template>
Store navigation with mega-menu dropdowns, featured categories, and mobile drawer.
store-nav-mega
Files
components/StoreNavSidebar.vue
<script lang="ts">
interface NavCategory {
  name: string
  featured: { name: string; href: string; imageSrc: string; imageAlt: string }[]
  sections: { name: string; items: { name: string; href: string }[] }[]
}

interface NavPage {
  name: string
  href: string
}
</script>

<script setup lang="ts">
import { ref } from 'vue'
import { ChevronDown } from 'lucide-vue-next'
import { Badge } from '@/components/ui/badge'
import { Button } from '@/components/ui/button'
import {
  MegaMenu,
  MegaMenuContent,
  MegaMenuItem,
  MegaMenuLink,
  MegaMenuList,
  MegaMenuTrigger,
} from '@/components/ui/mega-menu'
import {
  Sheet,
  SheetContent,
  SheetTitle,
  SheetTrigger,
} from '@/components/ui/sheet'
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'

withDefaults(defineProps<{
  /** Announcement bar text. Set to empty string to hide. */
  announcement?: string
  /** Logo text or brand name */
  logoText?: string
  /** Logo image src. When set, overrides logoText. */
  logoSrc?: string
  /** Top-level categories with mega-menu content */
  categories?: NavCategory[]
  /** Simple navigation links (no mega-menu) */
  pages?: NavPage[]
  /** Cart item count */
  cartCount?: number
  /** Currency label */
  currency?: string
  /** Currency flag image src */
  currencyFlag?: string
}>(), {
  announcement: 'Get free delivery on orders over $100',
  logoText: 'STORE',
  logoSrc: '',
  categories: () => [
    {
      name: 'Women',
      featured: [
        {
          name: 'New Arrivals',
          href: '#',
          imageSrc: '/placeholder.svg',
          imageAlt: 'New arrivals collection.',
        },
        {
          name: 'Basic Tees',
          href: '#',
          imageSrc: '/placeholder.svg',
          imageAlt: 'Basic tees collection.',
        },
      ],
      sections: [
        {
          name: 'Clothing',
          items: [
            { name: 'Tops', href: '#' },
            { name: 'Dresses', href: '#' },
            { name: 'Pants', href: '#' },
            { name: 'Denim', href: '#' },
            { name: 'Sweaters', href: '#' },
            { name: 'T-Shirts', href: '#' },
            { name: 'Jackets', href: '#' },
            { name: 'Activewear', href: '#' },
            { name: 'Browse All', href: '#' },
          ],
        },
        {
          name: 'Accessories',
          items: [
            { name: 'Watches', href: '#' },
            { name: 'Wallets', href: '#' },
            { name: 'Bags', href: '#' },
            { name: 'Sunglasses', href: '#' },
            { name: 'Hats', href: '#' },
            { name: 'Belts', href: '#' },
          ],
        },
        {
          name: 'Brands',
          items: [
            { name: 'Full Nelson', href: '#' },
            { name: 'My Way', href: '#' },
            { name: 'Re-Arranged', href: '#' },
            { name: 'Counterfeit', href: '#' },
            { name: 'Significant Other', href: '#' },
          ],
        },
      ],
    },
    {
      name: 'Men',
      featured: [
        {
          name: 'New Arrivals',
          href: '#',
          imageSrc: '/placeholder.svg',
          imageAlt: 'New arrivals collection.',
        },
        {
          name: 'Artwork Tees',
          href: '#',
          imageSrc: '/placeholder.svg',
          imageAlt: 'Artwork tees collection.',
        },
      ],
      sections: [
        {
          name: 'Clothing',
          items: [
            { name: 'Tops', href: '#' },
            { name: 'Pants', href: '#' },
            { name: 'Sweaters', href: '#' },
            { name: 'T-Shirts', href: '#' },
            { name: 'Jackets', href: '#' },
            { name: 'Activewear', href: '#' },
            { name: 'Browse All', href: '#' },
          ],
        },
        {
          name: 'Accessories',
          items: [
            { name: 'Watches', href: '#' },
            { name: 'Wallets', href: '#' },
            { name: 'Bags', href: '#' },
            { name: 'Sunglasses', href: '#' },
            { name: 'Hats', href: '#' },
            { name: 'Belts', href: '#' },
          ],
        },
        {
          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: '#' },
  ],
  cartCount: 0,
  currency: 'CAD',
  currencyFlag: '',
})

const mobileOpen = ref(false)
</script>

<template>
  <header class="sticky top-0 z-50 w-full bg-background">
    <!-- Announcement Bar -->
    <div
      v-if="announcement"
      class="flex h-10 items-center justify-center bg-primary px-4 text-sm font-medium text-primary-foreground sm:px-6 lg:px-8"
    >
      {{ announcement }}
    </div>

    <nav aria-label="Top" class="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8">
      <div class="border-b">
        <div class="flex h-16 items-center">
          <!-- Mobile Menu Trigger -->
          <Sheet v-model:open="mobileOpen">
            <SheetTrigger as-child>
              <Button variant="ghost" icon class="lg:hidden" aria-label="Open menu">
                <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="4" x2="20" y1="12" y2="12" /><line x1="4" x2="20" y1="6" y2="6" /><line x1="4" x2="20" y1="18" y2="18" /></svg>
              </Button>
            </SheetTrigger>

            <SheetContent side="left" class="w-full max-w-xs overflow-y-auto p-0">
              <SheetTitle class="sr-only">Navigation menu</SheetTitle>

              <!-- Mobile Tabs -->
              <Tabs :default-value="categories[0]?.name" class="mt-2">
                <TabsList variant="line" class="w-full border-b px-4">
                  <TabsTrigger
                    v-for="cat in categories"
                    :key="cat.name"
                    :value="cat.name"
                    variant="line"
                    class="flex-1"
                  >
                    {{ cat.name }}
                  </TabsTrigger>
                </TabsList>

                <TabsContent
                  v-for="cat in categories"
                  :key="cat.name"
                  :value="cat.name"
                  class="space-y-10 px-4 pt-10 pb-8"
                >
                  <!-- Featured Images -->
                  <div class="grid grid-cols-2 gap-x-4">
                    <div v-for="item in cat.featured" :key="item.name" class="group relative text-sm">
                      <img
                        :src="item.imageSrc"
                        :alt="item.imageAlt"
                        class="aspect-square w-full rounded-lg bg-muted object-cover group-hover:opacity-75"
                      />
                      <a :href="item.href" class="mt-6 block font-medium" @click="mobileOpen = false">
                        <span aria-hidden="true" class="absolute inset-0 z-10" />
                        {{ item.name }}
                      </a>
                      <p aria-hidden="true" class="mt-1 text-muted-foreground">Shop now</p>
                    </div>
                  </div>

                  <!-- Link Sections -->
                  <div v-for="section in cat.sections" :key="section.name">
                    <p class="font-medium">{{ section.name }}</p>
                    <ul role="list" class="mt-6 flex flex-col space-y-6">
                      <li v-for="link in section.items" :key="link.name" class="flow-root">
                        <a :href="link.href" class="-m-2 block p-2 text-muted-foreground" @click="mobileOpen = false">
                          {{ link.name }}
                        </a>
                      </li>
                    </ul>
                  </div>
                </TabsContent>
              </Tabs>

              <!-- Mobile Pages -->
              <div class="space-y-6 border-t px-4 py-6">
                <div v-for="page in pages" :key="page.name" class="flow-root">
                  <a :href="page.href" class="-m-2 block p-2 font-medium" @click="mobileOpen = false">
                    {{ page.name }}
                  </a>
                </div>
              </div>

              <!-- Mobile Auth -->
              <div class="space-y-6 border-t px-4 py-6">
                <div class="flow-root">
                  <a href="#" class="-m-2 block p-2 font-medium" @click="mobileOpen = false">Sign in</a>
                </div>
                <div class="flow-root">
                  <a href="#" class="-m-2 block p-2 font-medium" @click="mobileOpen = false">Create account</a>
                </div>
              </div>

              <!-- Mobile Currency -->
              <div v-if="currency" class="border-t px-4 py-6">
                <a href="#" class="-m-2 flex items-center p-2">
                  <img v-if="currencyFlag" :src="currencyFlag" alt="" class="block h-auto w-5 shrink-0" />
                  <span class="ml-3 block text-sm font-medium">{{ currency }}</span>
                  <span class="sr-only">, change currency</span>
                </a>
              </div>
            </SheetContent>
          </Sheet>

          <!-- Logo -->
          <div class="ml-4 flex lg:ml-0">
            <a href="#">
              <span v-if="!logoSrc" class="text-xl font-bold tracking-tight">{{ logoText }}</span>
              <img v-else :src="logoSrc" alt="" class="h-8 w-auto" />
            </a>
          </div>

          <!-- Desktop Mega Menu -->
          <MegaMenu class="hidden lg:ml-8 lg:flex lg:self-stretch">
            <MegaMenuList>
              <MegaMenuItem v-for="cat in categories" :key="cat.name">
                <MegaMenuTrigger
                  class="h-9 rounded-md bg-transparent px-4 py-2 hover:bg-accent hover:text-accent-foreground data-[state=open]:bg-accent/50"
                >
                  {{ cat.name }}
                  <ChevronDown class="relative top-px ml-1 size-3 transition duration-300 group-data-[state=open]:rotate-180" aria-hidden="true" />
                </MegaMenuTrigger>

                <MegaMenuContent class="border-t">
                  <div class="relative">
                    <div class="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8">
                      <div class="py-16">
                        <!-- Featured Categories — full-width horizontal row -->
                        <div class="grid gap-6" :class="[cat.featured.length === 2 ? 'grid-cols-2' : cat.featured.length >= 3 ? 'grid-cols-3' : 'grid-cols-1']">
                          <div v-for="item in cat.featured" :key="item.name" class="group relative text-sm">
                            <img
                              :src="item.imageSrc"
                              :alt="item.imageAlt"
                              class="aspect-[16/9] w-full rounded-lg bg-muted object-cover group-hover:opacity-75"
                            />
                            <MegaMenuLink as-child>
                              <a :href="item.href" class="mt-4 block font-medium">
                                <span aria-hidden="true" class="absolute inset-0 z-10" />
                                {{ item.name }}
                              </a>
                            </MegaMenuLink>
                            <p aria-hidden="true" class="mt-1 text-muted-foreground">Shop now</p>
                          </div>
                        </div>

                        <!-- Link Sections — full-width row below images -->
                        <div
                          class="mt-10 grid gap-x-8 gap-y-10 border-t pt-10 text-sm"
                          :class="[cat.sections.length === 2 ? 'grid-cols-2' : cat.sections.length >= 3 ? 'grid-cols-3' : 'grid-cols-1']"
                        >
                          <div v-for="section in cat.sections" :key="section.name">
                            <p class="font-medium">{{ section.name }}</p>
                            <ul role="list" class="mt-6 space-y-6 sm:mt-4 sm:space-y-4">
                              <li v-for="link in section.items" :key="link.name" class="flex">
                                <MegaMenuLink as-child>
                                  <a :href="link.href" class="text-muted-foreground hover:text-foreground">
                                    {{ link.name }}
                                  </a>
                                </MegaMenuLink>
                              </li>
                            </ul>
                          </div>
                        </div>
                      </div>
                    </div>
                  </div>
                </MegaMenuContent>
              </MegaMenuItem>

              <!-- Simple Pages -->
              <MegaMenuItem v-for="page in pages" :key="page.name" class="flex">
                <MegaMenuLink as-child>
                  <a
                    :href="page.href"
                    class="inline-flex h-9 items-center justify-center rounded-md bg-transparent px-4 py-2 text-sm font-medium hover:bg-accent hover:text-accent-foreground"
                  >
                    {{ page.name }}
                  </a>
                </MegaMenuLink>
              </MegaMenuItem>
            </MegaMenuList>
          </MegaMenu>

          <!-- Right Actions -->
          <div class="ml-auto flex items-center">
            <!-- Auth Links (Desktop) -->
            <div class="hidden lg:flex lg:flex-1 lg:items-center lg:justify-end lg:space-x-6">
              <a href="#" class="text-sm font-medium hover:text-foreground/80">Sign in</a>
              <span aria-hidden="true" class="h-6 w-px bg-border" />
              <a href="#" class="text-sm font-medium hover:text-foreground/80">Create account</a>
            </div>

            <!-- Currency (Desktop) -->
            <div v-if="currency" class="hidden lg:ml-8 lg:flex">
              <a href="#" class="flex items-center hover:text-foreground/80">
                <img v-if="currencyFlag" :src="currencyFlag" alt="" class="block h-auto w-5 shrink-0" />
                <span class="ml-3 block text-sm font-medium">{{ currency }}</span>
                <span class="sr-only">, change currency</span>
              </a>
            </div>

            <!-- Search -->
            <div class="flex lg:ml-6">
              <Button variant="ghost" icon aria-label="Search">
                <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="8" /><path d="m21 21-4.3-4.3" /></svg>
              </Button>
            </div>

            <!-- Cart -->
            <div class="ml-4 flow-root lg:ml-6">
              <a href="#" class="group -m-2 flex items-center p-2">
                <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="shrink-0 text-muted-foreground group-hover:text-foreground/80"><path d="M6 2 3 6v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2V6l-3-4Z" /><path d="M3 6h18" /><path d="M16 10a4 4 0 0 1-8 0" /></svg>
                <Badge
                  v-if="cartCount > 0"
                  variant="default"
                  size="sm"
                  class="ml-2 flex size-5 items-center justify-center rounded-full p-0 text-[10px]"
                >
                  {{ cartCount > 9 ? '9+' : cartCount }}
                </Badge>
                <span v-else class="ml-2 text-sm font-medium text-muted-foreground group-hover:text-foreground/80">0</span>
                <span class="sr-only">items in cart, view bag</span>
              </a>
            </div>
          </div>
        </div>
      </div>
    </nav>
  </header>
</template>
Store navigation with featured category images spanning full mega-menu width, announcement bar, and mobile drawer.
store-nav-sidebar
Files
components/StoreNavSimple.vue
<script lang="ts">
interface FeaturedItem {
  name: string
  href: string
  imageSrc: string
  imageAlt: string
  /** When true, renders wider (col-span-2) with aspect-2/1. First item in array is typically wide. */
  wide?: boolean
}

interface LinkGroup {
  name: string
  items: { name: string; href: string }[]
}

interface NavCategory {
  name: string
  featured: FeaturedItem[]
  sections: LinkGroup[]
}

interface NavPage {
  name: string
  href: string
}
</script>

<script setup lang="ts">
import { ref } from 'vue'
import { Button } from '@/components/ui/button'
import {
  MegaMenu,
  MegaMenuContent,
  MegaMenuItem,
  MegaMenuLink,
  MegaMenuList,
  MegaMenuTrigger,
  MegaMenuViewport,
} from '@/components/ui/mega-menu'
import {
  Sheet,
  SheetContent,
  SheetTitle,
  SheetTrigger,
} from '@/components/ui/sheet'
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'

const props = withDefaults(defineProps<{
  /** Logo text or brand name */
  logoText?: string
  /** Logo image src. When set, overrides logoText. */
  logoSrc?: string
  /** Top-level categories with mega-menu content */
  categories?: NavCategory[]
  /** Simple navigation links (no mega-menu) */
  pages?: NavPage[]
  /** Cart item count */
  cartCount?: number
  /** Currency label */
  currency?: string
  /** Currency flag image src */
  currencyFlag?: string
}>(), {
  logoText: 'STORE',
  logoSrc: '',
  categories: () => [
    {
      name: 'Women',
      featured: [
        {
          name: 'New Arrivals',
          href: '#',
          imageSrc: '/placeholder.svg',
          imageAlt: 'New arrivals collection.',
          wide: true,
        },
        {
          name: 'Basic Tees',
          href: '#',
          imageSrc: '/placeholder.svg',
          imageAlt: 'Basic tees collection.',
        },
        {
          name: 'Accessories',
          href: '#',
          imageSrc: '/placeholder.svg',
          imageAlt: 'Accessories collection.',
        },
      ],
      sections: [
        {
          name: 'Shoes & Accessories',
          items: [
            { name: 'Sneakers', href: '#' },
            { name: 'Boots', href: '#' },
            { name: 'Flats', href: '#' },
            { name: 'Sandals', href: '#' },
            { name: 'Heels', href: '#' },
            { name: 'Socks', href: '#' },
          ],
        },
        {
          name: 'Shop Collection',
          items: [
            { name: 'Everything', href: '#' },
            { name: 'Core', href: '#' },
            { name: 'New Arrivals', href: '#' },
            { name: 'Sale', href: '#' },
            { name: 'Accessories', href: '#' },
          ],
        },
        {
          name: 'All Clothing',
          items: [
            { name: 'Basic Tees', href: '#' },
            { name: 'Artwork Tees', href: '#' },
            { name: 'Tops', href: '#' },
            { name: 'Bottoms', href: '#' },
            { name: 'Swimwear', href: '#' },
            { name: 'Underwear', href: '#' },
          ],
        },
        {
          name: 'All Accessories',
          items: [
            { name: 'Watches', href: '#' },
            { name: 'Wallets', href: '#' },
            { name: 'Bags', href: '#' },
            { name: 'Sunglasses', href: '#' },
            { name: 'Hats', href: '#' },
            { name: 'Belts', href: '#' },
          ],
        },
        {
          name: 'Brands',
          items: [
            { name: 'Full Nelson', href: '#' },
            { name: 'My Way', href: '#' },
            { name: 'Re-Arranged', href: '#' },
            { name: 'Counterfeit', href: '#' },
            { name: 'Significant Other', href: '#' },
          ],
        },
      ],
    },
    {
      name: 'Men',
      featured: [
        {
          name: 'Accessories',
          href: '#',
          imageSrc: '/placeholder.svg',
          imageAlt: 'Accessories collection.',
          wide: true,
        },
        {
          name: 'New Arrivals',
          href: '#',
          imageSrc: '/placeholder.svg',
          imageAlt: 'New arrivals collection.',
        },
        {
          name: 'Artwork Tees',
          href: '#',
          imageSrc: '/placeholder.svg',
          imageAlt: 'Artwork tees collection.',
        },
      ],
      sections: [
        {
          name: 'Shoes & Accessories',
          items: [
            { name: 'Sneakers', href: '#' },
            { name: 'Boots', href: '#' },
            { name: 'Sandals', href: '#' },
            { name: 'Socks', href: '#' },
          ],
        },
        {
          name: 'Shop Collection',
          items: [
            { name: 'Everything', href: '#' },
            { name: 'Core', href: '#' },
            { name: 'New Arrivals', href: '#' },
            { name: 'Sale', href: '#' },
          ],
        },
        {
          name: 'All Clothing',
          items: [
            { name: 'Basic Tees', href: '#' },
            { name: 'Artwork Tees', href: '#' },
            { name: 'Pants', href: '#' },
            { name: 'Hoodies', href: '#' },
            { name: 'Swimsuits', href: '#' },
          ],
        },
        {
          name: 'All Accessories',
          items: [
            { name: 'Watches', href: '#' },
            { name: 'Wallets', href: '#' },
            { name: 'Bags', href: '#' },
            { name: 'Sunglasses', href: '#' },
            { name: 'Hats', href: '#' },
            { name: 'Belts', href: '#' },
          ],
        },
        {
          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: '#' },
  ],
  cartCount: 0,
  currency: 'CAD',
  currencyFlag: '',
})

const mobileOpen = ref(false)

/**
 * Group sections into columns of 2 for the 3-column desktop layout.
 * E.g. 5 sections → [[s0,s1],[s2,s3],[s4]]
 */
function groupSections(sections: LinkGroup[]): LinkGroup[][] {
  const cols: LinkGroup[][] = [[], [], []]
  sections.forEach((s, i) => {
    cols[Math.min(Math.floor(i / 2), 2)]!.push(s)
  })
  return cols.filter(c => c.length > 0)
}
</script>

<template>
  <header class="relative w-full bg-background">
    <MegaMenu>
      <nav aria-label="Top" class="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8">
        <div class="border-b">
          <div class="flex h-16 items-center justify-between">
            <!-- Left: Mobile hamburger + search -->
            <div class="flex flex-1 items-center lg:hidden">
            <Sheet v-model:open="mobileOpen">
              <SheetTrigger as-child>
                <Button variant="ghost" icon class="-ml-2" aria-label="Open menu">
                  <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M3.75 6.75h16.5M3.75 12h16.5m-16.5 5.25h16.5" /></svg>
                </Button>
              </SheetTrigger>

              <SheetContent side="left" class="w-full max-w-xs overflow-y-auto p-0">
                <SheetTitle class="sr-only">Navigation menu</SheetTitle>

                <!-- Mobile Tabs -->
                <Tabs :default-value="categories[0]?.name" class="mt-2">
                  <TabsList variant="line" class="w-full border-b px-4">
                    <TabsTrigger
                      v-for="cat in categories"
                      :key="cat.name"
                      :value="cat.name"
                      variant="line"
                      class="flex-1"
                    >
                      {{ cat.name }}
                    </TabsTrigger>
                  </TabsList>

                  <TabsContent
                    v-for="cat in categories"
                    :key="cat.name"
                    :value="cat.name"
                    class="space-y-10 px-4 pt-10 pb-8"
                  >
                    <!-- Featured Images (stacked, with overlay labels) -->
                    <div class="space-y-4">
                      <div
                        v-for="item in cat.featured"
                        :key="item.name"
                        class="group relative overflow-hidden rounded-md bg-muted"
                      >
                        <img
                          :src="item.imageSrc"
                          :alt="item.imageAlt"
                          class="aspect-square w-full object-cover group-hover:opacity-75"
                        />
                        <div class="absolute inset-0 flex flex-col justify-end">
                          <div class="bg-background/60 p-4 text-sm">
                            <a :href="item.href" class="font-medium" @click="mobileOpen = false">
                              <span aria-hidden="true" class="absolute inset-0" />
                              {{ item.name }}
                            </a>
                            <p aria-hidden="true" class="mt-0.5 text-muted-foreground">Shop now</p>
                          </div>
                        </div>
                      </div>
                    </div>

                    <!-- Link Sections -->
                    <div class="space-y-10">
                      <div v-for="section in cat.sections" :key="section.name">
                        <p class="font-medium">{{ section.name }}</p>
                        <ul role="list" class="mt-6 flex flex-col space-y-6">
                          <li v-for="link in section.items" :key="link.name" class="flow-root">
                            <a :href="link.href" class="-m-2 block p-2 text-muted-foreground" @click="mobileOpen = false">
                              {{ link.name }}
                            </a>
                          </li>
                        </ul>
                      </div>
                    </div>
                  </TabsContent>
                </Tabs>

                <!-- Mobile Pages -->
                <div class="space-y-6 border-t px-4 py-6">
                  <div v-for="page in pages" :key="page.name" class="flow-root">
                    <a :href="page.href" class="-m-2 block p-2 font-medium" @click="mobileOpen = false">
                      {{ page.name }}
                    </a>
                  </div>
                </div>

                <!-- Mobile Currency -->
                <div v-if="currency" class="border-t px-4 py-6">
                  <a href="#" class="-m-2 flex items-center p-2">
                    <img v-if="currencyFlag" :src="currencyFlag" alt="" class="block h-auto w-5 shrink-0" />
                    <span class="ml-3 block text-sm font-medium">{{ currency }}</span>
                    <span class="sr-only">, change currency</span>
                  </a>
                </div>
              </SheetContent>
            </Sheet>

            <!-- Mobile Search -->
            <Button variant="ghost" icon class="ml-2" aria-label="Search">
              <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="m21 21-5.197-5.197m0 0A7.5 7.5 0 1 0 5.196 5.196a7.5 7.5 0 0 0 10.607 10.607Z" /></svg>
            </Button>
          </div>

            <!-- Desktop Mega Menu (left side) -->
            <div class="hidden lg:flex lg:flex-1 lg:self-stretch">
              <MegaMenuList class="gap-8">
              <MegaMenuItem v-for="cat in categories" :key="cat.name">
                <MegaMenuTrigger
                  class="relative duration-200 ease-out hover:text-foreground/80 data-[state=closed]:text-foreground/70 data-[state=open]:text-foreground"
                >
                  {{ cat.name }}
                  <!-- Active indicator line -->
                  <span
                    aria-hidden="true"
                    class="absolute inset-x-0 -bottom-px z-30 h-0.5 transition-colors duration-200 ease-in group-data-[state=closed]:bg-transparent group-data-[state=open]:bg-primary"
                  />
                </MegaMenuTrigger>

                <MegaMenuContent class="text-sm text-muted-foreground">
                  <!-- Bottom shadow overlay -->
                  <div aria-hidden="true" class="absolute inset-0 top-1/2 bg-background shadow-sm" />
                  <div class="relative bg-background">
                    <div class="mx-auto max-w-7xl px-8">
                      <div class="grid grid-cols-2 gap-x-8 gap-y-10 py-16">
                        <!-- Featured Images (left column) -->
                        <div class="grid grid-cols-2 grid-rows-1 gap-8 text-sm">
                          <template v-for="(item, i) in cat.featured" :key="item.name">
                            <div
                              class="group relative overflow-hidden rounded-md bg-muted"
                              :class="{ 'col-span-2': item.wide || i === 0 }"
                            >
                              <img
                                :src="item.imageSrc"
                                :alt="item.imageAlt"
                                class="w-full object-cover group-hover:opacity-75"
                                :class="item.wide || i === 0 ? 'aspect-[2/1]' : 'aspect-square'"
                              />
                              <div class="absolute inset-0 flex flex-col justify-end">
                                <div class="bg-background/60 p-4 text-sm">
                                  <MegaMenuLink as-child>
                                    <a :href="item.href" class="font-medium text-foreground">
                                      <span aria-hidden="true" class="absolute inset-0" />
                                      {{ item.name }}
                                    </a>
                                  </MegaMenuLink>
                                  <p aria-hidden="true" class="mt-0.5 text-muted-foreground">Shop now</p>
                                </div>
                              </div>
                            </div>
                          </template>
                        </div>

                        <!-- Link Sections (right column, 3 sub-columns) -->
                        <div class="grid grid-cols-3 gap-x-8 gap-y-10 text-sm text-muted-foreground">
                          <div v-for="(col, ci) in groupSections(cat.sections)" :key="ci" class="space-y-10">
                            <div v-for="section in col" :key="section.name">
                              <p class="font-medium text-foreground">{{ section.name }}</p>
                              <ul role="list" class="mt-4 space-y-4">
                                <li v-for="link in section.items" :key="link.name" class="flex">
                                  <MegaMenuLink as-child>
                                    <a :href="link.href" class="hover:text-foreground">
                                      {{ link.name }}
                                    </a>
                                  </MegaMenuLink>
                                </li>
                              </ul>
                            </div>
                          </div>
                        </div>
                      </div>
                    </div>
                  </div>
                </MegaMenuContent>
              </MegaMenuItem>

              <!-- Simple Pages -->
              <MegaMenuItem v-for="page in pages" :key="page.name" class="flex">
                <MegaMenuLink as-child>
                  <a
                    :href="page.href"
                    class="flex items-center text-sm font-medium text-foreground/70 hover:text-foreground/80"
                  >
                    {{ page.name }}
                  </a>
                </MegaMenuLink>
              </MegaMenuItem>
              </MegaMenuList>
            </div>

          <!-- Logo (centered) -->
          <a href="#" class="flex">
            <span v-if="!logoSrc" class="text-xl font-bold tracking-tight">{{ logoText }}</span>
            <img v-else :src="logoSrc" alt="" class="h-8 w-auto" />
          </a>

          <!-- Right Actions -->
          <div class="flex flex-1 items-center justify-end">
            <!-- Currency (Desktop) -->
            <a v-if="currency" href="#" class="hidden text-foreground/70 hover:text-foreground/80 lg:flex lg:items-center">
              <img v-if="currencyFlag" :src="currencyFlag" alt="" class="block h-auto w-5 shrink-0" />
              <span class="ml-3 block text-sm font-medium">{{ currency }}</span>
              <span class="sr-only">, change currency</span>
            </a>

            <!-- Search (Desktop) -->
            <Button variant="ghost" icon class="ml-6 hidden lg:flex" aria-label="Search">
              <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="m21 21-5.197-5.197m0 0A7.5 7.5 0 1 0 5.196 5.196a7.5 7.5 0 0 0 10.607 10.607Z" /></svg>
            </Button>

            <!-- Account -->
            <Button variant="ghost" icon class="lg:ml-4" aria-label="Account">
              <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M15.75 6a3.75 3.75 0 1 1-7.5 0 3.75 3.75 0 0 1 7.5 0ZM4.501 20.118a7.5 7.5 0 0 1 14.998 0A17.933 17.933 0 0 1 12 21.75c-2.676 0-5.216-.584-7.499-1.632Z" /></svg>
            </Button>

            <!-- Cart -->
            <div class="ml-4 flow-root lg:ml-6">
              <a href="#" class="group -m-2 flex items-center p-2">
                <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" class="shrink-0 text-muted-foreground group-hover:text-foreground/80"><path d="M15.75 10.5V6a3.75 3.75 0 1 0-7.5 0v4.5m11.356-1.993 1.263 12c.07.665-.45 1.243-1.119 1.243H4.25a1.125 1.125 0 0 1-1.12-1.243l1.264-12A1.125 1.125 0 0 1 5.513 7.5h12.974c.576 0 1.059.435 1.119 1.007ZM8.625 10.5a.375.375 0 1 1-.75 0 .375.375 0 0 1 .75 0Zm7.5 0a.375.375 0 1 1-.75 0 .375.375 0 0 1 .75 0Z" /></svg>
                <span class="ml-2 text-sm font-medium text-muted-foreground group-hover:text-foreground/80">{{ cartCount }}</span>
                <span class="sr-only">items in cart, view bag</span>
              </a>
            </div>
          </div>
        </div>
      </div>
      </nav>

      <!-- Full-width flyout viewport -->
      <MegaMenuViewport class="z-50" />
    </MegaMenu>
  </header>
</template>
Store navigation with centered logo, image overlay mega-menu, and mobile drawer.
store-nav-simple