DocsComponentsNavigation menu
Navigation menu
Site navigation with panels that open on hover and click.
Alpine.js6 files
<pb-ui.navigation-menu>
<pb-ui.navigation-menu-list>
<pb-ui.navigation-menu-item>
<pb-ui.navigation-menu-trigger>Getting started</pb-ui.navigation-menu-trigger>
<pb-ui.navigation-menu-content class="grid w-[400px] grid-cols-[.75fr_1fr] gap-2">
<a href="#" class="flex h-full flex-col justify-end rounded-md bg-gradient-to-b from-muted/50 to-muted p-4 no-underline outline-hidden">
<div class="mb-2 text-lg font-medium">PyBlade UI</div>
<p class="text-sm leading-tight text-muted-foreground">Components to copy into your project.</p>
</a>
<div class="grid gap-1">
<pb-ui.navigation-menu-link href="#"><div class="font-medium">Introduction</div><p class="text-muted-foreground">How the library works.</p></pb-ui.navigation-menu-link>
<pb-ui.navigation-menu-link href="#"><div class="font-medium">Installation</div><p class="text-muted-foreground">Set up a project.</p></pb-ui.navigation-menu-link>
</div>
</pb-ui.navigation-menu-content>
</pb-ui.navigation-menu-item>
<pb-ui.navigation-menu-item>
<pb-ui.navigation-menu-trigger>Components</pb-ui.navigation-menu-trigger>
<pb-ui.navigation-menu-content class="grid w-[440px] grid-cols-2 gap-1">
@for(c in ['Dialog', 'Sheet', 'Tabs', 'Command'])
<pb-ui.navigation-menu-link href="#"><div class="font-medium">{{ c }}</div><p class="text-muted-foreground">See the component.</p></pb-ui.navigation-menu-link>
@endfor
</pb-ui.navigation-menu-content>
</pb-ui.navigation-menu-item>
<pb-ui.navigation-menu-item>
<pb-ui.navigation-menu-link href="#" class="px-4 py-2 font-medium">Docs</pb-ui.navigation-menu-link>
</pb-ui.navigation-menu-item>
</pb-ui.navigation-menu-list>
</pb-ui.navigation-menu>Installation
pyblade-ui add navigation-menu- Install the dependencies
Alpine.js, loaded in your
<head>. - Copy these files into your projectcomponents/ui/navigation_menu.html
<nav data-slot="navigation-menu" x-data="{ active: null, timer: null, show(v) { clearTimeout(this.timer); this.active = v }, hide() { this.timer = setTimeout(() => this.active = null, 150) } }" x-on:keydown.escape="active = null" x-on:click.outside="active = null" x-on:mouseleave="hide()" {{ attributes.cn("relative flex max-w-max flex-1 items-center justify-center") }}>{{ slot }}</nav>components/ui/navigation_menu_list.html<ul data-slot="navigation-menu-list" {{ attributes.cn("group flex flex-1 list-none items-center justify-center gap-1") }}>{{ slot }}</ul>components/ui/navigation_menu_item.html@props({'value': ''}) <li data-slot="navigation-menu-item" x-data="{ value: '{{ value }}' || Math.random().toString(36).slice(2) }" {{ attributes.cn("relative") }}>{{ slot }}</li>components/ui/navigation_menu_trigger.html<button type="button" data-slot="navigation-menu-trigger" x-on:click="active === value ? (active = null) : show(value)" x-on:mouseenter="show(value)" x-bind:aria-expanded="active === value" x-bind:data-state="active === value ? 'open' : 'closed'" {{ attributes.cn("group inline-flex h-9 w-max cursor-pointer items-center justify-center rounded-md bg-background px-4 py-2 text-sm font-medium transition-[color,box-shadow] outline-none hover:bg-accent hover:text-accent-foreground focus:bg-accent focus:text-accent-foreground focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:pointer-events-none disabled:opacity-50 data-[state=open]:bg-accent/50 data-[state=open]:text-accent-foreground") }}>{{ slot }}<svg class="relative top-px ml-1 size-3 transition duration-300" x-bind:class="{ 'rotate-180': active === value }" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="m6 9 6 6 6-6"/></svg></button>components/ui/navigation_menu_content.html@props({'align': 'start'}) <div data-slot="navigation-menu-content" x-show="active === value" x-cloak x-on:mouseenter="show(value)" x-transition:enter="duration-150 ease-out" x-transition:enter-start="opacity-0 -translate-y-1" x-transition:enter-end="opacity-100 translate-y-0" {{ attributes.cn( "absolute top-full z-50 mt-1.5 w-max overflow-hidden rounded-md border bg-popover p-2 text-popover-foreground shadow-md", {'start': 'left-0', 'center': 'left-1/2 -translate-x-1/2', 'end': 'right-0'}[align], ) }}>{{ slot }}</div>components/ui/navigation_menu_link.html@props({'href': '#', 'active': False}) <a href="{{ href }}" data-slot="navigation-menu-link" data-active="{{ str(bool(active)).lower() }}" @if(active)aria-current="page"@endif {{ attributes.cn("flex flex-col gap-1 rounded-sm p-2 text-sm transition-all outline-none hover:bg-accent hover:text-accent-foreground focus:bg-accent focus:text-accent-foreground focus-visible:ring-[3px] focus-visible:ring-ring/50 data-[active=true]:bg-accent/50 data-[active=true]:text-accent-foreground [&_svg:not([class*='size-'])]:size-4") }}>{{ slot }}</a> - Update the path to fit your project
The
componentsfolder is the one set inpyblade.toml.
Usage
The tags <pb-ui.navigation-menu> <pb-ui.navigation-menu-list> <pb-ui.navigation-menu-item> <pb-ui.navigation-menu-trigger> <pb-ui.navigation-menu-content> <pb-ui.navigation-menu-link> are available in all your templates.
<pb-ui.navigation-menu>
<pb-ui.navigation-menu-list>
<pb-ui.navigation-menu-item>
<pb-ui.navigation-menu-trigger>Products</pb-ui.navigation-menu-trigger>
<pb-ui.navigation-menu-content class="w-64">
<pb-ui.navigation-menu-link href="/ui">UI kit</pb-ui.navigation-menu-link>
</pb-ui.navigation-menu-content>
</pb-ui.navigation-menu-item>
<pb-ui.navigation-menu-item>
<pb-ui.navigation-menu-link href="/docs">Docs</pb-ui.navigation-menu-link>
</pb-ui.navigation-menu-item>
</pb-ui.navigation-menu-list>
</pb-ui.navigation-menu>Props
<pb-ui.navigation-menu-item>
| Prop | Default |
|---|---|
| value | '' |
<pb-ui.navigation-menu-content>
| Prop | Default |
|---|---|
| align | 'start' |
<pb-ui.navigation-menu-link>
| Prop | Default |
|---|---|
| href | '#' |
| active | False |
Any other attribute (class, id, x-on:click, hx-post…) is passed to the root element; your Tailwind classes win over the component's.