Navigation
Column Navmenu
Site navigation with a drop-down panel of columns: every link carries a line of explanation, so a section says what is inside it.
- navmenu
- mega menu
- popover
- site nav
Preview
Use it with AI
- 1. Copy the link.
- 2. Write to your agent in your own words and drop the link into the sentence.
- 3. The agent opens the link and installs the component from the registry.
put this in the header: https://vibeui.ru/c/navmenu-001?lang=en
Show the full instructions
What the link says, spelled out. Use it when your agent cannot open links — paste this text instead.
# Install and place "navmenu-001" (Column Navmenu) from VibeUI
## 1. Install first — do not skip, do not recreate
Run this exact command before writing any code:
npx shadcn@latest add https://vibeui.ru/r/navmenu-001.json
Registry item: https://vibeui.ru/r/navmenu-001.json
Installs to: components/vibeui/navmenu-001.tsx (the exact path follows this project's components.json aliases).
npm dependencies: none.
Registry dependencies: none.
Install it from the registry. Do not recreate it from the description,
do not substitute a similar component from another library, and do not
rewrite it to match the project's existing style.
## 2. What it is
Site navigation with a drop-down panel of columns: every link carries a line of explanation, so a section says what is inside it.
Site navigation with a wide drop-down panel: columns of links, each with a line of explanation. The panel runs on the HTML popover and anchors to the nav bar, so Escape and outside clicks come from the browser. Zero dependencies, one file, no client JS.
## 3. How to use it
import { Navmenu001 } from "@/components/vibeui/navmenu-001"
<Navmenu001
entries={[
{
label: "Product",
columns: [
{ title: "Tools", links: [{ label: "Editor", hint: "Blocks and grid" }] },
],
},
{ label: "Pricing", href: "/pricing" },
]}
/>
Read the installed file for the full prop list.
## 4. Where to place it
This is a small inline component. Put it exactly where the user asked,
inside the existing markup. Do not create a new page, section or
wrapper for it. If a similar control already sits in that spot,
replace it instead of adding a second one.
Placement: ___
(The user fills this line in. If it is still blank, ask where to put it
instead of guessing.)
## 5. Keep exactly as installed
- the local --vibeui-navmenu-001-* palette — do not swap it for your theme tokens (bg-popover, text-muted-foreground and the like)
- the hint under every link: a section name in a mega menu almost never explains what is inside
- anchoring to the whole bar rather than one button: a wide panel tied to a button slides off the window edge
- popover="auto": Escape, outside clicks and closing a neighbouring panel come from the browser
- auto-fit with minmax in the columns — a narrow panel rearranges itself without a second rule set
- plain links for sections without a panel: a button that merely navigates breaks opening in a new tab
## 6. You may change
- the entries array: sections, their columns, links and hints
- the panel width and the minimum column width
- the accent through the accent prop — it colours the focus rings
- the height of the nav bar
## 7. Rules
- The panel does not open on hover, only on click: a mega menu unfolding as the cursor passes annoys more than one extra click.
- Without anchor positioning support the panel opens centred: check your target browsers.
- Do not go past three columns: the list stops reading and turns into a wall of links.
- Do not lift the CSS variables into globals.css: the component has to stay a single file.
## 8. Verify
- it renders with no console errors;
- it looks like the preview on the VibeUI page you copied this from;
- if it does not, you changed something listed in section 5 — put it back.For developers
npx shadcn@latest add https://vibeui.ru/r/navmenu-001.jsonhttps://vibeui.ru/r/navmenu-001.jsonКомпонент самодостаточен: один файл, без зависимостей, собственная палитра в локальных переменных --vibeui-navmenu-001-*. Серверный: хуков нет, панель открывает popover="auto". Якорем служит вся полоса навигации (anchor-name на [data-part="bar"]), поэтому широкая панель встаёт под полосой целиком, а не под отдельной кнопкой. Колонки раскладывает grid с auto-fit и minmax, так что их число подстраивается под ширину панели. Разделы без панели — обычные ссылки.
Component source
The same file your agent installs. Here in case you would rather copy it by hand.
import type { CSSProperties } from "react"
export type Navmenu001Link = {
label: string
hint?: string
href?: string
}
export type Navmenu001Column = {
title: string
links: Navmenu001Link[]
}
export type Navmenu001Entry = {
label: string
href?: string
columns?: Navmenu001Column[]
}
export type Navmenu001Props = {
entries?: Navmenu001Entry[]
accent?: string
className?: string
style?: CSSProperties
}
// Идея компонента: навигация сайта, где выпадающая панель — не список ссылок, а
// колонки с пояснениями: по подписи «Аналитика» непонятно, что внутри, по строке
// под ней — понятно. Панель открывает HTML popover, а якорем служит вся полоса
// навигации, поэтому панель встаёт под ней целиком, а не под отдельной кнопкой.
const STYLES = `
:where([data-vibeui-block="navmenu-001"]){
--vibeui-navmenu-001-bg:oklch(1 0 0);
--vibeui-navmenu-001-fg:oklch(0.22 0.014 265);
--vibeui-navmenu-001-muted:oklch(0.55 0.014 265);
--vibeui-navmenu-001-border:oklch(0.91 0.006 265);
--vibeui-navmenu-001-hover:oklch(0.55 0.02 265 / 8%);
--vibeui-navmenu-001-accent:oklch(0.55 0.2 262);
--vibeui-navmenu-001-font:ui-sans-serif,system-ui,-apple-system,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif;
}
[data-vibeui-block="navmenu-001"]{
box-sizing:border-box;width:100%;max-width:44rem;
font-family:var(--vibeui-navmenu-001-font);color:var(--vibeui-navmenu-001-fg);
}
[data-vibeui-block="navmenu-001"] [data-part="bar"]{
box-sizing:border-box;padding:0.375rem;
display:flex;align-items:center;gap:0.125rem;
background:var(--vibeui-navmenu-001-bg);
border:1px solid var(--vibeui-navmenu-001-border);border-radius:0.75rem;
anchor-name:--vibeui-navmenu-001-bar;
}
[data-vibeui-block="navmenu-001"] [data-part="trigger"],
[data-vibeui-block="navmenu-001"] [data-part="plain"]{
appearance:none;border:0;background:none;cursor:pointer;text-decoration:none;
display:inline-flex;align-items:center;gap:0.375rem;
height:2rem;padding:0 0.75rem;border-radius:0.5rem;
font:inherit;font-size:0.875rem;color:inherit;
transition:background-color .14s ease;
}
[data-vibeui-block="navmenu-001"] [data-part="trigger"]:hover,
[data-vibeui-block="navmenu-001"] [data-part="plain"]:hover{background:var(--vibeui-navmenu-001-hover)}
[data-vibeui-block="navmenu-001"] [data-part="trigger"]:focus-visible,
[data-vibeui-block="navmenu-001"] [data-part="plain"]:focus-visible{outline:2px solid var(--vibeui-navmenu-001-accent);outline-offset:-2px}
[data-vibeui-block="navmenu-001"] [data-part="caret"]{
width:0.375rem;height:0.375rem;margin-top:-0.1875rem;
border:1.5px solid var(--vibeui-navmenu-001-muted);border-left:0;border-top:0;
transform:rotate(45deg);transition:transform .16s ease;
}
[data-vibeui-block="navmenu-001"] [data-part="panel"]{
position:fixed;inset:auto;margin:0;
width:min(40rem,92vw);padding:1rem;box-sizing:border-box;
background:var(--vibeui-navmenu-001-bg);color:var(--vibeui-navmenu-001-fg);
border:1px solid var(--vibeui-navmenu-001-border);border-radius:0.875rem;
font-family:var(--vibeui-navmenu-001-font);
box-shadow:0 24px 48px -24px oklch(0.2 0.03 265 / 40%);
}
/* Якорь — вся полоса: панель широкая и не должна прыгать за отдельной кнопкой. */
@supports (anchor-name: --a){
[data-vibeui-block="navmenu-001"] [data-part="panel"]{
position-anchor:--vibeui-navmenu-001-bar;
position-area:bottom span-right;margin-top:0.5rem;
position-try-fallbacks:flip-block;
}
}
[data-vibeui-block="navmenu-001"] [data-part="columns"]{
display:grid;grid-template-columns:repeat(auto-fit,minmax(11rem,1fr));gap:1rem 1.5rem;
}
[data-vibeui-block="navmenu-001"] [data-part="title"]{
margin:0 0 0.5rem;font-size:0.6875rem;letter-spacing:0.06em;text-transform:uppercase;
color:var(--vibeui-navmenu-001-muted);
}
[data-vibeui-block="navmenu-001"] [data-part="list"]{margin:0;padding:0;list-style:none;display:grid;gap:0.125rem}
[data-vibeui-block="navmenu-001"] [data-part="link"]{
display:block;padding:0.4375rem 0.5rem;border-radius:0.5rem;text-decoration:none;color:inherit;
}
[data-vibeui-block="navmenu-001"] [data-part="link"]:hover{background:var(--vibeui-navmenu-001-hover)}
[data-vibeui-block="navmenu-001"] [data-part="link"]:focus-visible{outline:2px solid var(--vibeui-navmenu-001-accent);outline-offset:-2px}
[data-vibeui-block="navmenu-001"] [data-part="name"]{display:block;font-size:0.875rem;font-weight:550}
[data-vibeui-block="navmenu-001"] [data-part="hint"]{display:block;margin-top:0.125rem;font-size:0.75rem;line-height:1.45;color:var(--vibeui-navmenu-001-muted)}
@media (prefers-reduced-motion:reduce){[data-vibeui-block="navmenu-001"] *{animation:none!important;transition:none!important}}
`
const DEFAULT_ENTRIES: Navmenu001Entry[] = [
{
label: "Продукт",
columns: [
{
title: "Инструменты",
links: [
{ label: "Редактор", hint: "Собирайте страницы из готовых блоков" },
{ label: "Аналитика", hint: "Просмотры, источники и воронки" },
{ label: "Формы", hint: "Заявки падают в почту и в CRM" },
],
},
{
title: "Платформа",
links: [
{ label: "Домены", hint: "Свой адрес и сертификат за минуту" },
{ label: "Хранилище", hint: "Картинки и файлы рядом с сайтом" },
{ label: "Роли", hint: "Кто редактирует, а кто только смотрит" },
],
},
],
},
{
label: "Решения",
columns: [
{
title: "Для кого",
links: [
{ label: "Студиям", hint: "Десятки сайтов в одной панели" },
{ label: "Магазинам", hint: "Каталог, оплата и доставка" },
{ label: "Медиа", hint: "Поток материалов и авторы" },
],
},
],
},
{ label: "Цены", href: "#" },
{ label: "Документация", href: "#" },
]
/**
* Навигация сайта с выпадающей панелью из колонок ссылок с описаниями.
* Один файл, ноль зависимостей, собственная палитра, клиентского JS нет.
*/
export function Navmenu001({
entries = DEFAULT_ENTRIES,
accent,
className,
style,
}: Navmenu001Props) {
const palette = {
...(accent ? { "--vibeui-navmenu-001-accent": accent } : null),
...style,
} as CSSProperties
return (
<>
<style href="vibeui-navmenu-001" precedence="medium">
{STYLES}
</style>
<nav
data-vibeui-block="navmenu-001"
aria-label="Основная навигация"
className={className}
style={palette}
>
<div data-part="bar">
{entries.map((entry, index) => {
if (!entry.columns) {
return (
<a key={entry.label} data-part="plain" href={entry.href ?? "#"}>
{entry.label}
</a>
)
}
const id = `vibeui-navmenu-001-${index}`
return (
<span key={entry.label}>
<button
type="button"
data-part="trigger"
aria-haspopup="true"
popoverTarget={id}
>
{entry.label}
<span data-part="caret" aria-hidden="true" />
</button>
<div
id={id}
data-part="panel"
popover="auto"
aria-label={entry.label}
>
<div data-part="columns">
{entry.columns.map((column) => (
<div key={column.title}>
<p data-part="title">{column.title}</p>
<ul data-part="list">
{column.links.map((link) => (
<li key={link.label}>
<a data-part="link" href={link.href ?? "#"}>
<span data-part="name">{link.label}</span>
{link.hint ? (
<span data-part="hint">{link.hint}</span>
) : null}
</a>
</li>
))}
</ul>
</div>
))}
</div>
</div>
</span>
)
})}
</div>
</nav>
</>
)
}