Sidebar
Gebruik
De zijbalkcomponent is een stand-alone, vaste zijbalk die de pagina-inhoud pusht. Op desktop wordt het inline weergegeven en kan het worden samengevouwen; op mobiel opent het een Modal, Slideover of Drawer component.
Gebruik de header-, default- en footer-slots om de inhoud van de zijbalk aan te passen.
De v-model:open-richtlijn is viewport-bewust: op desktop bestuurt het de uitgebreide / samengevouwen status, op mobiel bestuurt het het menu.
<script setup lang="ts">
import type { DropdownMenuItem, NavigationMenuItem } from '@nuxt/ui'
const open = ref(true)
const colorMode = useColorMode()
const teams = ref([
{
label: 'Nuxt',
avatar: {
src: 'https://github.com/nuxt.png',
alt: 'Nuxt'
}
},
{
label: 'Vue',
avatar: {
src: 'https://github.com/vuejs.png',
alt: 'Vue'
}
},
{
label: 'UnJS',
avatar: {
src: 'https://github.com/unjs.png',
alt: 'UnJS'
}
}
])
const selectedTeam = ref(teams.value[0])
const teamsItems = computed<DropdownMenuItem[][]>(() => {
return [
teams.value.map((team, index) => ({
...team,
kbds: ['meta', String(index + 1)],
onSelect() {
selectedTeam.value = team
}
})),
[
{
label: 'Create team',
icon: 'i-lucide-circle-plus'
}
]
]
})
function getItems(state: 'collapsed' | 'expanded') {
return [
{
label: 'Inbox',
icon: 'i-lucide-inbox',
badge: '4'
},
{
label: 'Issues',
icon: 'i-lucide-square-dot'
},
{
label: 'Activity',
icon: 'i-lucide-square-activity'
},
{
label: 'Settings',
icon: 'i-lucide-settings',
defaultOpen: true,
children:
state === 'expanded'
? [
{
label: 'General',
icon: 'i-lucide-house'
},
{
label: 'Team',
icon: 'i-lucide-users'
},
{
label: 'Billing',
icon: 'i-lucide-credit-card'
}
]
: []
}
] satisfies NavigationMenuItem[]
}
const user = ref({
name: 'Benjamin Canac',
avatar: {
src: 'https://github.com/benjamincanac.png',
alt: 'Benjamin Canac'
}
})
const userItems = computed<DropdownMenuItem[][]>(() => [
[
{
label: 'Profile',
icon: 'i-lucide-user'
},
{
label: 'Billing',
icon: 'i-lucide-credit-card'
},
{
label: 'Settings',
icon: 'i-lucide-settings',
to: '/settings'
}
],
[
{
label: 'Appearance',
icon: 'i-lucide-sun-moon',
children: [
{
label: 'Light',
icon: 'i-lucide-sun',
type: 'checkbox',
checked: colorMode.value === 'light',
onUpdateChecked(checked: boolean) {
if (checked) {
colorMode.preference = 'light'
}
},
onSelect(e: Event) {
e.preventDefault()
}
},
{
label: 'Dark',
icon: 'i-lucide-moon',
type: 'checkbox',
checked: colorMode.value === 'dark',
onUpdateChecked(checked: boolean) {
if (checked) {
colorMode.preference = 'dark'
}
},
onSelect(e: Event) {
e.preventDefault()
}
}
]
}
],
[
{
label: 'GitHub',
icon: 'i-simple-icons-github',
to: 'https://github.com/nuxt/ui',
target: '_blank'
},
{
label: 'Log out',
icon: 'i-lucide-log-out'
}
]
])
defineShortcuts(extractShortcuts(teamsItems.value))
</script>
<template>
<div class="flex flex-1">
<USidebar
v-model:open="open"
collapsible="icon"
rail
:ui="{
container: 'h-full',
inner: 'bg-elevated/25 divide-transparent',
body: 'py-0'
}"
>
<template #header>
<UDropdownMenu
:items="teamsItems"
:content="{ align: 'start', collisionPadding: 12 }"
:ui="{ content: 'w-(--reka-dropdown-menu-trigger-width) min-w-48' }"
>
<UButton
v-bind="selectedTeam"
trailing-icon="i-lucide-chevrons-up-down"
color="neutral"
variant="ghost"
square
class="w-full data-[state=open]:bg-elevated overflow-hidden"
:ui="{
trailingIcon: 'text-dimmed ms-auto'
}"
/>
</UDropdownMenu>
</template>
<template #default="{ state }">
<UNavigationMenu
:key="state"
:items="getItems(state)"
orientation="vertical"
:ui="{ link: 'p-1.5 overflow-hidden' }"
/>
</template>
<template #footer>
<UDropdownMenu
:items="userItems"
:content="{ align: 'center', collisionPadding: 12 }"
:ui="{ content: 'w-(--reka-dropdown-menu-trigger-width) min-w-48' }"
>
<UButton
v-bind="user"
:label="user?.name"
trailing-icon="i-lucide-chevrons-up-down"
color="neutral"
variant="ghost"
square
class="w-full data-[state=open]:bg-elevated overflow-hidden"
:ui="{
trailingIcon: 'text-dimmed ms-auto'
}"
/>
</UDropdownMenu>
</template>
</USidebar>
<div class="flex-1 flex flex-col">
<div class="h-(--ui-header-height) shrink-0 flex items-center px-4 border-b border-default">
<UButton
icon="i-lucide-panel-left"
color="neutral"
variant="ghost"
aria-label="Toggle sidebar"
@click="open = !open"
/>
</div>
<div class="flex-1 p-4">
<Placeholder class="size-full" />
</div>
</div>
</div>
</template><script setup lang="ts">
import { ref, computed } from 'vue'
import { useColorMode } from '@vueuse/core'
import type { DropdownMenuItem, NavigationMenuItem } from '@nuxt/ui'
const open = ref(true)
const colorMode = useColorMode()
const teams = ref([
{
label: 'Nuxt',
avatar: {
src: 'https://github.com/nuxt.png',
alt: 'Nuxt'
}
},
{
label: 'Vue',
avatar: {
src: 'https://github.com/vuejs.png',
alt: 'Vue'
}
},
{
label: 'UnJS',
avatar: {
src: 'https://github.com/unjs.png',
alt: 'UnJS'
}
}
])
const selectedTeam = ref(teams.value[0])
const teamsItems = computed<DropdownMenuItem[][]>(() => {
return [
teams.value.map((team, index) => ({
...team,
kbds: ['meta', String(index + 1)],
onSelect() {
selectedTeam.value = team
}
})),
[
{
label: 'Create team',
icon: 'i-lucide-circle-plus'
}
]
]
})
function getItems(state: 'collapsed' | 'expanded') {
return [
{
label: 'Inbox',
icon: 'i-lucide-inbox',
badge: '4'
},
{
label: 'Issues',
icon: 'i-lucide-square-dot'
},
{
label: 'Activity',
icon: 'i-lucide-square-activity'
},
{
label: 'Settings',
icon: 'i-lucide-settings',
defaultOpen: true,
children:
state === 'expanded'
? [
{
label: 'General',
icon: 'i-lucide-house'
},
{
label: 'Team',
icon: 'i-lucide-users'
},
{
label: 'Billing',
icon: 'i-lucide-credit-card'
}
]
: []
}
] satisfies NavigationMenuItem[]
}
const user = ref({
name: 'Benjamin Canac',
avatar: {
src: 'https://github.com/benjamincanac.png',
alt: 'Benjamin Canac'
}
})
const userItems = computed<DropdownMenuItem[][]>(() => [
[
{
label: 'Profile',
icon: 'i-lucide-user'
},
{
label: 'Billing',
icon: 'i-lucide-credit-card'
},
{
label: 'Settings',
icon: 'i-lucide-settings',
to: '/settings'
}
],
[
{
label: 'Appearance',
icon: 'i-lucide-sun-moon',
children: [
{
label: 'Light',
icon: 'i-lucide-sun',
type: 'checkbox',
checked: colorMode.value === 'light',
onUpdateChecked(checked: boolean) {
if (checked) {
colorMode.preference = 'light'
}
},
onSelect(e: Event) {
e.preventDefault()
}
},
{
label: 'Dark',
icon: 'i-lucide-moon',
type: 'checkbox',
checked: colorMode.value === 'dark',
onUpdateChecked(checked: boolean) {
if (checked) {
colorMode.preference = 'dark'
}
},
onSelect(e: Event) {
e.preventDefault()
}
}
]
}
],
[
{
label: 'GitHub',
icon: 'i-simple-icons-github',
to: 'https://github.com/nuxt/ui',
target: '_blank'
},
{
label: 'Log out',
icon: 'i-lucide-log-out'
}
]
])
defineShortcuts(extractShortcuts(teamsItems.value))
</script>
<template>
<div class="flex flex-1">
<USidebar
v-model:open="open"
collapsible="icon"
rail
:ui="{
container: 'h-full',
inner: 'bg-elevated/25 divide-transparent',
body: 'py-0'
}"
>
<template #header>
<UDropdownMenu
:items="teamsItems"
:content="{ align: 'start', collisionPadding: 12 }"
:ui="{ content: 'w-(--reka-dropdown-menu-trigger-width) min-w-48' }"
>
<UButton
v-bind="selectedTeam"
trailing-icon="i-lucide-chevrons-up-down"
color="neutral"
variant="ghost"
square
class="w-full data-[state=open]:bg-elevated overflow-hidden"
:ui="{
trailingIcon: 'text-dimmed ms-auto'
}"
/>
</UDropdownMenu>
</template>
<template #default="{ state }">
<UNavigationMenu
:key="state"
:items="getItems(state)"
orientation="vertical"
:ui="{ link: 'p-1.5 overflow-hidden' }"
/>
</template>
<template #footer>
<UDropdownMenu
:items="userItems"
:content="{ align: 'center', collisionPadding: 12 }"
:ui="{ content: 'w-(--reka-dropdown-menu-trigger-width) min-w-48' }"
>
<UButton
v-bind="user"
:label="user?.name"
trailing-icon="i-lucide-chevrons-up-down"
color="neutral"
variant="ghost"
square
class="w-full data-[state=open]:bg-elevated overflow-hidden"
:ui="{
trailingIcon: 'text-dimmed ms-auto'
}"
/>
</UDropdownMenu>
</template>
</USidebar>
<div class="flex-1 flex flex-col">
<div class="h-(--ui-header-height) shrink-0 flex items-center px-4 border-b border-default">
<UButton
icon="i-lucide-panel-left"
color="neutral"
variant="ghost"
aria-label="Toggle sidebar"
@click="open = !open"
/>
</div>
<div class="flex-1 p-4">
<Placeholder class="size-full" />
</div>
</div>
</div>
</template>Variant
Gebruik de variant prop om de visuele stijl van de zijbalk te wijzigen. Standaard sidebar.
<script setup lang="ts">
import type { NavigationMenuItem, SidebarProps } from '@nuxt/ui'
// Ignore the props for the example
defineProps<Pick<SidebarProps, 'variant' | 'collapsible' | 'side'>>()
const open = ref(true)
const items: NavigationMenuItem[] = [
{
label: 'Home',
icon: 'i-lucide-house',
active: true
},
{
label: 'Inbox',
icon: 'i-lucide-inbox',
badge: '4'
},
{
label: 'Contacts',
icon: 'i-lucide-users'
}
]
</script>
<template>
<div
class="flex flex-1"
:class="[
variant === 'inset' && 'bg-neutral-50 dark:bg-neutral-950',
side === 'right' && 'flex-row-reverse'
]"
>
<USidebar
v-model:open="open"
:variant="variant"
:collapsible="collapsible"
:side="side"
:ui="{
container: 'h-full'
}"
>
<template #header>
<UIcon name="i-logos-nuxt-icon" class="size-8" />
</template>
<UNavigationMenu
:items="items"
orientation="vertical"
:ui="{ link: 'p-1.5 overflow-hidden' }"
/>
</USidebar>
<div
class="flex-1 flex flex-col overflow-hidden lg:peer-data-[variant=floating]:my-4 peer-data-[variant=inset]:m-4 lg:peer-data-[variant=inset]:not-peer-data-[collapsible=offcanvas]:ms-0 peer-data-[variant=inset]:rounded-xl peer-data-[variant=inset]:shadow-sm peer-data-[variant=inset]:ring peer-data-[variant=inset]:ring-default bg-default"
>
<div
class="h-(--ui-header-height) shrink-0 flex items-center px-4"
:class="[
variant !== 'floating' && 'border-b border-default',
side === 'right' && 'justify-end'
]"
>
<UButton
:icon="side === 'left' ? 'i-lucide-panel-left' : 'i-lucide-panel-right'"
color="neutral"
variant="ghost"
aria-label="Toggle sidebar"
@click="open = !open"
/>
</div>
<div class="flex-1 p-4">
<Placeholder class="size-full" />
</div>
</div>
</div>
</template><script setup lang="ts">
import { ref } from 'vue'
import type { NavigationMenuItem, SidebarProps } from '@nuxt/ui'
// Ignore the props for the example
defineProps<Pick<SidebarProps, 'variant' | 'collapsible' | 'side'>>()
const open = ref(true)
const items: NavigationMenuItem[] = [
{
label: 'Home',
icon: 'i-lucide-house',
active: true
},
{
label: 'Inbox',
icon: 'i-lucide-inbox',
badge: '4'
},
{
label: 'Contacts',
icon: 'i-lucide-users'
}
]
</script>
<template>
<div
class="flex flex-1"
:class="[
variant === 'inset' && 'bg-neutral-50 dark:bg-neutral-950',
side === 'right' && 'flex-row-reverse'
]"
>
<USidebar
v-model:open="open"
:variant="variant"
:collapsible="collapsible"
:side="side"
:ui="{
container: 'h-full'
}"
>
<template #header>
<UIcon name="i-logos-nuxt-icon" class="size-8" />
</template>
<UNavigationMenu
:items="items"
orientation="vertical"
:ui="{ link: 'p-1.5 overflow-hidden' }"
/>
</USidebar>
<div
class="flex-1 flex flex-col overflow-hidden lg:peer-data-[variant=floating]:my-4 peer-data-[variant=inset]:m-4 lg:peer-data-[variant=inset]:not-peer-data-[collapsible=offcanvas]:ms-0 peer-data-[variant=inset]:rounded-xl peer-data-[variant=inset]:shadow-sm peer-data-[variant=inset]:ring peer-data-[variant=inset]:ring-default bg-default"
>
<div
class="h-(--ui-header-height) shrink-0 flex items-center px-4"
:class="[
variant !== 'floating' && 'border-b border-default',
side === 'right' && 'justify-end'
]"
>
<UButton
:icon="side === 'left' ? 'i-lucide-panel-left' : 'i-lucide-panel-right'"
color="neutral"
variant="ghost"
aria-label="Toggle sidebar"
@click="open = !open"
/>
</div>
<div class="flex-1 p-4">
<Placeholder class="size-full" />
</div>
</div>
</div>
</template>Inklapbaar
Gebruik de collapsible-prop om het samenvouwgedrag van de zijbalk te wijzigen. Standaard offcanvas.
offcanvas: De zijbalk schuift volledig uit beeld.icon: De zijbalk verkleint tot alleen pictogrambreedte.none: De zijbalk is niet inklapbaar.
<script setup lang="ts">
import type { NavigationMenuItem, SidebarProps } from '@nuxt/ui'
// Ignore the props for the example
defineProps<Pick<SidebarProps, 'variant' | 'collapsible' | 'side'>>()
const open = ref(true)
const items: NavigationMenuItem[] = [
{
label: 'Home',
icon: 'i-lucide-house',
active: true
},
{
label: 'Inbox',
icon: 'i-lucide-inbox',
badge: '4'
},
{
label: 'Contacts',
icon: 'i-lucide-users'
}
]
</script>
<template>
<div
class="flex flex-1"
:class="[
variant === 'inset' && 'bg-neutral-50 dark:bg-neutral-950',
side === 'right' && 'flex-row-reverse'
]"
>
<USidebar
v-model:open="open"
:variant="variant"
:collapsible="collapsible"
:side="side"
:ui="{
container: 'h-full'
}"
>
<template #header>
<UIcon name="i-logos-nuxt-icon" class="size-8" />
</template>
<UNavigationMenu
:items="items"
orientation="vertical"
:ui="{ link: 'p-1.5 overflow-hidden' }"
/>
</USidebar>
<div
class="flex-1 flex flex-col overflow-hidden lg:peer-data-[variant=floating]:my-4 peer-data-[variant=inset]:m-4 lg:peer-data-[variant=inset]:not-peer-data-[collapsible=offcanvas]:ms-0 peer-data-[variant=inset]:rounded-xl peer-data-[variant=inset]:shadow-sm peer-data-[variant=inset]:ring peer-data-[variant=inset]:ring-default bg-default"
>
<div
class="h-(--ui-header-height) shrink-0 flex items-center px-4"
:class="[
variant !== 'floating' && 'border-b border-default',
side === 'right' && 'justify-end'
]"
>
<UButton
:icon="side === 'left' ? 'i-lucide-panel-left' : 'i-lucide-panel-right'"
color="neutral"
variant="ghost"
aria-label="Toggle sidebar"
@click="open = !open"
/>
</div>
<div class="flex-1 p-4">
<Placeholder class="size-full" />
</div>
</div>
</div>
</template><script setup lang="ts">
import { ref } from 'vue'
import type { NavigationMenuItem, SidebarProps } from '@nuxt/ui'
// Ignore the props for the example
defineProps<Pick<SidebarProps, 'variant' | 'collapsible' | 'side'>>()
const open = ref(true)
const items: NavigationMenuItem[] = [
{
label: 'Home',
icon: 'i-lucide-house',
active: true
},
{
label: 'Inbox',
icon: 'i-lucide-inbox',
badge: '4'
},
{
label: 'Contacts',
icon: 'i-lucide-users'
}
]
</script>
<template>
<div
class="flex flex-1"
:class="[
variant === 'inset' && 'bg-neutral-50 dark:bg-neutral-950',
side === 'right' && 'flex-row-reverse'
]"
>
<USidebar
v-model:open="open"
:variant="variant"
:collapsible="collapsible"
:side="side"
:ui="{
container: 'h-full'
}"
>
<template #header>
<UIcon name="i-logos-nuxt-icon" class="size-8" />
</template>
<UNavigationMenu
:items="items"
orientation="vertical"
:ui="{ link: 'p-1.5 overflow-hidden' }"
/>
</USidebar>
<div
class="flex-1 flex flex-col overflow-hidden lg:peer-data-[variant=floating]:my-4 peer-data-[variant=inset]:m-4 lg:peer-data-[variant=inset]:not-peer-data-[collapsible=offcanvas]:ms-0 peer-data-[variant=inset]:rounded-xl peer-data-[variant=inset]:shadow-sm peer-data-[variant=inset]:ring peer-data-[variant=inset]:ring-default bg-default"
>
<div
class="h-(--ui-header-height) shrink-0 flex items-center px-4"
:class="[
variant !== 'floating' && 'border-b border-default',
side === 'right' && 'justify-end'
]"
>
<UButton
:icon="side === 'left' ? 'i-lucide-panel-left' : 'i-lucide-panel-right'"
color="neutral"
variant="ghost"
aria-label="Toggle sidebar"
@click="open = !open"
/>
</div>
<div class="flex-1 p-4">
<Placeholder class="size-full" />
</div>
</div>
</div>
</template>state in de sleufsteunen om de inhoud van de zijbalk aan te passen wanneer deze is samengevouwen.Zijde
Gebruik de side prop om de zijkant van de zijbalk te wijzigen. Standaard left.
<script setup lang="ts">
import type { NavigationMenuItem, SidebarProps } from '@nuxt/ui'
// Ignore the props for the example
defineProps<Pick<SidebarProps, 'variant' | 'collapsible' | 'side'>>()
const open = ref(true)
const items: NavigationMenuItem[] = [
{
label: 'Home',
icon: 'i-lucide-house',
active: true
},
{
label: 'Inbox',
icon: 'i-lucide-inbox',
badge: '4'
},
{
label: 'Contacts',
icon: 'i-lucide-users'
}
]
</script>
<template>
<div
class="flex flex-1"
:class="[
variant === 'inset' && 'bg-neutral-50 dark:bg-neutral-950',
side === 'right' && 'flex-row-reverse'
]"
>
<USidebar
v-model:open="open"
:variant="variant"
:collapsible="collapsible"
:side="side"
:ui="{
container: 'h-full'
}"
>
<template #header>
<UIcon name="i-logos-nuxt-icon" class="size-8" />
</template>
<UNavigationMenu
:items="items"
orientation="vertical"
:ui="{ link: 'p-1.5 overflow-hidden' }"
/>
</USidebar>
<div
class="flex-1 flex flex-col overflow-hidden lg:peer-data-[variant=floating]:my-4 peer-data-[variant=inset]:m-4 lg:peer-data-[variant=inset]:not-peer-data-[collapsible=offcanvas]:ms-0 peer-data-[variant=inset]:rounded-xl peer-data-[variant=inset]:shadow-sm peer-data-[variant=inset]:ring peer-data-[variant=inset]:ring-default bg-default"
>
<div
class="h-(--ui-header-height) shrink-0 flex items-center px-4"
:class="[
variant !== 'floating' && 'border-b border-default',
side === 'right' && 'justify-end'
]"
>
<UButton
:icon="side === 'left' ? 'i-lucide-panel-left' : 'i-lucide-panel-right'"
color="neutral"
variant="ghost"
aria-label="Toggle sidebar"
@click="open = !open"
/>
</div>
<div class="flex-1 p-4">
<Placeholder class="size-full" />
</div>
</div>
</div>
</template><script setup lang="ts">
import { ref } from 'vue'
import type { NavigationMenuItem, SidebarProps } from '@nuxt/ui'
// Ignore the props for the example
defineProps<Pick<SidebarProps, 'variant' | 'collapsible' | 'side'>>()
const open = ref(true)
const items: NavigationMenuItem[] = [
{
label: 'Home',
icon: 'i-lucide-house',
active: true
},
{
label: 'Inbox',
icon: 'i-lucide-inbox',
badge: '4'
},
{
label: 'Contacts',
icon: 'i-lucide-users'
}
]
</script>
<template>
<div
class="flex flex-1"
:class="[
variant === 'inset' && 'bg-neutral-50 dark:bg-neutral-950',
side === 'right' && 'flex-row-reverse'
]"
>
<USidebar
v-model:open="open"
:variant="variant"
:collapsible="collapsible"
:side="side"
:ui="{
container: 'h-full'
}"
>
<template #header>
<UIcon name="i-logos-nuxt-icon" class="size-8" />
</template>
<UNavigationMenu
:items="items"
orientation="vertical"
:ui="{ link: 'p-1.5 overflow-hidden' }"
/>
</USidebar>
<div
class="flex-1 flex flex-col overflow-hidden lg:peer-data-[variant=floating]:my-4 peer-data-[variant=inset]:m-4 lg:peer-data-[variant=inset]:not-peer-data-[collapsible=offcanvas]:ms-0 peer-data-[variant=inset]:rounded-xl peer-data-[variant=inset]:shadow-sm peer-data-[variant=inset]:ring peer-data-[variant=inset]:ring-default bg-default"
>
<div
class="h-(--ui-header-height) shrink-0 flex items-center px-4"
:class="[
variant !== 'floating' && 'border-b border-default',
side === 'right' && 'justify-end'
]"
>
<UButton
:icon="side === 'left' ? 'i-lucide-panel-left' : 'i-lucide-panel-right'"
color="neutral"
variant="ghost"
aria-label="Toggle sidebar"
@click="open = !open"
/>
</div>
<div class="flex-1 p-4">
<Placeholder class="size-full" />
</div>
</div>
</div>
</template>Titel
Gebruik de title prop om de titel van de sidebar header in te stellen.
<template>
<USidebar title="Navigation">
<Placeholder class="h-full" />
</USidebar>
</template>Beschrijving
Gebruik de description prop om de beschrijving van de sidebar header in te stellen.
<template>
<USidebar title="Navigation" description="Browse your workspace">
<Placeholder class="h-full" />
</USidebar>
</template>Rail
Gebruik de rail prop om een dunne interactieve rand op de zijbalk weer te geven die de samengevouwen status bij klikken omschakelt. De rail wordt alleen weergegeven als collapsible niet none is.
<template>
<USidebar rail collapsible="icon" title="Navigation">
<Placeholder class="h-full" />
</USidebar>
</template>Sluiten
Gebruik de close prop om een sluitknop in de zijbalkkop weer te geven. De sluitknop wordt alleen weergegeven als collapsible niet none is.
U kunt elke eigenschap van de Button component doorgeven om deze aan te passen.
<template>
<USidebar close rail collapsible="icon" title="Navigation">
<Placeholder class="h-full" />
</USidebar>
</template>Sluit pictogram
Gebruik de close-icon prop om de knop Sluiten aan te passen Icon. Standaard i-lucide-x.
<template>
<USidebar
close
close-icon="i-lucide-panel-right-close"
rail
collapsible="icon"
side="right"
title="Navigation"
>
<Placeholder class="h-full" />
</USidebar>
</template>Modus
Gebruik de mode prop om de modus van het zijbalkmenu op mobiel te wijzigen. Standaard ingesteld op slideover.
<script setup lang="ts">
import type { NavigationMenuItem, SidebarProps } from '@nuxt/ui'
// Ignore the props for the example
defineProps<Pick<SidebarProps, 'mode'>>()
const open = ref(true)
const items: NavigationMenuItem[] = [{
label: 'Home',
icon: 'i-lucide-house',
active: true
}, {
label: 'Inbox',
icon: 'i-lucide-inbox',
badge: '4'
}, {
label: 'Contacts',
icon: 'i-lucide-users'
}]
</script>
<template>
<div class="flex flex-1">
<USidebar v-model:open="open" :mode="mode" title="Navigation">
<UNavigationMenu
:items="items"
orientation="vertical"
:ui="{ link: 'p-1.5 overflow-hidden' }"
/>
</USidebar>
<div class="flex-1 flex flex-col">
<div class="h-(--ui-header-height) shrink-0 flex items-center px-4 border-b border-default">
<UButton
icon="i-lucide-panel-left"
color="neutral"
variant="ghost"
aria-label="Toggle sidebar"
@click="open = !open"
/>
</div>
<div class="flex-1 p-4">
<Placeholder class="size-full" />
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import type { NavigationMenuItem, SidebarProps } from '@nuxt/ui'
// Ignore the props for the example
defineProps<Pick<SidebarProps, 'mode'>>()
const open = ref(true)
const items: NavigationMenuItem[] = [{
label: 'Home',
icon: 'i-lucide-house',
active: true
}, {
label: 'Inbox',
icon: 'i-lucide-inbox',
badge: '4'
}, {
label: 'Contacts',
icon: 'i-lucide-users'
}]
</script>
<template>
<div class="flex flex-1">
<USidebar v-model:open="open" :mode="mode" title="Navigation">
<UNavigationMenu
:items="items"
orientation="vertical"
:ui="{ link: 'p-1.5 overflow-hidden' }"
/>
</USidebar>
<div class="flex-1 flex flex-col">
<div class="h-(--ui-header-height) shrink-0 flex items-center px-4 border-b border-default">
<UButton
icon="i-lucide-panel-left"
color="neutral"
variant="ghost"
aria-label="Toggle sidebar"
@click="open = !open"
/>
</div>
<div class="flex-1 p-4">
<Placeholder class="size-full" />
</div>
</div>
</div>
</template>
menu prop gebruiken om het menu van de zijbalk aan te passen, deze zal zich aanpassen afhankelijk van de modus die u kiest.Voorbeelden
Controle open staat
U kunt de open-status regelen met behulp van de open-prop of de v-model:open-richtlijn. Op het bureaublad regelt het de uitgebreide / samengevouwen status, op mobiel opent / sluit het het bladmenu.
<script setup lang="ts">
import type { NavigationMenuItem } from '@nuxt/ui'
const open = ref(true)
defineShortcuts({
o: () => (open.value = !open.value)
})
const items: NavigationMenuItem[] = [
{
label: 'Home',
icon: 'i-lucide-house',
active: true
},
{
label: 'Inbox',
icon: 'i-lucide-inbox',
badge: '4'
},
{
label: 'Contacts',
icon: 'i-lucide-users'
}
]
</script>
<template>
<div class="flex flex-1">
<USidebar v-model:open="open" title="Navigation" collapsible="icon">
<UNavigationMenu
:items="items"
orientation="vertical"
:ui="{ link: 'p-1.5 overflow-hidden' }"
/>
</USidebar>
<div class="flex-1 flex flex-col">
<div class="h-(--ui-header-height) shrink-0 flex items-center px-4 border-b border-default">
<UButton
icon="i-lucide-panel-left"
color="neutral"
variant="ghost"
:aria-label="open ? 'Close sidebar' : 'Open sidebar'"
@click="open = !open"
/>
</div>
<div class="flex-1 p-4">
<Placeholder class="size-full" />
</div>
</div>
</div>
</template><script setup lang="ts">
import { ref } from 'vue'
import type { NavigationMenuItem } from '@nuxt/ui'
const open = ref(true)
defineShortcuts({
o: () => (open.value = !open.value)
})
const items: NavigationMenuItem[] = [
{
label: 'Home',
icon: 'i-lucide-house',
active: true
},
{
label: 'Inbox',
icon: 'i-lucide-inbox',
badge: '4'
},
{
label: 'Contacts',
icon: 'i-lucide-users'
}
]
</script>
<template>
<div class="flex flex-1">
<USidebar v-model:open="open" title="Navigation" collapsible="icon">
<UNavigationMenu
:items="items"
orientation="vertical"
:ui="{ link: 'p-1.5 overflow-hidden' }"
/>
</USidebar>
<div class="flex-1 flex flex-col">
<div class="h-(--ui-header-height) shrink-0 flex items-center px-4 border-b border-default">
<UButton
icon="i-lucide-panel-left"
color="neutral"
variant="ghost"
:aria-label="open ? 'Close sidebar' : 'Open sidebar'"
@click="open = !open"
/>
</div>
<div class="flex-1 p-4">
<Placeholder class="size-full" />
</div>
</div>
</div>
</template>defineShortcuts, de open status van de zijbalk wijzigen door op: kbd{value="O"} te drukken.Persist open staat
Gebruik useLocalStorage van VueUse of useCookie in plaats van ref om de zijbalkstatus bij het herladen van pagina 's te behouden.
<script setup lang="ts">
import type { NavigationMenuItem } from '@nuxt/ui'
const open = useLocalStorage('sidebar-open', true)
defineShortcuts({
o: () => (open.value = !open.value)
})
const items: NavigationMenuItem[] = [
{
label: 'Home',
icon: 'i-lucide-house',
active: true
},
{
label: 'Inbox',
icon: 'i-lucide-inbox',
badge: '4'
},
{
label: 'Contacts',
icon: 'i-lucide-users'
}
]
</script>
<template>
<div class="flex flex-1">
<USidebar v-model:open="open" title="Navigation" collapsible="icon">
<UNavigationMenu
:items="items"
orientation="vertical"
:ui="{ link: 'p-1.5 overflow-hidden' }"
/>
</USidebar>
<div class="flex-1 flex flex-col">
<div class="h-(--ui-header-height) shrink-0 flex items-center px-4 border-b border-default">
<UButton
icon="i-lucide-panel-left"
color="neutral"
variant="ghost"
aria-label="Toggle sidebar"
@click="open = !open"
/>
</div>
<div class="flex-1 p-4">
<Placeholder class="size-full" />
</div>
</div>
</div>
</template><script setup lang="ts">
import { useLocalStorage } from '@vueuse/core'
import type { NavigationMenuItem } from '@nuxt/ui'
const open = useLocalStorage('sidebar-open', true)
defineShortcuts({
o: () => (open.value = !open.value)
})
const items: NavigationMenuItem[] = [
{
label: 'Home',
icon: 'i-lucide-house',
active: true
},
{
label: 'Inbox',
icon: 'i-lucide-inbox',
badge: '4'
},
{
label: 'Contacts',
icon: 'i-lucide-users'
}
]
</script>
<template>
<div class="flex flex-1">
<USidebar v-model:open="open" title="Navigation" collapsible="icon">
<UNavigationMenu
:items="items"
orientation="vertical"
:ui="{ link: 'p-1.5 overflow-hidden' }"
/>
</USidebar>
<div class="flex-1 flex flex-col">
<div class="h-(--ui-header-height) shrink-0 flex items-center px-4 border-b border-default">
<UButton
icon="i-lucide-panel-left"
color="neutral"
variant="ghost"
aria-label="Toggle sidebar"
@click="open = !open"
/>
</div>
<div class="flex-1 p-4">
<Placeholder class="size-full" />
</div>
</div>
</div>
</template>ref(true) door useLocalStorage('sidebar-open', true).Met aangepaste breedte
De breedte van de zijbalk wordt geregeld door de --sidebar-width CSS-variabele (standaard 16rem). De samengevouwen pictogrambreedte wordt geregeld door --sidebar-width-icon (standaard 4rem).
Overschrijd ze globaal in uw CSS of per instantie met het kenmerk style.
<script setup lang="ts">
import type { NavigationMenuItem } from '@nuxt/ui'
const open = ref(true)
const items: NavigationMenuItem[] = [
{
label: 'Home',
icon: 'i-lucide-house',
active: true
},
{
label: 'Inbox',
icon: 'i-lucide-inbox',
badge: '4'
},
{
label: 'Contacts',
icon: 'i-lucide-users'
}
]
</script>
<template>
<div class="flex flex-1">
<USidebar v-model:open="open" collapsible="icon" :style="{ '--sidebar-width': '20rem' }">
<UNavigationMenu
:items="items"
orientation="vertical"
:ui="{ link: 'p-1.5 overflow-hidden' }"
/>
</USidebar>
<div class="flex-1 flex flex-col">
<div class="h-(--ui-header-height) shrink-0 flex items-center px-4 border-b border-default">
<UButton
icon="i-lucide-panel-left"
color="neutral"
variant="ghost"
aria-label="Toggle sidebar"
@click="open = !open"
/>
</div>
<div class="flex-1 p-4">
<Placeholder class="size-full" />
</div>
</div>
</div>
</template><script setup lang="ts">
import { ref } from 'vue'
import type { NavigationMenuItem } from '@nuxt/ui'
const open = ref(true)
const items: NavigationMenuItem[] = [
{
label: 'Home',
icon: 'i-lucide-house',
active: true
},
{
label: 'Inbox',
icon: 'i-lucide-inbox',
badge: '4'
},
{
label: 'Contacts',
icon: 'i-lucide-users'
}
]
</script>
<template>
<div class="flex flex-1">
<USidebar v-model:open="open" collapsible="icon" :style="{ '--sidebar-width': '20rem' }">
<UNavigationMenu
:items="items"
orientation="vertical"
:ui="{ link: 'p-1.5 overflow-hidden' }"
/>
</USidebar>
<div class="flex-1 flex flex-col">
<div class="h-(--ui-header-height) shrink-0 flex items-center px-4 border-b border-default">
<UButton
icon="i-lucide-panel-left"
color="neutral"
variant="ghost"
aria-label="Toggle sidebar"
@click="open = !open"
/>
</div>
<div class="flex-1 p-4">
<Placeholder class="size-full" />
</div>
</div>
</div>
</template>Met koptekst
Om de zijbalk onder een Header te plaatsen, past u de gap en container aan met de ui prop.
<script setup lang="ts">
import type { NavigationMenuItem } from '@nuxt/ui'
const open = ref(true)
const items: NavigationMenuItem[] = [
{
label: 'Home',
icon: 'i-lucide-house',
active: true
},
{
label: 'Inbox',
icon: 'i-lucide-inbox',
badge: '4'
},
{
label: 'Contacts',
icon: 'i-lucide-users'
}
]
</script>
<template>
<div class="flex flex-col flex-1">
<UHeader toggle-side="left" :ui="{ container: 'px-4!' }">
<template #toggle>
<UButton
icon="i-lucide-panel-left"
color="neutral"
variant="ghost"
aria-label="Toggle sidebar"
@click="open = !open"
/>
</template>
</UHeader>
<div class="flex flex-1 min-h-0">
<USidebar
v-model:open="open"
collapsible="icon"
:ui="{
gap: 'h-[calc(100%-var(--ui-header-height))]',
container:
'absolute top-(--ui-header-height) bottom-0 h-[calc(100%-var(--ui-header-height))]'
}"
>
<UNavigationMenu
:items="items"
orientation="vertical"
:ui="{ link: 'p-1.5 overflow-hidden' }"
/>
</USidebar>
<div class="flex-1 p-4">
<Placeholder class="size-full" />
</div>
</div>
</div>
</template><script setup lang="ts">
import { ref } from 'vue'
import type { NavigationMenuItem } from '@nuxt/ui'
const open = ref(true)
const items: NavigationMenuItem[] = [
{
label: 'Home',
icon: 'i-lucide-house',
active: true
},
{
label: 'Inbox',
icon: 'i-lucide-inbox',
badge: '4'
},
{
label: 'Contacts',
icon: 'i-lucide-users'
}
]
</script>
<template>
<div class="flex flex-col flex-1">
<UHeader toggle-side="left" :ui="{ container: 'px-4!' }">
<template #toggle>
<UButton
icon="i-lucide-panel-left"
color="neutral"
variant="ghost"
aria-label="Toggle sidebar"
@click="open = !open"
/>
</template>
</UHeader>
<div class="flex flex-1 min-h-0">
<USidebar
v-model:open="open"
collapsible="icon"
:ui="{
gap: 'h-[calc(100%-var(--ui-header-height))]',
container:
'absolute top-(--ui-header-height) bottom-0 h-[calc(100%-var(--ui-header-height))]'
}"
>
<UNavigationMenu
:items="items"
orientation="vertical"
:ui="{ link: 'p-1.5 overflow-hidden' }"
/>
</USidebar>
<div class="flex-1 p-4">
<Placeholder class="size-full" />
</div>
</div>
</div>
</template>--ui-header-height is standaard 4rem en wordt gebruikt door de Header. Pas deze aan als uw navbar een andere hoogte gebruikt.Met AI chat
Gebruik de zijbalk aan de rechterkant met ChatMessages en ChatPrompt om een AI-chatpaneel te maken.
<script setup lang="ts">
import type { UIMessage } from 'ai'
import { isTextUIPart } from 'ai'
import { useChat } from '@ai-sdk/vue'
import { isPartStreaming } from '@nuxt/ui/utils/ai'
import { Markdown } from '@comark/vue'
import shiki from '@comark/vue/plugins/shiki'
import security from '@comark/vue/plugins/security'
const plugins = [
shiki(),
security({ blockedTags: ['script', 'style', 'iframe', 'object', 'embed', 'form'] })
]
const open = ref(true)
const input = ref('')
const initialMessages: UIMessage[] = [
{
id: '1',
role: 'user',
parts: [{ type: 'text', text: 'What is Nuxt UI?' }]
},
{
id: '2',
role: 'assistant',
parts: [
{
type: 'text',
text: 'Nuxt UI is a Vue component library built on Reka UI, Tailwind CSS, and Tailwind Variants. It provides 125+ accessible components for building modern web apps.'
}
]
}
]
const { messages, status, error, sendMessage, regenerate, stop } = useChat({
messages: initialMessages
})
function onSubmit() {
if (!input.value.trim()) return
sendMessage({ text: input.value })
input.value = ''
}
const ui = {
prose: {
p: { base: 'my-2 text-sm/6' },
li: { base: 'my-0.5 text-sm/6' },
ul: { base: 'my-2' },
ol: { base: 'my-2' },
h1: { base: 'text-xl mb-4' },
h2: { base: 'text-lg mt-6 mb-3' },
h3: { base: 'text-base mt-4 mb-2' },
h4: { base: 'text-sm mt-3 mb-1.5' },
code: { base: 'text-xs' },
pre: { root: 'my-2', base: 'text-xs/5' },
table: { root: 'my-2' },
hr: { base: 'my-4' }
}
}
</script>
<template>
<div class="flex flex-1">
<div class="flex-1 flex flex-col">
<div
class="h-(--ui-header-height) shrink-0 flex items-center justify-end px-4 border-b border-default"
>
<UButton
icon="i-lucide-panel-right"
color="neutral"
variant="ghost"
aria-label="Toggle sidebar"
@click="open = !open"
/>
</div>
<div class="flex-1 p-4">
<Placeholder class="size-full" />
</div>
</div>
<USidebar
v-model:open="open"
side="right"
title="AI Chat"
close
:style="{ '--sidebar-width': '20rem' }"
:ui="{ container: 'h-full' }"
>
<UTheme :ui="ui">
<UChatMessages :messages="messages" :status="status" compact class="px-0">
<template #content="{ message }">
<template
v-for="(part, index) in message.parts"
:key="`${message.id}-${part.type}-${index}`"
>
<template v-if="isTextUIPart(part)">
<Markdown
v-if="message.role === 'assistant'"
:value="part.text"
:streaming="isPartStreaming(part)"
:plugins="plugins"
class="*:first:mt-0 *:last:mb-0"
/>
<p v-else-if="message.role === 'user'" class="whitespace-pre-wrap text-sm/6">
{{ part.text }}
</p>
</template>
</template>
</template>
</UChatMessages>
</UTheme>
<template #footer>
<UChatPrompt
v-model="input"
:error="error"
:autofocus="false"
variant="subtle"
size="sm"
@submit="onSubmit"
>
<UChatPromptSubmit size="sm" :status="status" @stop="stop()" @reload="regenerate()" />
</UChatPrompt>
</template>
</USidebar>
</div>
</template><script setup lang="ts">
import { ref } from 'vue'
import type { UIMessage } from 'ai'
import { isTextUIPart } from 'ai'
import { useChat } from '@ai-sdk/vue'
import { isPartStreaming } from '@nuxt/ui/utils/ai'
import { Markdown } from '@comark/vue'
import shiki from '@comark/vue/plugins/shiki'
import security from '@comark/vue/plugins/security'
const plugins = [
shiki(),
security({ blockedTags: ['script', 'style', 'iframe', 'object', 'embed', 'form'] })
]
const open = ref(true)
const input = ref('')
const initialMessages: UIMessage[] = [
{
id: '1',
role: 'user',
parts: [{ type: 'text', text: 'What is Nuxt UI?' }]
},
{
id: '2',
role: 'assistant',
parts: [
{
type: 'text',
text: 'Nuxt UI is a Vue component library built on Reka UI, Tailwind CSS, and Tailwind Variants. It provides 125+ accessible components for building modern web apps.'
}
]
}
]
const { messages, status, error, sendMessage, regenerate, stop } = useChat({
messages: initialMessages
})
function onSubmit() {
if (!input.value.trim()) return
sendMessage({ text: input.value })
input.value = ''
}
const ui = {
prose: {
p: { base: 'my-2 text-sm/6' },
li: { base: 'my-0.5 text-sm/6' },
ul: { base: 'my-2' },
ol: { base: 'my-2' },
h1: { base: 'text-xl mb-4' },
h2: { base: 'text-lg mt-6 mb-3' },
h3: { base: 'text-base mt-4 mb-2' },
h4: { base: 'text-sm mt-3 mb-1.5' },
code: { base: 'text-xs' },
pre: { root: 'my-2', base: 'text-xs/5' },
table: { root: 'my-2' },
hr: { base: 'my-4' }
}
}
</script>
<template>
<div class="flex flex-1">
<div class="flex-1 flex flex-col">
<div
class="h-(--ui-header-height) shrink-0 flex items-center justify-end px-4 border-b border-default"
>
<UButton
icon="i-lucide-panel-right"
color="neutral"
variant="ghost"
aria-label="Toggle sidebar"
@click="open = !open"
/>
</div>
<div class="flex-1 p-4">
<Placeholder class="size-full" />
</div>
</div>
<USidebar
v-model:open="open"
side="right"
title="AI Chat"
close
:style="{ '--sidebar-width': '20rem' }"
:ui="{ container: 'h-full' }"
>
<UTheme :ui="ui">
<UChatMessages :messages="messages" :status="status" compact class="px-0">
<template #content="{ message }">
<template
v-for="(part, index) in message.parts"
:key="`${message.id}-${part.type}-${index}`"
>
<template v-if="isTextUIPart(part)">
<Markdown
v-if="message.role === 'assistant'"
:value="part.text"
:streaming="isPartStreaming(part)"
:plugins="plugins"
class="*:first:mt-0 *:last:mb-0"
/>
<p v-else-if="message.role === 'user'" class="whitespace-pre-wrap text-sm/6">
{{ part.text }}
</p>
</template>
</template>
</template>
</UChatMessages>
</UTheme>
<template #footer>
<UChatPrompt
v-model="input"
:error="error"
:autofocus="false"
variant="subtle"
size="sm"
@submit="onSubmit"
>
<UChatPromptSubmit size="sm" :status="status" @stop="stop()" @reload="regenerate()" />
</UChatPrompt>
</template>
</USidebar>
</div>
</template>API
Props
| Prop | Default | Type |
|---|---|---|
as | 'aside' | anyThe element or component this component should render as. |
variant | 'sidebar' | "floating" | "sidebar" | "inset"The visual variant of the sidebar. |
collapsible | 'offcanvas' | "offcanvas" | "icon" | "none"The collapse behavior of the sidebar.
|
side | 'left' | "left" | "right"The side to render the sidebar on. |
title | stringThe title displayed in the sidebar header. | |
description | stringThe description displayed in the sidebar header. | |
close | false | boolean | Omit<ButtonProps, LinkPropsKeys> Display a close button to collapse the sidebar.
Only renders when |
closeIcon | appConfig.ui.icons.close | anyThe icon displayed in the close button. |
rail | false | boolean Display a rail on the sidebar edge to toggle collapse.
Only renders when |
transition | true | boolean Animate the sidebar when collapsing or expanding. |
mode | 'slideover' | TThe mode of the sidebar menu on mobile. |
menu | SidebarMenu<T>The props for the sidebar menu component on mobile. | |
open | true | boolean |
ui | { root?: SlotClass; gap?: SlotClass; container?: SlotClass; inner?: SlotClass; header?: SlotClass; wrapper?: SlotClass; title?: SlotClass; description?: SlotClass; actions?: SlotClass; close?: SlotClass; body?: SlotClass; footer?: SlotClass; rail?: SlotClass; } |
Slots
| Slot | Type |
|---|---|
header | { state: SidebarState; open: boolean; close: () => void; } |
title | { state: SidebarState; } |
description | { state: SidebarState; } |
actions | { state: SidebarState; } |
close | { ui: object; } |
default | { state: SidebarState; open: boolean; close: () => void; } |
footer | { state: SidebarState; open: boolean; close: () => void; } |
rail | { ui: object; } |
content | { close: () => void; } |
Thema
export default defineAppConfig({
ui: {
sidebar: {
slots: {
root: 'peer [--sidebar-width:16rem] [--sidebar-width-icon:4rem]',
gap: 'relative w-(--sidebar-width) bg-transparent',
container: 'fixed inset-y-0 z-10 hidden h-svh w-(--sidebar-width) lg:flex',
inner: 'flex size-full flex-col overflow-hidden divide-y divide-default',
header: 'flex items-center gap-1.5 overflow-hidden px-4 min-h-(--ui-header-height)',
wrapper: 'min-w-0 flex-1',
title: 'text-highlighted font-semibold truncate',
description: 'text-muted text-sm truncate',
actions: 'flex items-center gap-1.5 shrink-0',
close: '',
body: 'flex min-h-0 flex-1 flex-col gap-4 overflow-y-auto p-4',
footer: 'flex items-center gap-1.5 overflow-hidden p-4',
rail: [
'absolute inset-y-0 z-20 hidden w-4 after:absolute after:inset-y-0 after:left-1/2 after:w-px lg:flex hover:after:bg-(--ui-border-accented)',
'after:transition-colors'
]
},
variants: {
transition: {
true: {
gap: 'transition-[width] duration-200 ease-out motion-reduce:transition-none',
container: 'transition-[inset-inline-start,inset-inline-end,width] duration-200 ease-out motion-reduce:transition-none'
}
},
side: {
left: {
container: 'start-0 border-e border-default',
rail: 'end-0 translate-x-1/2 rtl:-translate-x-1/2'
},
right: {
container: 'end-0 border-s border-default',
rail: '-start-px -translate-x-1/2 rtl:translate-x-1/2'
}
},
collapsible: {
offcanvas: {
root: 'group/sidebar hidden lg:block',
gap: 'data-[state=collapsed]:w-0'
},
icon: {
root: 'group/sidebar hidden lg:block',
gap: 'data-[state=collapsed]:w-(--sidebar-width-icon)',
container: 'data-[state=collapsed]:w-(--sidebar-width-icon)',
actions: 'group-data-[state=collapsed]/sidebar:hidden',
body: 'group-data-[state=collapsed]/sidebar:overflow-hidden'
},
none: {
root: 'h-full w-(--sidebar-width)'
}
},
variant: {
sidebar: {},
floating: {
container: 'p-4 border-transparent',
inner: 'rounded-lg ring ring-default shadow-lg',
rail: 'inset-y-4'
},
inset: {
container: 'py-4 border-transparent',
inner: 'divide-transparent',
rail: 'inset-y-4'
}
}
},
compoundVariants: [
{
side: 'left',
collapsible: [
'offcanvas',
'icon'
],
class: {
rail: 'cursor-w-resize rtl:cursor-e-resize data-[state=collapsed]:cursor-e-resize data-[state=collapsed]:rtl:cursor-w-resize'
}
},
{
side: 'right',
collapsible: [
'offcanvas',
'icon'
],
class: {
rail: 'cursor-e-resize rtl:cursor-w-resize data-[state=collapsed]:cursor-w-resize data-[state=collapsed]:rtl:cursor-e-resize'
}
},
{
side: 'left',
collapsible: 'none',
class: {
root: 'border-e border-default'
}
},
{
side: 'right',
collapsible: 'none',
class: {
root: 'border-s border-default'
}
},
{
side: 'left',
collapsible: 'offcanvas',
class: {
container: 'data-[state=collapsed]:-start-(--sidebar-width)'
}
},
{
side: 'right',
collapsible: 'offcanvas',
class: {
container: 'data-[state=collapsed]:-end-(--sidebar-width)'
}
},
{
variant: 'floating',
collapsible: 'icon',
class: {
gap: 'data-[state=collapsed]:w-[calc(var(--sidebar-width-icon)+--spacing(8))]',
container: 'data-[state=collapsed]:w-[calc(var(--sidebar-width-icon)+--spacing(8)+2px)]'
}
},
{
variant: 'floating',
collapsible: 'none',
class: {
root: 'p-4 border-0'
}
},
{
variant: 'inset',
collapsible: 'none',
class: {
root: 'py-4 border-0'
}
},
{
variant: 'floating',
side: 'left',
class: {
rail: 'end-4'
}
},
{
variant: 'floating',
side: 'right',
class: {
rail: 'start-[calc(--spacing(4)-1px)]'
}
}
]
}
}
})import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import ui from '@nuxt/ui/vite'
export default defineConfig({
plugins: [
vue(),
ui({
ui: {
sidebar: {
slots: {
root: 'peer [--sidebar-width:16rem] [--sidebar-width-icon:4rem]',
gap: 'relative w-(--sidebar-width) bg-transparent',
container: 'fixed inset-y-0 z-10 hidden h-svh w-(--sidebar-width) lg:flex',
inner: 'flex size-full flex-col overflow-hidden divide-y divide-default',
header: 'flex items-center gap-1.5 overflow-hidden px-4 min-h-(--ui-header-height)',
wrapper: 'min-w-0 flex-1',
title: 'text-highlighted font-semibold truncate',
description: 'text-muted text-sm truncate',
actions: 'flex items-center gap-1.5 shrink-0',
close: '',
body: 'flex min-h-0 flex-1 flex-col gap-4 overflow-y-auto p-4',
footer: 'flex items-center gap-1.5 overflow-hidden p-4',
rail: [
'absolute inset-y-0 z-20 hidden w-4 after:absolute after:inset-y-0 after:left-1/2 after:w-px lg:flex hover:after:bg-(--ui-border-accented)',
'after:transition-colors'
]
},
variants: {
transition: {
true: {
gap: 'transition-[width] duration-200 ease-out motion-reduce:transition-none',
container: 'transition-[inset-inline-start,inset-inline-end,width] duration-200 ease-out motion-reduce:transition-none'
}
},
side: {
left: {
container: 'start-0 border-e border-default',
rail: 'end-0 translate-x-1/2 rtl:-translate-x-1/2'
},
right: {
container: 'end-0 border-s border-default',
rail: '-start-px -translate-x-1/2 rtl:translate-x-1/2'
}
},
collapsible: {
offcanvas: {
root: 'group/sidebar hidden lg:block',
gap: 'data-[state=collapsed]:w-0'
},
icon: {
root: 'group/sidebar hidden lg:block',
gap: 'data-[state=collapsed]:w-(--sidebar-width-icon)',
container: 'data-[state=collapsed]:w-(--sidebar-width-icon)',
actions: 'group-data-[state=collapsed]/sidebar:hidden',
body: 'group-data-[state=collapsed]/sidebar:overflow-hidden'
},
none: {
root: 'h-full w-(--sidebar-width)'
}
},
variant: {
sidebar: {},
floating: {
container: 'p-4 border-transparent',
inner: 'rounded-lg ring ring-default shadow-lg',
rail: 'inset-y-4'
},
inset: {
container: 'py-4 border-transparent',
inner: 'divide-transparent',
rail: 'inset-y-4'
}
}
},
compoundVariants: [
{
side: 'left',
collapsible: [
'offcanvas',
'icon'
],
class: {
rail: 'cursor-w-resize rtl:cursor-e-resize data-[state=collapsed]:cursor-e-resize data-[state=collapsed]:rtl:cursor-w-resize'
}
},
{
side: 'right',
collapsible: [
'offcanvas',
'icon'
],
class: {
rail: 'cursor-e-resize rtl:cursor-w-resize data-[state=collapsed]:cursor-w-resize data-[state=collapsed]:rtl:cursor-e-resize'
}
},
{
side: 'left',
collapsible: 'none',
class: {
root: 'border-e border-default'
}
},
{
side: 'right',
collapsible: 'none',
class: {
root: 'border-s border-default'
}
},
{
side: 'left',
collapsible: 'offcanvas',
class: {
container: 'data-[state=collapsed]:-start-(--sidebar-width)'
}
},
{
side: 'right',
collapsible: 'offcanvas',
class: {
container: 'data-[state=collapsed]:-end-(--sidebar-width)'
}
},
{
variant: 'floating',
collapsible: 'icon',
class: {
gap: 'data-[state=collapsed]:w-[calc(var(--sidebar-width-icon)+--spacing(8))]',
container: 'data-[state=collapsed]:w-[calc(var(--sidebar-width-icon)+--spacing(8)+2px)]'
}
},
{
variant: 'floating',
collapsible: 'none',
class: {
root: 'p-4 border-0'
}
},
{
variant: 'inset',
collapsible: 'none',
class: {
root: 'py-4 border-0'
}
},
{
variant: 'floating',
side: 'left',
class: {
rail: 'end-4'
}
},
{
variant: 'floating',
side: 'right',
class: {
rail: 'start-[calc(--spacing(4)-1px)]'
}
}
]
}
}
})
]
})