Navigation menu
UI::NavigationMenuSite navigation with links and hover or click panels of rich links.
# VelvetKit
## Navigation menu (`UI::NavigationMenu`)
Site navigation with links and hover or click panels of rich links.
Add it: `bin/rails generate velvetkit:add navigation_menu`
Also adds: `icon`
Stimulus: `ui--navigation-menu`
Installs: `@floating-ui/dom`
### Arguments
```ruby
UI::NavigationMenu.new(label: nil, open_delay: 150, close_delay: 200, class: nil, **html_attrs)
```
Slots: `with_item`, `with_link`
### Notes
UI::NavigationMenu — a site's main navigation: plain links, and triggers
that open a panel of rich links (title, description, optional icon).
Follows the WAI-ARIA disclosure navigation pattern (buttons with
aria-expanded controlling a panel of ordinary links), not role="menu",
which is for application menus.
<%= render(UI::NavigationMenu.new) do |nav| %>
<% nav.with_item(label: "Getting started") do |item| %>
<% item.with_link(title: "Introduction", href: "/docs",
description: "Re-usable components built with Tailwind CSS.") %>
<% end %>
<% nav.with_item(label: "Docs", href: "/docs") %>
<% end %>
A panel opens on hover (mouse, after `open_delay` ms; instantly when
another panel is already open), on click, or with Enter/Space/ArrowDown.
Escape, a click outside or tabbing away closes it.
Compound:
UI::NavigationMenu — the <nav> and its list
UI::NavigationMenu::Item — a plain link, or a trigger plus its panel
UI::NavigationMenu::Link — a rich link inside a panel
### Example: Simple
```erb
<div class="flex min-h-96 justify-center pt-8">
<%= render(UI::NavigationMenu.new) do |nav| %>
<% nav.with_item(label: "Getting started") do |item| %>
<% item.with_link(title: "Introduction", href: "#introduction", description: "Re-usable components built with Tailwind CSS.") %>
<% item.with_link(title: "Installation", href: "#installation", description: "How to install dependencies and structure your app.") %>
<% item.with_link(title: "Typography", href: "#typography", description: "Styles for headings, paragraphs, lists…etc") %>
<% end %>
<% nav.with_item(label: "Components", columns: 2) do |item| %>
<% item.with_link(title: "Alert", href: "#alert", description: "A callout for important information.") %>
<% item.with_link(title: "Dialog", href: "#dialog", description: "A modal window on top of the page.") %>
<% item.with_link(title: "Menu", href: "#menu", description: "A list of actions behind a button.") %>
<% item.with_link(title: "Tabs", href: "#tabs", description: "Panels shown one at a time.") %>
<% item.with_link(title: "Toast", href: "#toast", description: "A short message that disappears on its own.") %>
<% item.with_link(title: "Tooltip", href: "#tooltip", description: "A label that appears on hover or focus.") %>
<% end %>
<% nav.with_item(label: "With Icon") do |item| %>
<% item.with_link(title: "Backlog", href: "#backlog", icon: "circle-alert") %>
<% item.with_link(title: "To do", href: "#todo", icon: "info") %>
<% item.with_link(title: "Done", href: "#done", icon: "circle-check") %>
<% end %>
<% nav.with_item(label: "Docs", href: "#docs") %>
<% end %>
</div>
```
### Example: Custom panel
```erb
<%# Any markup can go in a panel alongside, or instead of, its links. %>
<div class="flex min-h-[28rem] justify-center pt-8">
<%= render(UI::NavigationMenu.new) do |nav| %>
<% nav.with_item(label: "Company", panel_class: "w-[32rem]") do |item| %>
<% item.with_link(title: "About", href: "#about", description: "Who we are and why we build this.") %>
<% item.with_link(title: "Careers", href: "#careers", description: "Open roles, remote-first.") %>
<% item.with_link(title: "Customers", href: "#customers", description: "Teams that run on Acme, in their words.") %>
<% item.with_link(title: "Contact", href: "#contact", description: "Sales, support and partnerships.") %>
<div class="mt-2 flex items-center justify-between gap-4 rounded-md bg-muted p-4">
<div>
<p class="text-sm font-medium">We're hiring</p>
<p class="mt-1 text-sm text-muted-foreground">Three engineering roles are open this quarter.</p>
</div>
<%= render(UI::Button.new(size: :sm, variant: :outline)) { "See roles" } %>
</div>
<% end %>
<% nav.with_item(label: "Blog", href: "#blog") %>
<% nav.with_item(label: "Contact", href: "#contact") %>
<% end %>
</div>
```
bin/rails generate velvetkit:add navigation_menu
- Also adds
- Icon
- Stimulus
- ui--navigation-menu
- Installs
- @floating-ui/dom
Examples
Simple
simple.html.erb
<div class="flex min-h-96 justify-center pt-8">
<%= render(UI::NavigationMenu.new) do |nav| %>
<% nav.with_item(label: "Getting started") do |item| %>
<% item.with_link(title: "Introduction", href: "#introduction", description: "Re-usable components built with Tailwind CSS.") %>
<% item.with_link(title: "Installation", href: "#installation", description: "How to install dependencies and structure your app.") %>
<% item.with_link(title: "Typography", href: "#typography", description: "Styles for headings, paragraphs, lists…etc") %>
<% end %>
<% nav.with_item(label: "Components", columns: 2) do |item| %>
<% item.with_link(title: "Alert", href: "#alert", description: "A callout for important information.") %>
<% item.with_link(title: "Dialog", href: "#dialog", description: "A modal window on top of the page.") %>
<% item.with_link(title: "Menu", href: "#menu", description: "A list of actions behind a button.") %>
<% item.with_link(title: "Tabs", href: "#tabs", description: "Panels shown one at a time.") %>
<% item.with_link(title: "Toast", href: "#toast", description: "A short message that disappears on its own.") %>
<% item.with_link(title: "Tooltip", href: "#tooltip", description: "A label that appears on hover or focus.") %>
<% end %>
<% nav.with_item(label: "With Icon") do |item| %>
<% item.with_link(title: "Backlog", href: "#backlog", icon: "circle-alert") %>
<% item.with_link(title: "To do", href: "#todo", icon: "info") %>
<% item.with_link(title: "Done", href: "#done", icon: "circle-check") %>
<% end %>
<% nav.with_item(label: "Docs", href: "#docs") %>
<% end %>
</div>
Custom panel
custom_panel.html.erb
<%# Any markup can go in a panel alongside, or instead of, its links. %>
<div class="flex min-h-[28rem] justify-center pt-8">
<%= render(UI::NavigationMenu.new) do |nav| %>
<% nav.with_item(label: "Company", panel_class: "w-[32rem]") do |item| %>
<% item.with_link(title: "About", href: "#about", description: "Who we are and why we build this.") %>
<% item.with_link(title: "Careers", href: "#careers", description: "Open roles, remote-first.") %>
<% item.with_link(title: "Customers", href: "#customers", description: "Teams that run on Acme, in their words.") %>
<% item.with_link(title: "Contact", href: "#contact", description: "Sales, support and partnerships.") %>
<div class="mt-2 flex items-center justify-between gap-4 rounded-md bg-muted p-4">
<div>
<p class="text-sm font-medium">We're hiring</p>
<p class="mt-1 text-sm text-muted-foreground">Three engineering roles are open this quarter.</p>
</div>
<%= render(UI::Button.new(size: :sm, variant: :outline)) { "See roles" } %>
</div>
<% end %>
<% nav.with_item(label: "Blog", href: "#blog") %>
<% nav.with_item(label: "Contact", href: "#contact") %>
<% end %>
</div>
API
Arguments
UI::NavigationMenu.new(label: nil, open_delay: 150, close_delay: 200, class: nil, **html_attrs)
Slots:
with_item
with_link
Notes
UI::NavigationMenu — a site's main navigation: plain links, and triggers
that open a panel of rich links (title, description, optional icon).
Follows the WAI-ARIA disclosure navigation pattern (buttons with
aria-expanded controlling a panel of ordinary links), not role="menu",
which is for application menus.
<%= render(UI::NavigationMenu.new) do |nav| %>
<% nav.with_item(label: "Getting started") do |item| %>
<% item.with_link(title: "Introduction", href: "/docs",
description: "Re-usable components built with Tailwind CSS.") %>
<% end %>
<% nav.with_item(label: "Docs", href: "/docs") %>
<% end %>
A panel opens on hover (mouse, after `open_delay` ms; instantly when
another panel is already open), on click, or with Enter/Space/ArrowDown.
Escape, a click outside or tabbing away closes it.
Compound:
UI::NavigationMenu — the <nav> and its list
UI::NavigationMenu::Item — a plain link, or a trigger plus its panel
UI::NavigationMenu::Link — a rich link inside a panel