NavigationMenu
用法
Items
使用 items prop 作为对象数组,具有以下属性:
label?: stringicon?: stringavatar?: AvatarPropsbadge?: string | number | BadgePropstooltip?: TooltipPropstrailingIcon?: stringtype?: 'label' | 'trigger' | 'link'defaultOpen?: booleanopen?: booleanvalue?: stringdisabled?: booleanslot?: stringonSelect?(e: Event): voidchildren?: NavigationMenuChildItem[]class?: anyui?: { linkLeadingAvatarSize?: ClassNameValue, linkLeadingAvatar?: ClassNameValue, linkLeadingIcon?: ClassNameValue, linkLabel?: ClassNameValue, linkLabelExternalIcon?: ClassNameValue, linkTrailing?: ClassNameValue, linkTrailingBadgeSize?: ClassNameValue, linkTrailingBadge?: ClassNameValue, linkTrailingIcon?: ClassNameValue, label?: ClassNameValue, link?: ClassNameValue, content?: ClassNameValue, childList?: ClassNameValue, childLabel?: ClassNameValue, childItem?: ClassNameValue, childLink?: ClassNameValue, childLinkIcon?: ClassNameValue, childLinkWrapper?: ClassNameValue, childLinkLabel?: ClassNameValue, childLinkLabelExternalIcon?: ClassNameValue, childLinkDescription?: ClassNameValue }
你可以传递 Link 组件的任何属性,例如 to、target 等。
<script setup lang="ts">
import type { NavigationMenuItem } from '@nuxt/ui'
const items = ref<NavigationMenuItem[]>([
{
label: 'Guide',
icon: 'i-lucide-book-open',
to: '/getting-started',
children: [
{
label: 'Introduction',
description: 'Fully styled and customizable components for Nuxt.',
icon: 'i-lucide-house'
},
{
label: 'Installation',
description: 'Learn how to install and configure Nuxt UI in your application.',
icon: 'i-lucide-cloud-download'
},
{
label: 'Icons',
icon: 'i-lucide-smile',
description: 'You have nothing to do, @nuxt/icon will handle it automatically.'
},
{
label: 'Colors',
icon: 'i-lucide-swatch-book',
description: 'Choose a primary and a neutral color from your Tailwind CSS theme.'
},
{
label: 'Theme',
icon: 'i-lucide-cog',
description: 'You can customize components by using the `class` / `ui` props or in your app.config.ts.'
}
]
},
{
label: 'Composables',
icon: 'i-lucide-database',
to: '/composables',
children: [
{
label: 'defineShortcuts',
icon: 'i-lucide-file-text',
description: 'Define shortcuts for your application.',
to: '/composables/define-shortcuts'
},
{
label: 'useOverlay',
icon: 'i-lucide-file-text',
description: 'Display a modal/slideover within your application.',
to: '/composables/use-overlay'
},
{
label: 'useToast',
icon: 'i-lucide-file-text',
description: 'Display a toast within your application.',
to: '/composables/use-toast'
}
]
},
{
label: 'Components',
icon: 'i-lucide-box',
to: '/components',
active: true,
children: [
{
label: 'Link',
icon: 'i-lucide-file-text',
description: 'Use NuxtLink with superpowers.',
to: '/components/link'
},
{
label: 'Modal',
icon: 'i-lucide-file-text',
description: 'Display a modal within your application.',
to: '/components/modal'
},
{
label: 'NavigationMenu',
icon: 'i-lucide-file-text',
description: 'Display a list of links.',
to: '/components/navigation-menu'
},
{
label: 'Pagination',
icon: 'i-lucide-file-text',
description: 'Display a list of pages.',
to: '/components/pagination'
},
{
label: 'Popover',
icon: 'i-lucide-file-text',
description: 'Display a non-modal dialog that floats around a trigger element.',
to: '/components/popover'
},
{
label: 'Progress',
icon: 'i-lucide-file-text',
description: 'Show a horizontal bar to indicate task progression.',
to: '/components/progress'
}
]
},
{
label: 'GitHub',
icon: 'i-simple-icons-github',
badge: '3.8k',
to: 'https://github.com/nuxt/ui',
target: '_blank'
},
{
label: 'Help',
icon: 'i-lucide-circle-help',
disabled: true
}
])
</script>
<template>
<UNavigationMenu :items="items" class="w-full justify-center" />
</template>
items prop,以显示项目组。children 对象数组,其中包含以下属性来创建子菜单:label: stringdescription?: stringicon?: stringonSelect?(e: Event): voidclass?: any
方向 (Orientation)
使用 orientation prop 更改 NavigationMenu 的方向。
vertical 时,将使用 Accordion 组件来显示每个组。你可以使用 open 和 defaultOpen 属性控制每个项目的打开状态,并使用 collapsible 和 type props 更改行为。<script setup lang="ts">
import type { NavigationMenuItem } from '@nuxt/ui'
const items = ref<NavigationMenuItem[][]>([
[
{
label: 'Links',
type: 'label'
},
{
label: 'Guide',
icon: 'i-lucide-book-open',
children: [
{
label: 'Introduction',
description: 'Fully styled and customizable components for Nuxt.',
icon: 'i-lucide-house'
},
{
label: 'Installation',
description: 'Learn how to install and configure Nuxt UI in your application.',
icon: 'i-lucide-cloud-download'
},
{
label: 'Icons',
icon: 'i-lucide-smile',
description: 'You have nothing to do, @nuxt/icon will handle it automatically.'
},
{
label: 'Colors',
icon: 'i-lucide-swatch-book',
description: 'Choose a primary and a neutral color from your Tailwind CSS theme.'
},
{
label: 'Theme',
icon: 'i-lucide-cog',
description: 'You can customize components by using the `class` / `ui` props or in your app.config.ts.'
}
]
},
{
label: 'Composables',
icon: 'i-lucide-database',
children: [
{
label: 'defineShortcuts',
icon: 'i-lucide-file-text',
description: 'Define shortcuts for your application.',
to: '/composables/define-shortcuts'
},
{
label: 'useOverlay',
icon: 'i-lucide-file-text',
description: 'Display a modal/slideover within your application.',
to: '/composables/use-overlay'
},
{
label: 'useToast',
icon: 'i-lucide-file-text',
description: 'Display a toast within your application.',
to: '/composables/use-toast'
}
]
},
{
label: 'Components',
icon: 'i-lucide-box',
to: '/components',
active: true,
defaultOpen: true,
children: [
{
label: 'Link',
icon: 'i-lucide-file-text',
description: 'Use NuxtLink with superpowers.',
to: '/components/link'
},
{
label: 'Modal',
icon: 'i-lucide-file-text',
description: 'Display a modal within your application.',
to: '/components/modal'
},
{
label: 'NavigationMenu',
icon: 'i-lucide-file-text',
description: 'Display a list of links.',
to: '/components/navigation-menu'
},
{
label: 'Pagination',
icon: 'i-lucide-file-text',
description: 'Display a list of pages.',
to: '/components/pagination'
},
{
label: 'Popover',
icon: 'i-lucide-file-text',
description: 'Display a non-modal dialog that floats around a trigger element.',
to: '/components/popover'
},
{
label: 'Progress',
icon: 'i-lucide-file-text',
description: 'Show a horizontal bar to indicate task progression.',
to: '/components/progress'
}
]
}
],
[
{
label: 'GitHub',
icon: 'i-simple-icons-github',
badge: '3.8k',
to: 'https://github.com/nuxt/ui',
target: '_blank'
},
{
label: 'Help',
icon: 'i-lucide-circle-help',
disabled: true
}
]
])
</script>
<template>
<UNavigationMenu orientation="vertical" :items="items" class="data-[orientation=vertical]:w-48" />
</template>
orientation 为 horizontal 时,组之间会留有间距;当 orientation 为 vertical 时,组之间会分隔开。折叠 (Collapsed)
在 vertical 方向上,使用 collapsed prop 折叠 NavigationMenu,这在侧边栏中非常有用。
<script setup lang="ts">
import type { NavigationMenuItem } from '@nuxt/ui'
const items = ref<NavigationMenuItem[][]>([
[
{
label: 'Links',
type: 'label'
},
{
label: 'Guide',
icon: 'i-lucide-book-open',
children: [
{
label: 'Introduction',
description: 'Fully styled and customizable components for Nuxt.',
icon: 'i-lucide-house'
},
{
label: 'Installation',
description: 'Learn how to install and configure Nuxt UI in your application.',
icon: 'i-lucide-cloud-download'
},
{
label: 'Icons',
icon: 'i-lucide-smile',
description: 'You have nothing to do, @nuxt/icon will handle it automatically.'
},
{
label: 'Colors',
icon: 'i-lucide-swatch-book',
description: 'Choose a primary and a neutral color from your Tailwind CSS theme.'
},
{
label: 'Theme',
icon: 'i-lucide-cog',
description: 'You can customize components by using the `class` / `ui` props or in your app.config.ts.'
}
]
},
{
label: 'Composables',
icon: 'i-lucide-database',
children: [
{
label: 'defineShortcuts',
icon: 'i-lucide-file-text',
description: 'Define shortcuts for your application.',
to: '/composables/define-shortcuts'
},
{
label: 'useOverlay',
icon: 'i-lucide-file-text',
description: 'Display a modal/slideover within your application.',
to: '/composables/use-overlay'
},
{
label: 'useToast',
icon: 'i-lucide-file-text',
description: 'Display a toast within your application.',
to: '/composables/use-toast'
}
]
},
{
label: 'Components',
icon: 'i-lucide-box',
to: '/components',
active: true,
children: [
{
label: 'Link',
icon: 'i-lucide-file-text',
description: 'Use NuxtLink with superpowers.',
to: '/components/link'
},
{
label: 'Modal',
icon: 'i-lucide-file-text',
description: 'Display a modal within your application.',
to: '/components/modal'
},
{
label: 'NavigationMenu',
icon: 'i-lucide-file-text',
description: 'Display a list of links.',
to: '/components/navigation-menu'
},
{
label: 'Pagination',
icon: 'i-lucide-file-text',
description: 'Display a list of pages.',
to: '/components/pagination'
},
{
label: 'Popover',
icon: 'i-lucide-file-text',
description: 'Display a non-modal dialog that floats around a trigger element.',
to: '/components/popover'
},
{
label: 'Progress',
icon: 'i-lucide-file-text',
description: 'Show a horizontal bar to indicate task progression.',
to: '/components/progress'
}
]
}
],
[
{
label: 'GitHub',
icon: 'i-simple-icons-github',
badge: '3.8k',
to: 'https://github.com/nuxt/ui',
target: '_blank'
},
{
label: 'Help',
icon: 'i-lucide-circle-help',
disabled: true
}
]
])
</script>
<template>
<UNavigationMenu collapsed orientation="vertical" :items="items" />
</template>
高亮 (Highlight)
使用 highlight prop 为活动项显示高亮边框。
使用 highlight-color prop 更改边框颜色。它默认为 color prop。
<script setup lang="ts">
import type { NavigationMenuItem } from '@nuxt/ui'
const items = ref<NavigationMenuItem[][]>([
[
{
label: 'Guide',
icon: 'i-lucide-book-open',
children: [
{
label: 'Introduction',
description: 'Fully styled and customizable components for Nuxt.',
icon: 'i-lucide-house'
},
{
label: 'Installation',
description: 'Learn how to install and configure Nuxt UI in your application.',
icon: 'i-lucide-cloud-download'
},
{
label: 'Icons',
icon: 'i-lucide-smile',
description: 'You have nothing to do, @nuxt/icon will handle it automatically.'
},
{
label: 'Colors',
icon: 'i-lucide-swatch-book',
description: 'Choose a primary and a neutral color from your Tailwind CSS theme.'
},
{
label: 'Theme',
icon: 'i-lucide-cog',
description:
'You can customize components by using the `class` / `ui` props or in your app.config.ts.'
}
]
},
{
label: 'Composables',
icon: 'i-lucide-database',
children: [
{
label: 'defineShortcuts',
icon: 'i-lucide-file-text',
description: 'Define shortcuts for your application.',
to: '/composables/define-shortcuts'
},
{
label: 'useOverlay',
icon: 'i-lucide-file-text',
description: 'Display a modal/slideover within your application.',
to: '/composables/use-overlay'
},
{
label: 'useToast',
icon: 'i-lucide-file-text',
description: 'Display a toast within your application.',
to: '/composables/use-toast'
}
]
},
{
label: 'Components',
icon: 'i-lucide-box',
to: '/components',
active: true,
defaultOpen: true,
children: [
{
label: 'Link',
icon: 'i-lucide-file-text',
description: 'Use NuxtLink with superpowers.',
to: '/components/link'
},
{
label: 'Modal',
icon: 'i-lucide-file-text',
description: 'Display a modal within your application.',
to: '/components/modal'
},
{
label: 'NavigationMenu',
icon: 'i-lucide-file-text',
description: 'Display a list of links.',
to: '/components/navigation-menu'
},
{
label: 'Pagination',
icon: 'i-lucide-file-text',
description: 'Display a list of pages.',
to: '/components/pagination'
},
{
label: 'Popover',
icon: 'i-lucide-file-text',
description: 'Display a non-modal dialog that floats around a trigger element.',
to: '/components/popover'
},
{
label: 'Progress',
icon: 'i-lucide-file-text',
description: 'Show a horizontal bar to indicate task progression.',
to: '/components/progress'
}
]
}
],
[
{
label: 'GitHub',
icon: 'i-simple-icons-github',
badge: '3.8k',
to: 'https://github.com/nuxt/ui',
target: '_blank'
},
{
label: 'Help',
icon: 'i-lucide-circle-help',
disabled: true
}
]
])
</script>
<template>
<UNavigationMenu
highlight
highlight-color="primary"
orientation="horizontal"
:items="items"
class="data-[orientation=horizontal]:border-b border-default data-[orientation=horizontal]:w-full data-[orientation=vertical]:w-48"
/>
</template>
border-b 类应用于在 horizontal 方向上显示边框,这不是默认行为,以便您拥有一个干净的画布。vertical 方向上,highlight prop 仅高亮活动子项的边框。颜色 (Color)
使用 color prop 更改 NavigationMenu 的颜色。
<script setup lang="ts">
import type { NavigationMenuItem } from '@nuxt/ui'
const items = ref<NavigationMenuItem[][]>([
[
{
label: 'Guide',
icon: 'i-lucide-book-open',
to: '/getting-started'
},
{
label: 'Composables',
icon: 'i-lucide-database',
to: '/composables'
},
{
label: 'Components',
icon: 'i-lucide-box',
to: '/components',
active: true
}
],
[
{
label: 'GitHub',
icon: 'i-simple-icons-github',
badge: '3.8k',
to: 'https://github.com/nuxt/ui',
target: '_blank'
}
]
])
</script>
<template>
<UNavigationMenu color="neutral" :items="items" class="w-full" />
</template>
变体 (Variant)
使用 variant prop 更改 NavigationMenu 的变体。
<script setup lang="ts">
import type { NavigationMenuItem } from '@nuxt/ui'
const items = ref<NavigationMenuItem[][]>([
[
{
label: 'Guide',
icon: 'i-lucide-book-open',
to: '/getting-started'
},
{
label: 'Composables',
icon: 'i-lucide-database',
to: '/composables'
},
{
label: 'Components',
icon: 'i-lucide-box',
to: '/components',
active: true
}
],
[
{
label: 'GitHub',
icon: 'i-simple-icons-github',
badge: '3.8k',
to: 'https://github.com/nuxt/ui',
target: '_blank'
}
]
])
</script>
<template>
<UNavigationMenu color="neutral" variant="link" :items="items" class="w-full" />
</template>
highlight prop 改变了 pill 变体活动项的样式。尝试一下看看区别。尾部图标 (Trailing Icon)
使用 trailing-icon prop 来自定义每个项目的尾部 Icon。默认为 i-lucide-chevron-down。此图标仅在项目有子项时显示。
trailingIcon 属性为特定项目设置图标。<script setup lang="ts">
import type { NavigationMenuItem } from '@nuxt/ui'
const items = ref<NavigationMenuItem[]>([
{
label: 'Guide',
icon: 'i-lucide-book-open',
to: '/getting-started',
children: [
{
label: 'Introduction',
description: 'Fully styled and customizable components for Nuxt.',
icon: 'i-lucide-house'
},
{
label: 'Installation',
description: 'Learn how to install and configure Nuxt UI in your application.',
icon: 'i-lucide-cloud-download'
},
{
label: 'Icons',
icon: 'i-lucide-smile',
description: 'You have nothing to do, @nuxt/icon will handle it automatically.'
},
{
label: 'Colors',
icon: 'i-lucide-swatch-book',
description: 'Choose a primary and a neutral color from your Tailwind CSS theme.'
},
{
label: 'Theme',
icon: 'i-lucide-cog',
description: 'You can customize components by using the `class` / `ui` props or in your app.config.ts.'
}
]
},
{
label: 'Composables',
icon: 'i-lucide-database',
to: '/composables',
children: [
{
label: 'defineShortcuts',
icon: 'i-lucide-file-text',
description: 'Define shortcuts for your application.',
to: '/composables/define-shortcuts'
},
{
label: 'useOverlay',
icon: 'i-lucide-file-text',
description: 'Display a modal/slideover within your application.',
to: '/composables/use-overlay'
},
{
label: 'useToast',
icon: 'i-lucide-file-text',
description: 'Display a toast within your application.',
to: '/composables/use-toast'
}
]
},
{
label: 'Components',
icon: 'i-lucide-box',
to: '/components',
active: true,
children: [
{
label: 'Link',
icon: 'i-lucide-file-text',
description: 'Use NuxtLink with superpowers.',
to: '/components/link'
},
{
label: 'Modal',
icon: 'i-lucide-file-text',
description: 'Display a modal within your application.',
to: '/components/modal'
},
{
label: 'NavigationMenu',
icon: 'i-lucide-file-text',
description: 'Display a list of links.',
to: '/components/navigation-menu'
},
{
label: 'Pagination',
icon: 'i-lucide-file-text',
description: 'Display a list of pages.',
to: '/components/pagination'
},
{
label: 'Popover',
icon: 'i-lucide-file-text',
description: 'Display a non-modal dialog that floats around a trigger element.',
to: '/components/popover'
},
{
label: 'Progress',
icon: 'i-lucide-file-text',
description: 'Show a horizontal bar to indicate task progression.',
to: '/components/progress'
}
]
}
])
</script>
<template>
<UNavigationMenu trailing-icon="i-lucide-arrow-down" :items="items" class="w-full justify-center" />
</template>
箭头 (Arrow)
使用 arrow prop 在项目有子项时显示 NavigationMenu 内容上的箭头。
<script setup lang="ts">
import type { NavigationMenuItem } from '@nuxt/ui'
const items = ref<NavigationMenuItem[]>([
{
label: 'Guide',
icon: 'i-lucide-book-open',
to: '/getting-started',
children: [
{
label: 'Introduction',
description: 'Fully styled and customizable components for Nuxt.',
icon: 'i-lucide-house'
},
{
label: 'Installation',
description: 'Learn how to install and configure Nuxt UI in your application.',
icon: 'i-lucide-cloud-download'
},
{
label: 'Icons',
icon: 'i-lucide-smile',
description: 'You have nothing to do, @nuxt/icon will handle it automatically.'
},
{
label: 'Colors',
icon: 'i-lucide-swatch-book',
description: 'Choose a primary and a neutral color from your Tailwind CSS theme.'
},
{
label: 'Theme',
icon: 'i-lucide-cog',
description: 'You can customize components by using the `class` / `ui` props or in your app.config.ts.'
}
]
},
{
label: 'Composables',
icon: 'i-lucide-database',
to: '/composables',
children: [
{
label: 'defineShortcuts',
icon: 'i-lucide-file-text',
description: 'Define shortcuts for your application.',
to: '/composables/define-shortcuts'
},
{
label: 'useOverlay',
icon: 'i-lucide-file-text',
description: 'Display a modal/slideover within your application.',
to: '/composables/use-overlay'
},
{
label: 'useToast',
icon: 'i-lucide-file-text',
description: 'Display a toast within your application.',
to: '/composables/use-toast'
}
]
},
{
label: 'Components',
icon: 'i-lucide-box',
to: '/components',
active: true,
children: [
{
label: 'Link',
icon: 'i-lucide-file-text',
description: 'Use NuxtLink with superpowers.',
to: '/components/link'
},
{
label: 'Modal',
icon: 'i-lucide-file-text',
description: 'Display a modal within your application.',
to: '/components/modal'
},
{
label: 'NavigationMenu',
icon: 'i-lucide-file-text',
description: 'Display a list of links.',
to: '/components/navigation-menu'
},
{
label: 'Pagination',
icon: 'i-lucide-file-text',
description: 'Display a list of pages.',
to: '/components/pagination'
},
{
label: 'Popover',
icon: 'i-lucide-file-text',
description: 'Display a non-modal dialog that floats around a trigger element.',
to: '/components/popover'
},
{
label: 'Progress',
icon: 'i-lucide-file-text',
description: 'Show a horizontal bar to indicate task progression.',
to: '/components/progress'
}
]
}
])
</script>
<template>
<UNavigationMenu arrow :items="items" class="w-full justify-center" />
</template>
内容方向 (Content Orientation)
使用 content-orientation prop 更改内容的显示方向。
orientation 为 horizontal 时有效。<script setup lang="ts">
import type { NavigationMenuItem } from '@nuxt/ui'
const items = ref<NavigationMenuItem[]>([
{
label: 'Guide',
icon: 'i-lucide-book-open',
to: '/getting-started',
children: [
{
label: 'Introduction',
description: 'Fully styled and customizable components for Nuxt.',
icon: 'i-lucide-house'
},
{
label: 'Installation',
description: 'Learn how to install and configure Nuxt UI in your application.',
icon: 'i-lucide-cloud-download'
},
{
label: 'Icons',
icon: 'i-lucide-smile',
description: 'You have nothing to do, @nuxt/icon will handle it automatically.'
}
]
},
{
label: 'Composables',
icon: 'i-lucide-database',
to: '/composables',
children: [
{
label: 'defineShortcuts',
icon: 'i-lucide-file-text',
description: 'Define shortcuts for your application.',
to: '/composables/define-shortcuts'
},
{
label: 'useOverlay',
icon: 'i-lucide-file-text',
description: 'Display a modal/slideover within your application.',
to: '/composables/use-overlay'
},
{
label: 'useToast',
icon: 'i-lucide-file-text',
description: 'Display a toast within your application.',
to: '/composables/use-toast'
}
]
},
{
label: 'Components',
icon: 'i-lucide-box',
to: '/components',
active: true,
children: [
{
label: 'Link',
icon: 'i-lucide-file-text',
description: 'Use NuxtLink with superpowers.',
to: '/components/link'
},
{
label: 'Modal',
icon: 'i-lucide-file-text',
description: 'Display a modal within your application.',
to: '/components/modal'
},
{
label: 'NavigationMenu',
icon: 'i-lucide-file-text',
description: 'Display a list of links.',
to: '/components/navigation-menu'
},
{
label: 'Pagination',
icon: 'i-lucide-file-text',
description: 'Display a list of pages.',
to: '/components/pagination'
}
]
}
])
</script>
<template>
<UNavigationMenu arrow content-orientation="vertical" :items="items" class="w-full justify-center" />
</template>
卸载 (Unmount)
使用 unmount-on-hide prop 控制内容卸载行为。默认为 true。
<script setup lang="ts">
import type { NavigationMenuItem } from '@nuxt/ui'
const items = ref<NavigationMenuItem[]>([
{
label: 'Guide',
icon: 'i-lucide-book-open',
to: '/getting-started',
children: [
{
label: 'Introduction',
description: 'Fully styled and customizable components for Nuxt.',
icon: 'i-lucide-house'
},
{
label: 'Installation',
description: 'Learn how to install and configure Nuxt UI in your application.',
icon: 'i-lucide-cloud-download'
},
{
label: 'Icons',
icon: 'i-lucide-smile',
description: 'You have nothing to do, @nuxt/icon will handle it automatically.'
},
{
label: 'Colors',
icon: 'i-lucide-swatch-book',
description: 'Choose a primary and a neutral color from your Tailwind CSS theme.'
},
{
label: 'Theme',
icon: 'i-lucide-cog',
description: 'You can customize components by using the `class` / `ui` props or in your app.config.ts.'
}
]
},
{
label: 'Composables',
icon: 'i-lucide-database',
to: '/composables',
children: [
{
label: 'defineShortcuts',
icon: 'i-lucide-file-text',
description: 'Define shortcuts for your application.',
to: '/composables/define-shortcuts'
},
{
label: 'useOverlay',
icon: 'i-lucide-file-text',
description: 'Display a modal/slideover within your application.',
to: '/composables/use-overlay'
},
{
label: 'useToast',
icon: 'i-lucide-file-text',
description: 'Display a toast within your application.',
to: '/composables/use-toast'
}
]
},
{
label: 'Components',
icon: 'i-lucide-box',
to: '/components',
active: true,
children: [
{
label: 'Link',
icon: 'i-lucide-file-text',
description: 'Use NuxtLink with superpowers.',
to: '/components/link'
},
{
label: 'Modal',
icon: 'i-lucide-file-text',
description: 'Display a modal within your application.',
to: '/components/modal'
},
{
label: 'NavigationMenu',
icon: 'i-lucide-file-text',
description: 'Display a list of links.',
to: '/components/navigation-menu'
},
{
label: 'Pagination',
icon: 'i-lucide-file-text',
description: 'Display a list of pages.',
to: '/components/pagination'
},
{
label: 'Popover',
icon: 'i-lucide-file-text',
description: 'Display a non-modal dialog that floats around a trigger element.',
to: '/components/popover'
},
{
label: 'Progress',
icon: 'i-lucide-file-text',
description: 'Show a horizontal bar to indicate task progression.',
to: '/components/progress'
}
]
}
])
</script>
<template>
<UNavigationMenu :unmount-on-hide="false" :items="items" class="w-full justify-center" />
</template>
示例
项中带工具提示 New
当 orientation 为 vertical 且菜单 collapsed 时,你可以将 tooltip prop 设置为 true,以在项目周围显示一个带标签的 Tooltip。你也可以在每个项目上使用 tooltip 属性来覆盖默认工具提示。
你可以全局或在每个项目上传递 Tooltip 组件的任何属性。
<script setup lang="ts">
import type { NavigationMenuItem } from '@nuxt/ui'
const items = ref<NavigationMenuItem[][]>([
[
{
label: 'Links',
type: 'label'
},
{
label: 'Guide',
icon: 'i-lucide-book-open',
children: [
{
label: 'Introduction',
description: 'Fully styled and customizable components for Nuxt.',
icon: 'i-lucide-house'
},
{
label: 'Installation',
description: 'Learn how to install and configure Nuxt UI in your application.',
icon: 'i-lucide-cloud-download'
},
{
label: 'Icons',
icon: 'i-lucide-smile',
description: 'You have nothing to do, @nuxt/icon will handle it automatically.'
},
{
label: 'Colors',
icon: 'i-lucide-swatch-book',
description: 'Choose a primary and a neutral color from your Tailwind CSS theme.'
},
{
label: 'Theme',
icon: 'i-lucide-cog',
description: 'You can customize components by using the `class` / `ui` props or in your app.config.ts.'
}
]
},
{
label: 'Composables',
icon: 'i-lucide-database',
children: [
{
label: 'defineShortcuts',
icon: 'i-lucide-file-text',
description: 'Define shortcuts for your application.',
to: '/composables/define-shortcuts'
},
{
label: 'useOverlay',
icon: 'i-lucide-file-text',
description: 'Display a modal/slideover within your application.',
to: '/composables/use-overlay'
},
{
label: 'useToast',
icon: 'i-lucide-file-text',
description: 'Display a toast within your application.',
to: '/composables/use-toast'
}
]
},
{
label: 'Components',
icon: 'i-lucide-box',
to: '/components',
active: true,
children: [
{
label: 'Link',
icon: 'i-lucide-file-text',
description: 'Use NuxtLink with superpowers.',
to: '/components/link'
},
{
label: 'Modal',
icon: 'i-lucide-file-text',
description: 'Display a modal within your application.',
to: '/components/modal'
},
{
label: 'NavigationMenu',
icon: 'i-lucide-file-text',
description: 'Display a list of links.',
to: '/components/navigation-menu'
},
{
label: 'Pagination',
icon: 'i-lucide-file-text',
description: 'Display a list of pages.',
to: '/components/pagination'
},
{
label: 'Popover',
icon: 'i-lucide-file-text',
description: 'Display a non-modal dialog that floats around a trigger element.',
to: '/components/popover'
},
{
label: 'Progress',
icon: 'i-lucide-file-text',
description: 'Show a horizontal bar to indicate task progression.',
to: '/components/progress'
}
]
}
],
[
{
label: 'GitHub',
icon: 'i-simple-icons-github',
badge: '3.8k',
to: 'https://github.com/nuxt/ui',
target: '_blank',
tooltip: {
text: 'Open on GitHub',
kbds: [
'3.8k'
]
}
},
{
label: 'Help',
icon: 'i-lucide-circle-help',
disabled: true
}
]
])
</script>
<template>
<UNavigationMenu tooltip collapsed orientation="vertical" :items="items" />
</template>
项中带气泡框 New
当 orientation 为 vertical 且菜单 collapsed 时,你可以将 popover prop 设置为 true,以在项目周围显示一个带子项的 Popover。你也可以在每个项目上使用 popover 属性来覆盖默认气泡框。
你可以全局或在每个项目上传递 Popover 组件的任何属性。
<script setup lang="ts">
import type { NavigationMenuItem } from '@nuxt/ui'
const items = ref<NavigationMenuItem[][]>([
[
{
label: 'Links',
type: 'label'
},
{
label: 'Guide',
icon: 'i-lucide-book-open',
children: [
{
label: 'Introduction',
description: 'Fully styled and customizable components for Nuxt.',
icon: 'i-lucide-house'
},
{
label: 'Installation',
description: 'Learn how to install and configure Nuxt UI in your application.',
icon: 'i-lucide-cloud-download'
},
{
label: 'Icons',
icon: 'i-lucide-smile',
description: 'You have nothing to do, @nuxt/icon will handle it automatically.'
},
{
label: 'Colors',
icon: 'i-lucide-swatch-book',
description: 'Choose a primary and a neutral color from your Tailwind CSS theme.'
},
{
label: 'Theme',
icon: 'i-lucide-cog',
description: 'You can customize components by using the `class` / `ui` props or in your app.config.ts.'
}
]
},
{
label: 'Composables',
icon: 'i-lucide-database',
popover: {
mode: 'click'
},
children: [
{
label: 'defineShortcuts',
icon: 'i-lucide-file-text',
description: 'Define shortcuts for your application.',
to: '/composables/define-shortcuts'
},
{
label: 'useOverlay',
icon: 'i-lucide-file-text',
description: 'Display a modal/slideover within your application.',
to: '/composables/use-overlay'
},
{
label: 'useToast',
icon: 'i-lucide-file-text',
description: 'Display a toast within your application.',
to: '/composables/use-toast'
}
]
},
{
label: 'Components',
icon: 'i-lucide-box',
to: '/components',
active: true,
children: [
{
label: 'Link',
icon: 'i-lucide-file-text',
description: 'Use NuxtLink with superpowers.',
to: '/components/link'
},
{
label: 'Modal',
icon: 'i-lucide-file-text',
description: 'Display a modal within your application.',
to: '/components/modal'
},
{
label: 'NavigationMenu',
icon: 'i-lucide-file-text',
description: 'Display a list of links.',
to: '/components/navigation-menu'
},
{
label: 'Pagination',
icon: 'i-lucide-file-text',
description: 'Display a list of pages.',
to: '/components/pagination'
},
{
label: 'Popover',
icon: 'i-lucide-file-text',
description: 'Display a non-modal dialog that floats around a trigger element.',
to: '/components/popover'
},
{
label: 'Progress',
icon: 'i-lucide-file-text',
description: 'Show a horizontal bar to indicate task progression.',
to: '/components/progress'
}
]
}
],
[
{
label: 'GitHub',
icon: 'i-simple-icons-github',
badge: '3.8k',
to: 'https://github.com/nuxt/ui',
target: '_blank',
tooltip: {
text: 'Open on GitHub',
kbds: [
'3.8k'
]
}
},
{
label: 'Help',
icon: 'i-lucide-circle-help',
disabled: true
}
]
])
</script>
<template>
<UNavigationMenu popover collapsed orientation="vertical" :items="items" />
</template>
控制活动项
你可以使用 default-value prop 或 v-model 指令以及项目的索引来控制活动项。
<script setup lang="ts">
import type { NavigationMenuItem } from '@nuxt/ui'
const items: NavigationMenuItem[] = [
{
label: 'Guide',
icon: 'i-lucide-book-open',
children: [
{
label: 'Introduction',
description: 'Fully styled and customizable components for Nuxt.',
icon: 'i-lucide-house'
},
{
label: 'Installation',
description: 'Learn how to install and configure Nuxt UI in your application.',
icon: 'i-lucide-cloud-download'
},
{
label: 'Icons',
icon: 'i-lucide-smile',
description: 'You have nothing to do, @nuxt/icon will handle it automatically.'
},
{
label: 'Colors',
icon: 'i-lucide-swatch-book',
description: 'Choose a primary and a neutral color from your Tailwind CSS theme.'
},
{
label: 'Theme',
icon: 'i-lucide-cog',
description: 'You can customize components by using the `class` / `ui` props or in your app.config.ts.'
}
]
},
{
label: 'Composables',
icon: 'i-lucide-database',
children: [
{
label: 'defineShortcuts',
icon: 'i-lucide-file-text',
description: 'Define shortcuts for your application.'
},
{
label: 'useOverlay',
icon: 'i-lucide-file-text',
description: 'Display a modal/slideover within your application.'
},
{
label: 'useToast',
icon: 'i-lucide-file-text',
description: 'Display a toast within your application.'
}
]
},
{
label: 'Components',
icon: 'i-lucide-box',
children: [
{
label: 'Link',
icon: 'i-lucide-file-text',
description: 'Use NuxtLink with superpowers.'
},
{
label: 'Modal',
icon: 'i-lucide-file-text',
description: 'Display a modal within your application.'
},
{
label: 'NavigationMenu',
icon: 'i-lucide-file-text',
description: 'Display a list of links.'
},
{
label: 'Pagination',
icon: 'i-lucide-file-text',
description: 'Display a list of pages.'
},
{
label: 'Popover',
icon: 'i-lucide-file-text',
description: 'Display a non-modal dialog that floats around a trigger element.'
},
{
label: 'Progress',
icon: 'i-lucide-file-text',
description: 'Show a horizontal bar to indicate task progression.'
}
]
}
]
const active = ref()
defineShortcuts({
1: () => {
active.value = 'item-0'
},
2: () => {
active.value = 'item-1'
},
3: () => {
active.value = 'item-2'
}
})
</script>
<template>
<UNavigationMenu v-model="active" :items="items" class="w-full justify-center" />
</template>
defineShortcuts,你可以通过按下 1、 2 或 3 来切换活动项。NavigationMenuItem 没有显式 value,组件内部会自动生成默认值。根据你给出的实现(和常见实现一致):- 顶层项(level = 0)默认
value为:item-${index},也就是item-0,item-1,item-2... - 第一层子项(level = 1)默认
value为:item-1-0,item-1-1...(格式item-${level}-${index})
value,要激活第 1 个顶层项,应该把 active.value = 'item-0',而不是 '0'。value 值。带自定义插槽
使用 slot 属性自定义特定项目。
你将可以使用以下插槽:
#{{ item.slot }}#{{ item.slot }}-leading#{{ item.slot }}-label#{{ item.slot }}-trailing#{{ item.slot }}-content
<script setup lang="ts">
import type { NavigationMenuItem } from '@nuxt/ui'
const items = [
{
label: 'Guide',
icon: 'i-lucide-book-open'
},
{
label: 'Composables',
icon: 'i-lucide-database'
},
{
label: 'Components',
icon: 'i-lucide-box',
slot: 'components' as const
}
] satisfies NavigationMenuItem[]
</script>
<template>
<UNavigationMenu :items="items" class="w-full justify-center">
<template #components-trailing>
<UBadge label="44" variant="subtle" size="sm" />
</template>
</UNavigationMenu>
</template>
带内容插槽
使用 #item-content 插槽或 slot 属性(#{{ item.slot }}-content)来自定义特定项目的内容。
<script setup lang="ts">
import type { NavigationMenuItem } from '@nuxt/ui'
const items = [
{
label: 'Docs',
icon: 'i-lucide-book-open',
slot: 'docs' as const,
children: [
{
label: 'Icons',
description: 'You have nothing to do, @nuxt/icon will handle it automatically.'
},
{
label: 'Colors',
description: 'Choose a primary and a neutral color from your Tailwind CSS theme.'
},
{
label: 'Theme',
description: 'You can customize components by using the `class` / `ui` props or in your app.config.ts.'
}
]
},
{
label: 'Components',
icon: 'i-lucide-box',
slot: 'components' as const,
children: [
{
label: 'Link',
description: 'Use NuxtLink with superpowers.'
},
{
label: 'Modal',
description: 'Display a modal within your application.'
},
{
label: 'NavigationMenu',
description: 'Display a list of links.'
},
{
label: 'Pagination',
description: 'Display a list of pages.'
},
{
label: 'Popover',
description: 'Display a non-modal dialog that floats around a trigger element.'
},
{
label: 'Progress',
description: 'Show a horizontal bar to indicate task progression.'
}
]
},
{
label: 'GitHub',
icon: 'i-simple-icons-github'
}
] satisfies NavigationMenuItem[]
</script>
<template>
<UNavigationMenu
:items="items"
class="w-full justify-center"
:ui="{
viewport: 'sm:w-(--reka-navigation-menu-viewport-width)',
content: 'sm:w-auto',
childList: 'sm:w-96',
childLinkDescription: 'text-balance line-clamp-2'
}"
>
<template #docs-content="{ item }">
<ul class="grid gap-2 p-4 lg:w-[500px] lg:grid-cols-[minmax(0,.75fr)_minmax(0,1fr)]">
<li class="row-span-3">
<Placeholder class="size-full min-h-48" />
</li>
<li v-for="child in item.children" :key="child.label">
<ULink class="text-sm text-left rounded-md p-3 transition-colors hover:bg-elevated/50">
<p class="font-medium text-highlighted">
{{ child.label }}
</p>
<p class="text-muted line-clamp-2">
{{ child.description }}
</p>
</ULink>
</li>
</ul>
</template>
</UNavigationMenu>
</template>
viewport 上添加 sm:w-(--reka-navigation-menu-viewport-width) 类以获得动态宽度。这要求在内容的第一个子元素上设置宽度。API
Props
| Prop | Default | Type |
|---|---|---|
as |
|
The element or component this component should render as. |
trailingIcon |
|
The icon displayed to open the menu. |
externalIcon |
|
The icon displayed when the item is an external link.
Set to |
items |
| |
color |
|
|
variant |
|
|
orientation |
|
The orientation of the menu. |
collapsed |
|
Collapse the navigation menu to only show icons.
Only works when |
tooltip |
|
Display a tooltip on the items when the menu is collapsed with the label of the item.
|
popover |
|
Display a popover on the items when the menu is collapsed with the children list.
|
highlight |
Display a line next to the active item. | |
highlightColor |
|
|
content |
The content of the menu.
| |
contentOrientation |
|
The orientation of the content.
Only works when |
arrow |
|
Display an arrow alongside the menu. |
labelKey |
|
The key used to get the label from the item. |
defaultValue |
The value of the menu item that should be active when initially rendered. Use when you do not need to control the value state. | |
modelValue |
The controlled value of the menu item to activate. Can be used as | |
unmountOnHide |
|
When |
delayDuration |
|
The duration from when the pointer enters the trigger until the tooltip gets opened. |
disableClickTrigger |
|
If |
disableHoverTrigger |
|
If |
skipDelayDuration |
|
How much time a user has to enter another trigger without incurring a delay again. |
disablePointerLeaveClose |
|
If |
collapsible |
|
When type is "single", allows closing content when clicking trigger for an open item. When type is "multiple", this prop has no effect. |
type |
|
Determines whether a "single" or "multiple" items can be selected at a time. This prop will overwrite the inferred type from |
disabled |
|
When |
ui |
|
modeValue
UNavigationMenu 组件中的 modelValue 主要用于管理 垂直方向 且 非折叠 状态下的手风琴(Accordion)菜单的 展开/收起状态。
下面是 modelValue 在不同场景下的详细工作原理:
modelValue 的作用:
UNavigationMenu 组件内部使用了 reka-ui 的 AccordionRoot 组件来处理垂直方向的菜单。AccordionRoot 组件通过 v-model 来绑定一个值,这个值决定了哪些子菜单项(AccordionItem)是展开的。
modelValue的类型:- 如果
type为'single',modelValue是一个string,表示当前唯一展开的菜单项的value。 - 如果
type为'multiple',modelValue是一个string[],表示当前所有展开的菜单项的value数组。
- 如果
value属性:- 每个菜单项(
NavigationMenuItem)都可以有一个value属性,用于作为其唯一标识。 - 如果未手动指定
value,组件会根据菜单项的层级和索引自动生成一个值,例如item-0-1。
- 每个菜单项(
简而言之,modelValue 存储着当前处于展开状态的菜单项的 value。
哪些菜单项会修改 modelValue:
并非所有菜单项的点击都会修改 modelValue。只有满足特定条件的菜单项才会被渲染为 AccordionTrigger,从而具备修改 modelValue 的能力。
根据您提供的代码,触发 modelValue 变更的菜单项需要满足以下所有条件:
- 方向为垂直:
orientation === 'vertical'。 - 菜单未折叠:
!collapsed。 - 菜单项有子项:
item.children?.length。 - 菜单项本身不是链接:
!(slotProps as any).href。
当用户点击满足这些条件的菜单项时,AccordionTrigger 会触发 update:modelValue 事件,从而更新父组件绑定的 modelValue。
不会修改 modelValue 的菜单项:
在以下几种情况下,菜单项的点击 不会 影响 modelValue:
- 普通链接菜单项:当菜单项不包含子项时,它会被渲染为
NavigationMenuLink。点击它会触发导航,但不会修改modelValue。 - 水平方向的菜单项:水平菜单使用
NavigationMenuRoot,其modelValue行为不同,主要控制弹出内容的显示。 - 折叠状态下的菜单项:当
collapsed为true时,垂直菜单项会被渲染为UPopover或UTooltip,它们的交互行为不再是展开手风琴,因此也不会修改modelValue。
总结:
UNavigationMenu 的 modelValue 是一个 上下文相关 的值,其作用和行为取决于组件的 orientation、collapsed 和 type 等属性。它主要用于管理 垂直非折叠菜单 中子项的展开状态。
如果您希望在其他情况下(例如水平菜单)管理选中状态,您需要监听 onSelect 事件或通过路由状态来手动控制菜单项的 active 属性。
defaultValue
modelValue 用于双向绑定,而 defaultValue 仅用于设置组件的初始值。
modelValue是一个组件props,它与组件的update:modelValue事件配合使用,以实现v-model的双向数据绑定。defaultValue是一个props,它只在组件的初始渲染时发挥作用,用于设置组件的初始值。- 当
modelValue未提供(即没有使用v-model)时,defaultValue会作为组件的初始状态值。这在非受控组件(uncontrolled component)中很常见。
- 当
受控 vs 非受控:
- 当
props.modelValue === undefined(父没有传v-model)时,运行时passive真值为true,useVModel会创建内部ref并通过watch同步父prop的变化 —— 你能在子组件内直接赋modelValue.value = 'x',不会触发update:modelValue(因为根本没发出该事件)。 - 当父传了
modelValue,运行时passive为false,useVModel返回的是 writable computed:你写入会emit('update:modelValue', val)去通知父。
Slots
| Slot | Type |
|---|---|
item |
|
item-leading |
|
item-label |
|
item-trailing |
|
item-content |
|
list-leading |
|
list-trailing |
|
Emits
| Event | Type |
|---|---|
update:modelValue |
|
支持的事件
UNavigationMenu 组件支持的事件主要由其内部使用的 reka-ui 组件决定。由于 UNavigationMenu 封装了 NavigationMenuRoot 和 AccordionRoot 这两个组件,它能够触发这些底层组件的所有事件。
下面是 UNavigationMenu 支持的主要事件及其作用:
NavigationMenu 事件(适用于水平菜单)
当 orientation 为 'horizontal' 时,组件会使用 NavigationMenuRoot,因此它支持以下事件:
update:modelValue: 当菜单的modelValue发生变化时触发。在水平菜单中,这通常意味着某个子菜单内容被打开或关闭。open: 当一个子菜单被打开时触发。close: 当一个子菜单被关闭时触发。select: 当用户选择一个可导航的菜单项时触发。
Accordion 事件(适用于垂直菜单)
当 orientation 为 'vertical' 时,组件会使用 AccordionRoot,因此它支持以下事件:
update:modelValue: 当手风琴菜单的modelValue发生变化时触发。这表示某个可折叠的菜单项被展开或收起。update:disabled: 当disabled属性发生变化时触发。update:type: 当type属性('single'或'multiple')发生变化时触发。update:collapsible: 当collapsible属性发生变化时触发。
总结
UNavigationMenu 组件本身通过 emits 声明了 NavigationMenuEmits,这实际上包含了 NavigationMenuRoot 和 AccordionRoot 的所有事件。
此外,每个单独的菜单项(NavigationMenuItem)也可以定义一个 onSelect 回调函数。这个回调会在该菜单项被选中时触发,允许您为每个菜单项执行特定的操作,而不仅仅依赖于组件的全局事件。
Theme
export default defineAppConfig({
ui: {
navigationMenu: {
slots: {
root: 'relative flex gap-1.5 [&>div]:min-w-0',
list: 'isolate min-w-0',
label: 'w-full flex items-center gap-1.5 font-semibold text-xs/5 text-highlighted px-2.5 py-1.5',
item: 'min-w-0',
link: 'group relative w-full flex items-center gap-1.5 font-medium text-sm before:absolute before:z-[-1] before:rounded-md focus:outline-none focus-visible:outline-none dark:focus-visible:outline-none focus-visible:before:ring-inset focus-visible:before:ring-2',
linkLeadingIcon: 'shrink-0 size-5',
linkLeadingAvatar: 'shrink-0',
linkLeadingAvatarSize: '2xs',
linkTrailing: 'group ms-auto inline-flex gap-1.5 items-center',
linkTrailingBadge: 'shrink-0',
linkTrailingBadgeSize: 'sm',
linkTrailingIcon: 'size-5 transform shrink-0 group-data-[state=open]:rotate-180 transition-transform duration-200',
linkLabel: 'truncate',
linkLabelExternalIcon: 'inline-block size-3 align-top text-dimmed',
childList: 'isolate',
childLabel: 'text-xs text-highlighted',
childItem: '',
childLink: 'group relative size-full flex items-start text-start text-sm before:absolute before:z-[-1] before:rounded-md focus:outline-none focus-visible:outline-none dark:focus-visible:outline-none focus-visible:before:ring-inset focus-visible:before:ring-2',
childLinkWrapper: 'min-w-0',
childLinkIcon: 'size-5 shrink-0',
childLinkLabel: 'truncate',
childLinkLabelExternalIcon: 'inline-block size-3 align-top text-dimmed',
childLinkDescription: 'text-muted',
separator: 'px-2 h-px bg-border',
viewportWrapper: 'absolute top-full left-0 flex w-full',
viewport: 'relative overflow-hidden bg-default shadow-lg rounded-md ring ring-default h-(--reka-navigation-menu-viewport-height) w-full transition-[width,height,left] duration-200 origin-[top_center] data-[state=open]:animate-[scale-in_100ms_ease-out] data-[state=closed]:animate-[scale-out_100ms_ease-in] z-[1]',
content: '',
indicator: 'absolute data-[state=visible]:animate-[fade-in_100ms_ease-out] data-[state=hidden]:animate-[fade-out_100ms_ease-in] data-[state=hidden]:opacity-0 bottom-0 z-[2] w-(--reka-navigation-menu-indicator-size) translate-x-(--reka-navigation-menu-indicator-position) flex h-2.5 items-end justify-center overflow-hidden transition-[translate,width] duration-200',
arrow: 'relative top-[50%] size-2.5 rotate-45 border border-default bg-default z-[1] rounded-xs'
},
variants: {
color: {
primary: {
link: 'focus-visible:before:ring-primary',
childLink: 'focus-visible:before:ring-primary'
},
secondary: {
link: 'focus-visible:before:ring-secondary',
childLink: 'focus-visible:before:ring-secondary'
},
success: {
link: 'focus-visible:before:ring-success',
childLink: 'focus-visible:before:ring-success'
},
info: {
link: 'focus-visible:before:ring-info',
childLink: 'focus-visible:before:ring-info'
},
warning: {
link: 'focus-visible:before:ring-warning',
childLink: 'focus-visible:before:ring-warning'
},
error: {
link: 'focus-visible:before:ring-error',
childLink: 'focus-visible:before:ring-error'
},
neutral: {
link: 'focus-visible:before:ring-inverted',
childLink: 'focus-visible:before:ring-inverted'
}
},
highlightColor: {
primary: '',
secondary: '',
success: '',
info: '',
warning: '',
error: '',
neutral: ''
},
variant: {
pill: '',
link: ''
},
orientation: {
horizontal: {
root: 'items-center justify-between',
list: 'flex items-center',
item: 'py-2',
link: 'px-2.5 py-1.5 before:inset-x-px before:inset-y-0',
childList: 'grid p-2',
childLink: 'px-3 py-2 gap-2 before:inset-x-px before:inset-y-0',
childLinkLabel: 'font-medium',
content: 'absolute top-0 left-0 w-full max-h-[70vh] overflow-y-auto'
},
vertical: {
root: 'flex-col',
link: 'flex-row px-2.5 py-1.5 before:inset-y-px before:inset-x-0',
childLabel: 'px-1.5 py-0.5',
childLink: 'p-1.5 gap-1.5 before:inset-y-px before:inset-x-0'
}
},
contentOrientation: {
horizontal: {
viewportWrapper: 'justify-center',
content: 'data-[motion=from-start]:animate-[enter-from-left_200ms_ease] data-[motion=from-end]:animate-[enter-from-right_200ms_ease] data-[motion=to-start]:animate-[exit-to-left_200ms_ease] data-[motion=to-end]:animate-[exit-to-right_200ms_ease]'
},
vertical: {
viewport: 'sm:w-(--reka-navigation-menu-viewport-width) left-(--reka-navigation-menu-viewport-left)'
}
},
active: {
true: {
childLink: 'before:bg-elevated text-highlighted',
childLinkIcon: 'text-default'
},
false: {
link: 'text-muted',
linkLeadingIcon: 'text-dimmed',
childLink: [
'hover:before:bg-elevated/50 text-default hover:text-highlighted',
'transition-colors before:transition-colors'
],
childLinkIcon: [
'text-dimmed group-hover:text-default',
'transition-colors'
]
}
},
disabled: {
true: {
link: 'cursor-not-allowed opacity-75'
}
},
highlight: {
true: ''
},
level: {
true: ''
},
collapsed: {
true: ''
}
},
compoundVariants: [
{
orientation: 'horizontal',
contentOrientation: 'horizontal',
class: {
childList: 'grid-cols-2 gap-2'
}
},
{
orientation: 'horizontal',
contentOrientation: 'vertical',
class: {
childList: 'gap-1',
content: 'w-60'
}
},
{
orientation: 'vertical',
collapsed: false,
class: {
childList: 'ms-5 border-s border-default',
childItem: 'ps-1.5 -ms-px',
content: 'data-[state=open]:animate-[collapsible-down_200ms_ease-out] data-[state=closed]:animate-[collapsible-up_200ms_ease-out] overflow-hidden'
}
},
{
orientation: 'vertical',
collapsed: true,
class: {
link: 'px-1.5',
content: 'shadow-sm rounded-sm min-h-6 p-1'
}
},
{
orientation: 'horizontal',
highlight: true,
class: {
link: [
'after:absolute after:-bottom-2 after:inset-x-2.5 after:block after:h-px after:rounded-full',
'after:transition-colors'
]
}
},
{
orientation: 'vertical',
highlight: true,
level: true,
class: {
link: [
'after:absolute after:-start-1.5 after:inset-y-0.5 after:block after:w-px after:rounded-full',
'after:transition-colors'
]
}
},
{
disabled: false,
active: false,
variant: 'pill',
class: {
link: [
'hover:text-highlighted hover:before:bg-elevated/50',
'transition-colors before:transition-colors'
],
linkLeadingIcon: [
'group-hover:text-default',
'transition-colors'
]
}
},
{
disabled: false,
active: false,
variant: 'pill',
orientation: 'horizontal',
class: {
link: 'data-[state=open]:text-highlighted',
linkLeadingIcon: 'group-data-[state=open]:text-default'
}
},
{
disabled: false,
variant: 'pill',
highlight: true,
orientation: 'horizontal',
class: {
link: 'data-[state=open]:before:bg-elevated/50'
}
},
{
disabled: false,
variant: 'pill',
highlight: false,
active: false,
orientation: 'horizontal',
class: {
link: 'data-[state=open]:before:bg-elevated/50'
}
},
{
color: 'primary',
variant: 'pill',
active: true,
class: {
link: 'text-primary',
linkLeadingIcon: 'text-primary group-data-[state=open]:text-primary'
}
},
{
color: 'neutral',
variant: 'pill',
active: true,
class: {
link: 'text-highlighted',
linkLeadingIcon: 'text-highlighted group-data-[state=open]:text-highlighted'
}
},
{
variant: 'pill',
active: true,
highlight: false,
class: {
link: 'before:bg-elevated'
}
},
{
variant: 'pill',
active: true,
highlight: true,
disabled: false,
class: {
link: [
'hover:before:bg-elevated/50',
'before:transition-colors'
]
}
},
{
disabled: false,
active: false,
variant: 'link',
class: {
link: [
'hover:text-highlighted',
'transition-colors'
],
linkLeadingIcon: [
'group-hover:text-default',
'transition-colors'
]
}
},
{
disabled: false,
active: false,
variant: 'link',
orientation: 'horizontal',
class: {
link: 'data-[state=open]:text-highlighted',
linkLeadingIcon: 'group-data-[state=open]:text-default'
}
},
{
color: 'primary',
variant: 'link',
active: true,
class: {
link: 'text-primary',
linkLeadingIcon: 'text-primary group-data-[state=open]:text-primary'
}
},
{
color: 'neutral',
variant: 'link',
active: true,
class: {
link: 'text-highlighted',
linkLeadingIcon: 'text-highlighted group-data-[state=open]:text-highlighted'
}
},
{
highlightColor: 'primary',
highlight: true,
level: true,
active: true,
class: {
link: 'after:bg-primary'
}
},
{
highlightColor: 'neutral',
highlight: true,
level: true,
active: true,
class: {
link: 'after:bg-inverted'
}
}
],
defaultVariants: {
color: 'primary',
highlightColor: 'primary',
variant: 'pill'
}
}
}
})
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import ui from '@nuxt/ui/vite'
export default defineConfig({
plugins: [
vue(),
ui({
ui: {
navigationMenu: {
slots: {
root: 'relative flex gap-1.5 [&>div]:min-w-0',
list: 'isolate min-w-0',
label: 'w-full flex items-center gap-1.5 font-semibold text-xs/5 text-highlighted px-2.5 py-1.5',
item: 'min-w-0',
link: 'group relative w-full flex items-center gap-1.5 font-medium text-sm before:absolute before:z-[-1] before:rounded-md focus:outline-none focus-visible:outline-none dark:focus-visible:outline-none focus-visible:before:ring-inset focus-visible:before:ring-2',
linkLeadingIcon: 'shrink-0 size-5',
linkLeadingAvatar: 'shrink-0',
linkLeadingAvatarSize: '2xs',
linkTrailing: 'group ms-auto inline-flex gap-1.5 items-center',
linkTrailingBadge: 'shrink-0',
linkTrailingBadgeSize: 'sm',
linkTrailingIcon: 'size-5 transform shrink-0 group-data-[state=open]:rotate-180 transition-transform duration-200',
linkLabel: 'truncate',
linkLabelExternalIcon: 'inline-block size-3 align-top text-dimmed',
childList: 'isolate',
childLabel: 'text-xs text-highlighted',
childItem: '',
childLink: 'group relative size-full flex items-start text-start text-sm before:absolute before:z-[-1] before:rounded-md focus:outline-none focus-visible:outline-none dark:focus-visible:outline-none focus-visible:before:ring-inset focus-visible:before:ring-2',
childLinkWrapper: 'min-w-0',
childLinkIcon: 'size-5 shrink-0',
childLinkLabel: 'truncate',
childLinkLabelExternalIcon: 'inline-block size-3 align-top text-dimmed',
childLinkDescription: 'text-muted',
separator: 'px-2 h-px bg-border',
viewportWrapper: 'absolute top-full left-0 flex w-full',
viewport: 'relative overflow-hidden bg-default shadow-lg rounded-md ring ring-default h-(--reka-navigation-menu-viewport-height) w-full transition-[width,height,left] duration-200 origin-[top_center] data-[state=open]:animate-[scale-in_100ms_ease-out] data-[state=closed]:animate-[scale-out_100ms_ease-in] z-[1]',
content: '',
indicator: 'absolute data-[state=visible]:animate-[fade-in_100ms_ease-out] data-[state=hidden]:animate-[fade-out_100ms_ease-in] data-[state=hidden]:opacity-0 bottom-0 z-[2] w-(--reka-navigation-menu-indicator-size) translate-x-(--reka-navigation-menu-indicator-position) flex h-2.5 items-end justify-center overflow-hidden transition-[translate,width] duration-200',
arrow: 'relative top-[50%] size-2.5 rotate-45 border border-default bg-default z-[1] rounded-xs'
},
variants: {
color: {
primary: {
link: 'focus-visible:before:ring-primary',
childLink: 'focus-visible:before:ring-primary'
},
secondary: {
link: 'focus-visible:before:ring-secondary',
childLink: 'focus-visible:before:ring-secondary'
},
success: {
link: 'focus-visible:before:ring-success',
childLink: 'focus-visible:before:ring-success'
},
info: {
link: 'focus-visible:before:ring-info',
childLink: 'focus-visible:before:ring-info'
},
warning: {
link: 'focus-visible:before:ring-warning',
childLink: 'focus-visible:before:ring-warning'
},
error: {
link: 'focus-visible:before:ring-error',
childLink: 'focus-visible:before:ring-error'
},
neutral: {
link: 'focus-visible:before:ring-inverted',
childLink: 'focus-visible:before:ring-inverted'
}
},
highlightColor: {
primary: '',
secondary: '',
success: '',
info: '',
warning: '',
error: '',
neutral: ''
},
variant: {
pill: '',
link: ''
},
orientation: {
horizontal: {
root: 'items-center justify-between',
list: 'flex items-center',
item: 'py-2',
link: 'px-2.5 py-1.5 before:inset-x-px before:inset-y-0',
childList: 'grid p-2',
childLink: 'px-3 py-2 gap-2 before:inset-x-px before:inset-y-0',
childLinkLabel: 'font-medium',
content: 'absolute top-0 left-0 w-full max-h-[70vh] overflow-y-auto'
},
vertical: {
root: 'flex-col',
link: 'flex-row px-2.5 py-1.5 before:inset-y-px before:inset-x-0',
childLabel: 'px-1.5 py-0.5',
childLink: 'p-1.5 gap-1.5 before:inset-y-px before:inset-x-0'
}
},
contentOrientation: {
horizontal: {
viewportWrapper: 'justify-center',
content: 'data-[motion=from-start]:animate-[enter-from-left_200ms_ease] data-[motion=from-end]:animate-[enter-from-right_200ms_ease] data-[motion=to-start]:animate-[exit-to-left_200ms_ease] data-[motion=to-end]:animate-[exit-to-right_200ms_ease]'
},
vertical: {
viewport: 'sm:w-(--reka-navigation-menu-viewport-width) left-(--reka-navigation-menu-viewport-left)'
}
},
active: {
true: {
childLink: 'before:bg-elevated text-highlighted',
childLinkIcon: 'text-default'
},
false: {
link: 'text-muted',
linkLeadingIcon: 'text-dimmed',
childLink: [
'hover:before:bg-elevated/50 text-default hover:text-highlighted',
'transition-colors before:transition-colors'
],
childLinkIcon: [
'text-dimmed group-hover:text-default',
'transition-colors'
]
}
},
disabled: {
true: {
link: 'cursor-not-allowed opacity-75'
}
},
highlight: {
true: ''
},
level: {
true: ''
},
collapsed: {
true: ''
}
},
compoundVariants: [
{
orientation: 'horizontal',
contentOrientation: 'horizontal',
class: {
childList: 'grid-cols-2 gap-2'
}
},
{
orientation: 'horizontal',
contentOrientation: 'vertical',
class: {
childList: 'gap-1',
content: 'w-60'
}
},
{
orientation: 'vertical',
collapsed: false,
class: {
childList: 'ms-5 border-s border-default',
childItem: 'ps-1.5 -ms-px',
content: 'data-[state=open]:animate-[collapsible-down_200ms_ease-out] data-[state=closed]:animate-[collapsible-up_200ms_ease-out] overflow-hidden'
}
},
{
orientation: 'vertical',
collapsed: true,
class: {
link: 'px-1.5',
content: 'shadow-sm rounded-sm min-h-6 p-1'
}
},
{
orientation: 'horizontal',
highlight: true,
class: {
link: [
'after:absolute after:-bottom-2 after:inset-x-2.5 after:block after:h-px after:rounded-full',
'after:transition-colors'
]
}
},
{
orientation: 'vertical',
highlight: true,
level: true,
class: {
link: [
'after:absolute after:-start-1.5 after:inset-y-0.5 after:block after:w-px after:rounded-full',
'after:transition-colors'
]
}
},
{
disabled: false,
active: false,
variant: 'pill',
class: {
link: [
'hover:text-highlighted hover:before:bg-elevated/50',
'transition-colors before:transition-colors'
],
linkLeadingIcon: [
'group-hover:text-default',
'transition-colors'
]
}
},
{
disabled: false,
active: false,
variant: 'pill',
orientation: 'horizontal',
class: {
link: 'data-[state=open]:text-highlighted',
linkLeadingIcon: 'group-data-[state=open]:text-default'
}
},
{
disabled: false,
variant: 'pill',
highlight: true,
orientation: 'horizontal',
class: {
link: 'data-[state=open]:before:bg-elevated/50'
}
},
{
disabled: false,
variant: 'pill',
highlight: false,
active: false,
orientation: 'horizontal',
class: {
link: 'data-[state=open]:before:bg-elevated/50'
}
},
{
color: 'primary',
variant: 'pill',
active: true,
class: {
link: 'text-primary',
linkLeadingIcon: 'text-primary group-data-[state=open]:text-primary'
}
},
{
color: 'neutral',
variant: 'pill',
active: true,
class: {
link: 'text-highlighted',
linkLeadingIcon: 'text-highlighted group-data-[state=open]:text-highlighted'
}
},
{
variant: 'pill',
active: true,
highlight: false,
class: {
link: 'before:bg-elevated'
}
},
{
variant: 'pill',
active: true,
highlight: true,
disabled: false,
class: {
link: [
'hover:before:bg-elevated/50',
'before:transition-colors'
]
}
},
{
disabled: false,
active: false,
variant: 'link',
class: {
link: [
'hover:text-highlighted',
'transition-colors'
],
linkLeadingIcon: [
'group-hover:text-default',
'transition-colors'
]
}
},
{
disabled: false,
active: false,
variant: 'link',
orientation: 'horizontal',
class: {
link: 'data-[state=open]:text-highlighted',
linkLeadingIcon: 'group-data-[state=open]:text-default'
}
},
{
color: 'primary',
variant: 'link',
active: true,
class: {
link: 'text-primary',
linkLeadingIcon: 'text-primary group-data-[state=open]:text-primary'
}
},
{
color: 'neutral',
variant: 'link',
active: true,
class: {
link: 'text-highlighted',
linkLeadingIcon: 'text-highlighted group-data-[state=open]:text-highlighted'
}
},
{
highlightColor: 'primary',
highlight: true,
level: true,
active: true,
class: {
link: 'after:bg-primary'
}
},
{
highlightColor: 'neutral',
highlight: true,
level: true,
active: true,
class: {
link: 'after:bg-inverted'
}
}
],
defaultVariants: {
color: 'primary',
highlightColor: 'primary',
variant: 'pill'
}
}
}
})
]
})
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import uiPro from '@nuxt/ui-pro/vite'
export default defineConfig({
plugins: [
vue(),
uiPro({
ui: {
navigationMenu: {
slots: {
root: 'relative flex gap-1.5 [&>div]:min-w-0',
list: 'isolate min-w-0',
label: 'w-full flex items-center gap-1.5 font-semibold text-xs/5 text-highlighted px-2.5 py-1.5',
item: 'min-w-0',
link: 'group relative w-full flex items-center gap-1.5 font-medium text-sm before:absolute before:z-[-1] before:rounded-md focus:outline-none focus-visible:outline-none dark:focus-visible:outline-none focus-visible:before:ring-inset focus-visible:before:ring-2',
linkLeadingIcon: 'shrink-0 size-5',
linkLeadingAvatar: 'shrink-0',
linkLeadingAvatarSize: '2xs',
linkTrailing: 'group ms-auto inline-flex gap-1.5 items-center',
linkTrailingBadge: 'shrink-0',
linkTrailingBadgeSize: 'sm',
linkTrailingIcon: 'size-5 transform shrink-0 group-data-[state=open]:rotate-180 transition-transform duration-200',
linkLabel: 'truncate',
linkLabelExternalIcon: 'inline-block size-3 align-top text-dimmed',
childList: 'isolate',
childLabel: 'text-xs text-highlighted',
childItem: '',
childLink: 'group relative size-full flex items-start text-start text-sm before:absolute before:z-[-1] before:rounded-md focus:outline-none focus-visible:outline-none dark:focus-visible:outline-none focus-visible:before:ring-inset focus-visible:before:ring-2',
childLinkWrapper: 'min-w-0',
childLinkIcon: 'size-5 shrink-0',
childLinkLabel: 'truncate',
childLinkLabelExternalIcon: 'inline-block size-3 align-top text-dimmed',
childLinkDescription: 'text-muted',
separator: 'px-2 h-px bg-border',
viewportWrapper: 'absolute top-full left-0 flex w-full',
viewport: 'relative overflow-hidden bg-default shadow-lg rounded-md ring ring-default h-(--reka-navigation-menu-viewport-height) w-full transition-[width,height,left] duration-200 origin-[top_center] data-[state=open]:animate-[scale-in_100ms_ease-out] data-[state=closed]:animate-[scale-out_100ms_ease-in] z-[1]',
content: '',
indicator: 'absolute data-[state=visible]:animate-[fade-in_100ms_ease-out] data-[state=hidden]:animate-[fade-out_100ms_ease-in] data-[state=hidden]:opacity-0 bottom-0 z-[2] w-(--reka-navigation-menu-indicator-size) translate-x-(--reka-navigation-menu-indicator-position) flex h-2.5 items-end justify-center overflow-hidden transition-[translate,width] duration-200',
arrow: 'relative top-[50%] size-2.5 rotate-45 border border-default bg-default z-[1] rounded-xs'
},
variants: {
color: {
primary: {
link: 'focus-visible:before:ring-primary',
childLink: 'focus-visible:before:ring-primary'
},
secondary: {
link: 'focus-visible:before:ring-secondary',
childLink: 'focus-visible:before:ring-secondary'
},
success: {
link: 'focus-visible:before:ring-success',
childLink: 'focus-visible:before:ring-success'
},
info: {
link: 'focus-visible:before:ring-info',
childLink: 'focus-visible:before:ring-info'
},
warning: {
link: 'focus-visible:before:ring-warning',
childLink: 'focus-visible:before:ring-warning'
},
error: {
link: 'focus-visible:before:ring-error',
childLink: 'focus-visible:before:ring-error'
},
neutral: {
link: 'focus-visible:before:ring-inverted',
childLink: 'focus-visible:before:ring-inverted'
}
},
highlightColor: {
primary: '',
secondary: '',
success: '',
info: '',
warning: '',
error: '',
neutral: ''
},
variant: {
pill: '',
link: ''
},
orientation: {
horizontal: {
root: 'items-center justify-between',
list: 'flex items-center',
item: 'py-2',
link: 'px-2.5 py-1.5 before:inset-x-px before:inset-y-0',
childList: 'grid p-2',
childLink: 'px-3 py-2 gap-2 before:inset-x-px before:inset-y-0',
childLinkLabel: 'font-medium',
content: 'absolute top-0 left-0 w-full max-h-[70vh] overflow-y-auto'
},
vertical: {
root: 'flex-col',
link: 'flex-row px-2.5 py-1.5 before:inset-y-px before:inset-x-0',
childLabel: 'px-1.5 py-0.5',
childLink: 'p-1.5 gap-1.5 before:inset-y-px before:inset-x-0'
}
},
contentOrientation: {
horizontal: {
viewportWrapper: 'justify-center',
content: 'data-[motion=from-start]:animate-[enter-from-left_200ms_ease] data-[motion=from-end]:animate-[enter-from-right_200ms_ease] data-[motion=to-start]:animate-[exit-to-left_200ms_ease] data-[motion=to-end]:animate-[exit-to-right_200ms_ease]'
},
vertical: {
viewport: 'sm:w-(--reka-navigation-menu-viewport-width) left-(--reka-navigation-menu-viewport-left)'
}
},
active: {
true: {
childLink: 'before:bg-elevated text-highlighted',
childLinkIcon: 'text-default'
},
false: {
link: 'text-muted',
linkLeadingIcon: 'text-dimmed',
childLink: [
'hover:before:bg-elevated/50 text-default hover:text-highlighted',
'transition-colors before:transition-colors'
],
childLinkIcon: [
'text-dimmed group-hover:text-default',
'transition-colors'
]
}
},
disabled: {
true: {
link: 'cursor-not-allowed opacity-75'
}
},
highlight: {
true: ''
},
level: {
true: ''
},
collapsed: {
true: ''
}
},
compoundVariants: [
{
orientation: 'horizontal',
contentOrientation: 'horizontal',
class: {
childList: 'grid-cols-2 gap-2'
}
},
{
orientation: 'horizontal',
contentOrientation: 'vertical',
class: {
childList: 'gap-1',
content: 'w-60'
}
},
{
orientation: 'vertical',
collapsed: false,
class: {
childList: 'ms-5 border-s border-default',
childItem: 'ps-1.5 -ms-px',
content: 'data-[state=open]:animate-[collapsible-down_200ms_ease-out] data-[state=closed]:animate-[collapsible-up_200ms_ease-out] overflow-hidden'
}
},
{
orientation: 'vertical',
collapsed: true,
class: {
link: 'px-1.5',
content: 'shadow-sm rounded-sm min-h-6 p-1'
}
},
{
orientation: 'horizontal',
highlight: true,
class: {
link: [
'after:absolute after:-bottom-2 after:inset-x-2.5 after:block after:h-px after:rounded-full',
'after:transition-colors'
]
}
},
{
orientation: 'vertical',
highlight: true,
level: true,
class: {
link: [
'after:absolute after:-start-1.5 after:inset-y-0.5 after:block after:w-px after:rounded-full',
'after:transition-colors'
]
}
},
{
disabled: false,
active: false,
variant: 'pill',
class: {
link: [
'hover:text-highlighted hover:before:bg-elevated/50',
'transition-colors before:transition-colors'
],
linkLeadingIcon: [
'group-hover:text-default',
'transition-colors'
]
}
},
{
disabled: false,
active: false,
variant: 'pill',
orientation: 'horizontal',
class: {
link: 'data-[state=open]:text-highlighted',
linkLeadingIcon: 'group-data-[state=open]:text-default'
}
},
{
disabled: false,
variant: 'pill',
highlight: true,
orientation: 'horizontal',
class: {
link: 'data-[state=open]:before:bg-elevated/50'
}
},
{
disabled: false,
variant: 'pill',
highlight: false,
active: false,
orientation: 'horizontal',
class: {
link: 'data-[state=open]:before:bg-elevated/50'
}
},
{
color: 'primary',
variant: 'pill',
active: true,
class: {
link: 'text-primary',
linkLeadingIcon: 'text-primary group-data-[state=open]:text-primary'
}
},
{
color: 'neutral',
variant: 'pill',
active: true,
class: {
link: 'text-highlighted',
linkLeadingIcon: 'text-highlighted group-data-[state=open]:text-highlighted'
}
},
{
variant: 'pill',
active: true,
highlight: false,
class: {
link: 'before:bg-elevated'
}
},
{
variant: 'pill',
active: true,
highlight: true,
disabled: false,
class: {
link: [
'hover:before:bg-elevated/50',
'before:transition-colors'
]
}
},
{
disabled: false,
active: false,
variant: 'link',
class: {
link: [
'hover:text-highlighted',
'transition-colors'
],
linkLeadingIcon: [
'group-hover:text-default',
'transition-colors'
]
}
},
{
disabled: false,
active: false,
variant: 'link',
orientation: 'horizontal',
class: {
link: 'data-[state=open]:text-highlighted',
linkLeadingIcon: 'group-data-[state=open]:text-default'
}
},
{
color: 'primary',
variant: 'link',
active: true,
class: {
link: 'text-primary',
linkLeadingIcon: 'text-primary group-data-[state=open]:text-primary'
}
},
{
color: 'neutral',
variant: 'link',
active: true,
class: {
link: 'text-highlighted',
linkLeadingIcon: 'text-highlighted group-data-[state=open]:text-highlighted'
}
},
{
highlightColor: 'primary',
highlight: true,
level: true,
active: true,
class: {
link: 'after:bg-primary'
}
},
{
highlightColor: 'neutral',
highlight: true,
level: true,
active: true,
class: {
link: 'after:bg-inverted'
}
}
],
defaultVariants: {
color: 'primary',
highlightColor: 'primary',
variant: 'pill'
}
}
}
})
]
})