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
<template>
<footer class="w-full border-t">
<div class="container mx-auto flex flex-col items-center justify-between gap-4 px-4 py-8 md:flex-row md:px-6">
<!-- Logo + Copyright -->
<div class="flex items-center gap-3">
<a href="#" class="text-lg font-bold tracking-tight">STORE</a>
<span class="text-sm text-muted-foreground">© {{ new Date().getFullYear() }}</span>
</div>
<!-- Links -->
<nav class="flex flex-wrap items-center gap-6">
<a href="#" class="text-sm text-muted-foreground transition-colors hover:text-foreground">Shop</a>
<a href="#" class="text-sm text-muted-foreground transition-colors hover:text-foreground">About</a>
<a href="#" class="text-sm text-muted-foreground transition-colors hover:text-foreground">Help</a>
<a href="#" class="text-sm text-muted-foreground transition-colors hover:text-foreground">Privacy</a>
<a href="#" class="text-sm text-muted-foreground transition-colors hover:text-foreground">Terms</a>
</nav>
<!-- Social -->
<div class="flex items-center gap-3">
<a href="#" class="text-muted-foreground transition-colors hover:text-foreground" aria-label="Instagram">
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="20" height="20" x="2" y="2" rx="5" ry="5" /><path d="M16 11.37A4 4 0 1 1 12.63 8 4 4 0 0 1 16 11.37z" /><line x1="17.5" x2="17.51" y1="6.5" y2="6.5" /></svg>
</a>
<a href="#" class="text-muted-foreground transition-colors hover:text-foreground" aria-label="X">
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M4 4l11.733 16h4.267l-11.733 -16h-4.267z" /><path d="M4 20l6.768 -6.768m2.46 -2.46l6.772 -6.772" /></svg>
</a>
<a href="#" class="text-muted-foreground transition-colors hover:text-foreground" aria-label="Facebook">
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M18 2h-3a5 5 0 0 0-5 5v3H7v4h3v8h4v-8h3l1-4h-4V7a1 1 0 0 1 1-1h3z" /></svg>
</a>
</div>
</div>
</footer>
</template>
Minimal footer with logo, copyright, and inline links.
footer-minimal
Files
<script setup lang="ts">
import { Button } from '@/components/ui/button'
const footerLinks = [
{
title: 'Shop',
links: [
{ label: 'Women', href: '#' },
{ label: 'Men', href: '#' },
{ label: 'Kids', href: '#' },
{ label: 'Accessories', href: '#' },
{ label: 'New Arrivals', href: '#' },
{ label: 'Sale', href: '#' },
],
},
{
title: 'Help',
links: [
{ label: 'Customer Service', href: '#' },
{ label: 'Shipping & Returns', href: '#' },
{ label: 'Size Guide', href: '#' },
{ label: 'FAQ', href: '#' },
{ label: 'Contact Us', href: '#' },
],
},
{
title: 'Company',
links: [
{ label: 'About Us', href: '#' },
{ label: 'Careers', href: '#' },
{ label: 'Press', href: '#' },
{ label: 'Sustainability', href: '#' },
],
},
]
</script>
<template>
<footer class="w-full">
<!-- Main Footer -->
<div class="bg-muted">
<div class="container mx-auto grid gap-8 px-4 py-12 md:grid-cols-2 md:px-6 lg:grid-cols-5">
<!-- Newsletter -->
<div class="lg:col-span-2">
<h3 class="text-lg font-semibold">
Stay in the loop
</h3>
<p class="mt-1 text-sm text-muted-foreground">
Subscribe to get special offers, new arrivals, and style tips.
</p>
<form class="mt-4 flex gap-2" @submit.prevent>
<input
type="email"
placeholder="Enter your email"
class="h-10 flex-1 rounded-md border bg-background px-3 text-sm placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-ring"
/>
<Button type="submit">
Subscribe
</Button>
</form>
<!-- Social Icons -->
<div class="mt-6 flex gap-3">
<a href="#" class="text-muted-foreground transition-colors hover:text-foreground" aria-label="Instagram">
<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"><rect width="20" height="20" x="2" y="2" rx="5" ry="5" /><path d="M16 11.37A4 4 0 1 1 12.63 8 4 4 0 0 1 16 11.37z" /><line x1="17.5" x2="17.51" y1="6.5" y2="6.5" /></svg>
</a>
<a href="#" class="text-muted-foreground transition-colors hover:text-foreground" aria-label="X">
<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="M4 4l11.733 16h4.267l-11.733 -16h-4.267z" /><path d="M4 20l6.768 -6.768m2.46 -2.46l6.772 -6.772" /></svg>
</a>
<a href="#" class="text-muted-foreground transition-colors hover:text-foreground" aria-label="Facebook">
<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="M18 2h-3a5 5 0 0 0-5 5v3H7v4h3v8h4v-8h3l1-4h-4V7a1 1 0 0 1 1-1h3z" /></svg>
</a>
<a href="#" class="text-muted-foreground transition-colors hover:text-foreground" aria-label="Pinterest">
<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="12" x2="12" y1="17" y2="22" /><path d="M5 17h14v-1.76a2 2 0 0 0-1.11-1.79l-1.78-.9A2 2 0 0 1 15 10.76V6h1a2 2 0 0 0 0-4H8a2 2 0 0 0 0 4h1v4.76a2 2 0 0 1-1.11 1.79l-1.78.9A2 2 0 0 0 5 15.24Z" /></svg>
</a>
</div>
</div>
<!-- Link Columns -->
<div
v-for="group in footerLinks"
:key="group.title"
class="space-y-3"
>
<h3 class="text-sm font-semibold">
{{ group.title }}
</h3>
<ul class="space-y-2">
<li v-for="link in group.links" :key="link.label">
<a
:href="link.href"
class="text-sm text-muted-foreground transition-colors hover:text-foreground"
>
{{ link.label }}
</a>
</li>
</ul>
</div>
</div>
</div>
<!-- Legal Bar -->
<div class="bg-foreground text-background">
<div class="container mx-auto flex flex-col items-center justify-between gap-4 px-4 py-6 text-sm md:flex-row md:px-6">
<div class="flex items-center gap-2">
<span class="font-bold">STORE</span>
<span class="text-background/70">© {{ new Date().getFullYear() }} All rights reserved.</span>
</div>
<div class="flex flex-wrap items-center gap-4 text-background/70">
<a href="#" class="transition-colors hover:text-background">Privacy Policy</a>
<a href="#" class="transition-colors hover:text-background">Terms of Service</a>
<a href="#" class="transition-colors hover:text-background">Cookie Settings</a>
</div>
</div>
</div>
</footer>
</template>
Rich footer with newsletter, link columns, social icons, and legal bar.
footer-rich
Files
<template>
<footer class="w-full border-t">
<!-- Trust Badges -->
<div class="border-b">
<div class="container mx-auto grid grid-cols-2 gap-6 px-4 py-8 md:grid-cols-4 md:px-6">
<div class="flex items-center gap-3">
<div class="flex size-10 shrink-0 items-center justify-center rounded-full bg-muted">
<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="M5 18H3a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h3.19M15 6h2a2 2 0 0 1 2 2v8a2 2 0 0 1-2 2h-3.19" /><line x1="23" x2="23" y1="13" y2="11" /><path d="M11 6l-4 6h6l-4 6" /></svg>
</div>
<div>
<p class="text-sm font-medium">Free Shipping</p>
<p class="text-xs text-muted-foreground">On orders over 50 €</p>
</div>
</div>
<div class="flex items-center gap-3">
<div class="flex size-10 shrink-0 items-center justify-center rounded-full bg-muted">
<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="M21 12V7H5a2 2 0 0 1 0-4h14v4" /><path d="M3 5v14a2 2 0 0 0 2 2h16v-5" /><path d="M18 12a2 2 0 0 0 0 4h4v-4Z" /></svg>
</div>
<div>
<p class="text-sm font-medium">Secure Payment</p>
<p class="text-xs text-muted-foreground">SSL encrypted checkout</p>
</div>
</div>
<div class="flex items-center gap-3">
<div class="flex size-10 shrink-0 items-center justify-center rounded-full bg-muted">
<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="m7.5 4.27 9 5.15" /><path d="M21 8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16Z" /><path d="m3.3 7 8.7 5 8.7-5" /><path d="M12 22V12" /></svg>
</div>
<div>
<p class="text-sm font-medium">Easy Returns</p>
<p class="text-xs text-muted-foreground">30-day return policy</p>
</div>
</div>
<div class="flex items-center gap-3">
<div class="flex size-10 shrink-0 items-center justify-center rounded-full bg-muted">
<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="M18 18.72a9.094 9.094 0 0 0 3.741-.479 3 3 0 0 0-4.682-2.72m.94 3.198.001.031c0 .225-.012.447-.037.666A11.944 11.944 0 0 1 12 21c-2.17 0-4.207-.576-5.963-1.584A6.062 6.062 0 0 1 6 18.719m12 0a5.971 5.971 0 0 0-.941-3.197m0 0A5.995 5.995 0 0 0 12 12.75a5.995 5.995 0 0 0-5.058 2.772m0 0a3 3 0 0 0-4.681 2.72 8.986 8.986 0 0 0 3.74.477m.94-3.197a5.971 5.971 0 0 0-.94 3.197M15 6.75a3 3 0 1 1-6 0 3 3 0 0 1 6 0Zm6 3a2.25 2.25 0 1 1-4.5 0 2.25 2.25 0 0 1 4.5 0Zm-13.5 0a2.25 2.25 0 1 1-4.5 0 2.25 2.25 0 0 1 4.5 0Z" /></svg>
</div>
<div>
<p class="text-sm font-medium">Customer Support</p>
<p class="text-xs text-muted-foreground">Mon–Fri, 9am–5pm</p>
</div>
</div>
</div>
</div>
<!-- Payment Methods + Links -->
<div class="container mx-auto flex flex-col items-center gap-6 px-4 py-8 md:flex-row md:justify-between md:px-6">
<!-- Payment Icons (simplified placeholders) -->
<div class="flex flex-wrap items-center gap-3">
<div class="flex h-8 w-12 items-center justify-center rounded border bg-background text-[10px] font-bold text-muted-foreground">
VISA
</div>
<div class="flex h-8 w-12 items-center justify-center rounded border bg-background text-[10px] font-bold text-muted-foreground">
MC
</div>
<div class="flex h-8 w-12 items-center justify-center rounded border bg-background text-[10px] font-bold text-muted-foreground">
AMEX
</div>
<div class="flex h-8 w-12 items-center justify-center rounded border bg-background text-[10px] font-bold text-muted-foreground">
PayPal
</div>
<div class="flex h-8 w-12 items-center justify-center rounded border bg-background text-[10px] font-bold text-muted-foreground">
Klarna
</div>
</div>
<!-- Legal Links -->
<div class="flex flex-wrap items-center gap-4 text-sm text-muted-foreground">
<span>© {{ new Date().getFullYear() }} STORE</span>
<a href="#" class="transition-colors hover:text-foreground">Privacy</a>
<a href="#" class="transition-colors hover:text-foreground">Terms</a>
<a href="#" class="transition-colors hover:text-foreground">Imprint</a>
</div>
</div>
</footer>
</template>
Trust-focused footer with payment icons, trust badges, and legal links.
footer-trust
Files
<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
<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
<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
<script lang="ts">
interface FooterLinkGroup {
title: string
links: { label: string; href: string }[]
}
interface SocialLink {
label: string
href: string
icon: 'instagram' | 'x' | 'facebook' | 'youtube' | 'pinterest' | 'tiktok'
}
</script>
<script setup lang="ts">
import { Button } from '@/components/ui/button'
import { Separator } from '@/components/ui/separator'
withDefaults(defineProps<{
/** Brand name displayed in the footer */
brandName?: string
/** Brand logo src. When set, overrides brandName text. */
logoSrc?: string
/** Short brand description */
tagline?: string
/** Link groups displayed as columns */
linkGroups?: FooterLinkGroup[]
/** Newsletter heading */
newsletterHeading?: string
/** Newsletter description */
newsletterDescription?: string
/** Social media links */
socialLinks?: SocialLink[]
/** Copyright text. Use {year} placeholder for current year. */
copyright?: string
/** Legal links shown in the copyright bar */
legalLinks?: { label: string; href: string }[]
}>(), {
brandName: 'STORE',
logoSrc: '',
tagline: 'Crafted with care. Worn with confidence.',
linkGroups: () => [
{
title: 'Shop',
links: [
{ label: 'Women', href: '#' },
{ label: 'Men', href: '#' },
{ label: 'Kids', href: '#' },
{ label: 'Accessories', href: '#' },
{ label: 'New Arrivals', href: '#' },
{ label: 'Sale', href: '#' },
],
},
{
title: 'Company',
links: [
{ label: 'About Us', href: '#' },
{ label: 'Careers', href: '#' },
{ label: 'Press', href: '#' },
{ label: 'Sustainability', href: '#' },
{ label: 'Blog', href: '#' },
],
},
{
title: 'Support',
links: [
{ label: 'Customer Service', href: '#' },
{ label: 'Shipping & Returns', href: '#' },
{ label: 'Size Guide', href: '#' },
{ label: 'FAQ', href: '#' },
{ label: 'Contact Us', href: '#' },
],
},
{
title: 'Legal',
links: [
{ label: 'Privacy Policy', href: '#' },
{ label: 'Terms of Service', href: '#' },
{ label: 'Cookie Policy', href: '#' },
{ label: 'Accessibility', href: '#' },
],
},
],
newsletterHeading: 'Subscribe to our newsletter',
newsletterDescription: 'Be the first to know about new collections, exclusive offers, and style tips.',
socialLinks: () => [
{ label: 'Instagram', href: '#', icon: 'instagram' },
{ label: 'X', href: '#', icon: 'x' },
{ label: 'Facebook', href: '#', icon: 'facebook' },
{ label: 'YouTube', href: '#', icon: 'youtube' },
],
copyright: '© {year} STORE. All rights reserved.',
legalLinks: () => [
{ label: 'Privacy Policy', href: '#' },
{ label: 'Terms of Service', href: '#' },
{ label: 'Cookie Settings', href: '#' },
],
})
const currentYear = new Date().getFullYear()
function formatCopyright(text: string) {
return text.replace('{year}', String(currentYear))
}
const socialIcons: Record<string, string> = {
instagram: 'M7.8 2h8.4C19.4 2 22 4.6 22 7.8v8.4a5.8 5.8 0 0 1-5.8 5.8H7.8C4.6 22 2 19.4 2 16.2V7.8A5.8 5.8 0 0 1 7.8 2m-.2 2A3.6 3.6 0 0 0 4 7.6v8.8C4 18.39 5.61 20 7.6 20h8.8a3.6 3.6 0 0 0 3.6-3.6V7.6C20 5.61 18.39 4 16.4 4H7.6m9.65 1.5a1.25 1.25 0 0 1 1.25 1.25A1.25 1.25 0 0 1 17.25 8 1.25 1.25 0 0 1 16 6.75a1.25 1.25 0 0 1 1.25-1.25M12 7a5 5 0 0 1 5 5 5 5 0 0 1-5 5 5 5 0 0 1-5-5 5 5 0 0 1 5-5m0 2a3 3 0 0 0-3 3 3 3 0 0 0 3 3 3 3 0 0 0 3-3 3 3 0 0 0-3-3z',
x: 'M4 4l11.733 16h4.267l-11.733-16zM4 20l6.768-6.768m2.46-2.46L20 4',
facebook: 'M18 2h-3a5 5 0 0 0-5 5v3H7v4h3v8h4v-8h3l1-4h-4V7a1 1 0 0 1 1-1h3z',
youtube: 'M2.5 17a24.12 24.12 0 0 1 0-10 2 2 0 0 1 1.4-1.4 49.56 49.56 0 0 1 16.2 0A2 2 0 0 1 21.5 7a24.12 24.12 0 0 1 0 10 2 2 0 0 1-1.4 1.4 49.55 49.55 0 0 1-16.2 0A2 2 0 0 1 2.5 17M10 15l5-3-5-3z',
pinterest: 'M12 2a10 10 0 0 0-3.64 19.33c-.1-.94-.2-2.4 0-3.43l1.45-6.15s-.37-.74-.37-1.84c0-1.72 1-3 2.24-3 1.06 0 1.57.8 1.57 1.75 0 1.06-.68 2.65-1.03 4.12-.29 1.23.62 2.24 1.84 2.24 2.21 0 3.9-2.33 3.9-5.68 0-2.97-2.13-5.04-5.18-5.04-3.53 0-5.6 2.64-5.6 5.38 0 1.06.41 2.2.92 2.82.1.12.11.23.08.35l-.34 1.4c-.06.23-.18.28-.42.17-1.56-.73-2.54-3-2.54-4.82C4.33 7.12 7.53 4.2 12.28 4.2c3.78 0 6.72 2.7 6.72 6.3 0 3.76-2.37 6.78-5.66 6.78-1.1 0-2.14-.58-2.5-1.26l-.68 2.6c-.25.95-.92 2.14-1.37 2.86A10 10 0 0 0 12 2z',
tiktok: 'M9 12a4 4 0 1 0 4 4V4a5 5 0 0 0 5 5',
}
</script>
<template>
<footer class="w-full bg-muted/50">
<div class="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8">
<!-- Main Footer Content -->
<div class="grid gap-8 py-12 lg:grid-cols-6 lg:gap-12 lg:py-16">
<!-- Brand + Newsletter — spans 2 cols -->
<div class="lg:col-span-2">
<!-- Brand -->
<a href="#" class="inline-block">
<span v-if="!logoSrc" class="text-xl font-bold tracking-tight">{{ brandName }}</span>
<img v-else :src="logoSrc" alt="" class="h-8 w-auto" />
</a>
<p v-if="tagline" class="mt-2 text-sm text-muted-foreground">
{{ tagline }}
</p>
<!-- Newsletter -->
<div class="mt-8">
<h3 class="text-sm font-semibold">
{{ newsletterHeading }}
</h3>
<p class="mt-1 text-sm text-muted-foreground">
{{ newsletterDescription }}
</p>
<form class="mt-4 flex gap-2" @submit.prevent>
<input
type="email"
placeholder="Enter your email"
class="h-10 flex-1 rounded-md border bg-background px-3 text-sm placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-ring"
/>
<Button type="submit">
Subscribe
</Button>
</form>
</div>
<!-- Social Icons -->
<div v-if="socialLinks.length" class="mt-6 flex gap-3">
<a
v-for="social in socialLinks"
:key="social.label"
:href="social.href"
:aria-label="social.label"
class="text-muted-foreground transition-colors hover:text-foreground"
>
<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="socialIcons[social.icon]" />
</svg>
</a>
</div>
</div>
<!-- Link Columns — 4 cols -->
<div class="grid grid-cols-2 gap-8 sm:grid-cols-4 lg:col-span-4">
<div
v-for="group in linkGroups"
:key="group.title"
class="space-y-3"
>
<h3 class="text-sm font-semibold">
{{ group.title }}
</h3>
<ul class="space-y-2.5">
<li v-for="link in group.links" :key="link.label">
<a
:href="link.href"
class="text-sm text-muted-foreground transition-colors hover:text-foreground"
>
{{ link.label }}
</a>
</li>
</ul>
</div>
</div>
</div>
<Separator />
<!-- Copyright Bar -->
<div class="flex flex-col items-center justify-between gap-4 py-6 text-sm sm:flex-row">
<p class="text-muted-foreground">
{{ formatCopyright(copyright) }}
</p>
<div v-if="legalLinks.length" class="flex flex-wrap items-center gap-4">
<a
v-for="link in legalLinks"
:key="link.label"
:href="link.href"
class="text-muted-foreground transition-colors hover:text-foreground"
>
{{ link.label }}
</a>
</div>
</div>
</div>
</footer>
</template>
Multi-column store footer with 4 link sections, newsletter signup, social icons, and copyright bar.
store-footer
Files
<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
<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
<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