Sidebar Soon
Usage
The Sidebar component is a standalone, fixed sidebar that pushes the page content. On desktop, it renders inline and can be collapsed; on mobile, it opens as a sheet (Modal, Slideover or Drawer).
Use the title, description and close props to customize the sidebar header just like the Modal, Slideover and Drawer components.
Use the body, default and footer slots to customize the sidebar content. The v-model:open directive is viewport-aware: on desktop it controls the expanded/collapsed state, on mobile it controls the sheet menu.
<script setup lang="ts">
import type { DropdownMenuItem, NavigationMenuItem } from '@nuxt/ui'
const open = ref(true)
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'
}]]
})
const items: NavigationMenuItem[] = [{
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: [{
label: 'General',
icon: 'i-lucide-house'
}, {
label: 'Team',
icon: 'i-lucide-users'
}, {
label: 'Billing',
icon: 'i-lucide-credit-card'
}]
}]
defineShortcuts(extractShortcuts(teamsItems.value))
</script>
<template>
<div class="flex h-full w-full contain-[paint]">
<USidebar
v-model:open="open"
collapsible="icon"
:ui="{ inner: 'divide-y-0 bg-muted' }"
>
<template #header>
<UDropdownMenu
:items="teamsItems"
:content="{ align: 'start', collisionPadding: 12 }"
:ui="{ content: 'w-(--reka-dropdown-menu-trigger-width) min-w-56' }"
>
<UButton
v-bind="selectedTeam"
trailing-icon="i-lucide-chevrons-up-down"
color="neutral"
variant="ghost"
block
class="overflow-hidden data-[state=open]:bg-elevated transition-[width,height,padding] duration-200 ease-linear group-data-[state=collapsed]/sidebar:size-8! group-data-[state=collapsed]/sidebar:p-0!"
:ui="{ trailingIcon: 'text-dimmed' }"
/>
</UDropdownMenu>
</template>
<template #default="{ state }">
<UNavigationMenu
:collapsed="state === 'collapsed'"
:items="items"
orientation="vertical"
/>
</template>
<template #footer>
<UButton
:avatar="{ src: 'https://github.com/benjamincanac.png' }"
label="Benjamin"
color="neutral"
variant="ghost"
class="w-full overflow-hidden transition-[width,height,padding] duration-200 ease-linear group-data-[state=collapsed]/sidebar:size-8! group-data-[state=collapsed]/sidebar:p-0!"
/>
</template>
</USidebar>
<div class="flex-1 flex flex-col">
<div class="h-(--ui-header-height) shrink-0 flex items-center gap-2 px-4 border-b border-default">
<UButton
icon="i-lucide-panel-left"
color="neutral"
variant="ghost"
@click="open = !open"
/>
<span class="font-semibold text-sm">Page Title</span>
</div>
<div class="flex-1" />
</div>
</div>
</template>
Variant
Use the variant prop to change the visual style of the sidebar. Defaults to sidebar.
<template>
<USidebar variant="sidebar">
<Placeholder class="h-full" />
</USidebar>
</template>
Collapsible
Use the collapsible prop to change the collapse behavior of the sidebar. Defaults to none.
offcanvas: The sidebar slides out of view completely.icon: The sidebar shrinks to icon-only width.none: The sidebar is not collapsible.
<template>
<USidebar collapsible="icon">
<Placeholder class="h-full" />
</USidebar>
</template>
state in the slot props to customize the content of the sidebar when it is collapsed.Side
Use the side prop to change the side of the sidebar. Defaults to left.
<template>
<USidebar side="left">
<Placeholder class="h-full" />
</USidebar>
</template>
Title
Use the title prop to set the title of the sidebar header.
<template>
<USidebar title="Navigation">
<Placeholder class="h-full" />
</USidebar>
</template>
Description
Use the description prop to set the description of the sidebar header.
<template>
<USidebar title="Navigation" description="Browse your workspace">
<Placeholder class="h-full" />
</USidebar>
</template>
Close
Use the close prop to display a close button in the sidebar header. The close button is only rendered when collapsible is not none.
You can pass any property from the Button component to customize it.
<template>
<USidebar close title="Navigation" collapsible="offcanvas">
<Placeholder class="h-full" />
</USidebar>
</template>
Close Icon
Use the close-icon prop to customize the close button Icon. Defaults to i-lucide-x.
<template>
<USidebar close close-icon="i-lucide-arrow-left" title="Navigation" collapsible="offcanvas">
<Placeholder class="h-full" />
</USidebar>
</template>
#title, #description and #close slots to customize them.Mode
Use the mode prop to change the mode of the sidebar menu on mobile. Defaults to slideover.
<template>
<USidebar mode="slideover">
<Placeholder class="h-full" />
</USidebar>
</template>
menu prop to customize the menu of the sidebar, it will adapt depending on the mode you choose.Examples
With inset variant
Use the inset variant with peer data-attribute classes on the content panel to create a visually inset layout where the content area adapts to the sidebar variant.
<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'
}, {
label: 'Settings',
icon: 'i-lucide-settings'
}]
</script>
<template>
<div class="flex h-full w-full [contain:paint]">
<USidebar
v-model:open="open"
variant="inset"
collapsible="icon"
:ui="{ container: 'absolute' }"
>
<template #header>
<Logo class="h-5 shrink-0" />
</template>
<template #default="{ state }">
<UNavigationMenu
:collapsed="state === 'collapsed'"
:items="items"
orientation="vertical"
/>
</template>
<template #footer>
<UButton
:avatar="{ src: 'https://github.com/benjamincanac.png' }"
label="Benjamin"
color="neutral"
variant="ghost"
class="w-full overflow-hidden transition-[width,height,padding] duration-200 ease-linear group-data-[state=collapsed]/sidebar:size-8! group-data-[state=collapsed]/sidebar:p-0!"
/>
</template>
</USidebar>
<div class="flex-1 flex flex-col overflow-hidden lg:peer-data-[variant=inset]:my-2 lg:peer-data-[variant=inset]:ms-0 lg:peer-data-[variant=inset]:rounded-xl lg:peer-data-[variant=inset]:shadow-sm lg:peer-data-[variant=inset]:ring lg:peer-data-[variant=inset]:ring-default">
<div class="h-(--ui-header-height) shrink-0 flex items-center gap-2 px-4 border-b border-default">
<UButton
icon="i-lucide-panel-left"
color="neutral"
variant="ghost"
@click="open = !open"
/>
<span class="font-semibold text-sm">Page Title</span>
</div>
<div class="flex-1" />
</div>
</div>
</template>
With chat
Use the sidebar on the right side with ChatMessages and ChatPrompt to create an AI chat panel.
<script setup lang="ts">
import type { UIMessage } from 'ai'
import { Chat } from '@ai-sdk/vue'
const open = ref(true)
const input = ref('')
const messages: 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 chat = new Chat({
messages,
onError() {}
})
function onSubmit() {
chat.sendMessage({ text: input.value })
input.value = ''
}
</script>
<template>
<div class="flex h-full w-full [contain:paint]">
<div class="flex-1 flex flex-col">
<div class="h-(--ui-header-height) shrink-0 flex items-center justify-end gap-2 px-4 border-b border-default">
<span class="font-semibold text-sm me-auto">Page Title</span>
<UButton
icon="i-lucide-panel-right"
color="neutral"
variant="ghost"
@click="open = !open"
/>
</div>
<div class="flex-1" />
</div>
<USidebar
v-model:open="open"
side="right"
collapsible="offcanvas"
title="AI Chat"
close
:style="{ '--sidebar-width': '20rem' }"
:ui="{ container: 'absolute', footer: 'p-0' }"
>
<template #body>
<UChatMessages
:messages="chat.messages"
:status="chat.status"
compact
class="px-0"
/>
</template>
<template #footer>
<UChatPrompt
v-model="input"
:error="chat.error"
variant="naked"
:ui="{ base: 'px-0' }"
class="px-4"
@submit="onSubmit"
>
<UChatPromptSubmit :status="chat.status" @stop="chat.stop()" @reload="chat.regenerate()" />
</UChatPrompt>
</template>
</USidebar>
</div>
</template>
With custom width
The sidebar width is controlled by the --sidebar-width CSS variable (defaults to 16rem). The collapsed icon width is controlled by --sidebar-width-icon (defaults to 4rem).
Override them globally in your CSS or per-instance with the style attribute.
<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 h-full w-full contain-[paint]">
<USidebar
v-model:open="open"
collapsible="icon"
:style="{ '--sidebar-width': '20rem' }"
:ui="{ container: 'absolute' }"
>
<template #default="{ state }">
<UNavigationMenu
:collapsed="state === 'collapsed'"
:items="items"
orientation="vertical"
/>
</template>
</USidebar>
<div class="flex-1 flex flex-col">
<div class="h-(--ui-header-height) shrink-0 flex items-center gap-2 px-4 border-b border-default">
<UButton
icon="i-lucide-panel-left"
color="neutral"
variant="ghost"
@click="open = !open"
/>
<span class="font-semibold text-sm">Page Title</span>
</div>
<div class="flex-1" />
</div>
</div>
</template>
With Navbar
To position the sidebar below a fixed navbar, customize the gap and container using the 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 h-full w-full contain-[paint]">
<div class="h-(--ui-header-height) shrink-0 flex items-center gap-2 px-4 border-b border-default">
<UButton
icon="i-lucide-panel-left"
color="neutral"
variant="ghost"
@click="open = !open"
/>
<span class="font-semibold text-sm">Page Title</span>
</div>
<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))]'
}"
>
<template #default="{ state }">
<UNavigationMenu
:collapsed="state === 'collapsed'"
:items="items"
orientation="vertical"
/>
</template>
</USidebar>
<div class="flex-1" />
</div>
</div>
</template>
--ui-header-height variable defaults to 4rem and is used by the Header and DashboardNavbar components. Adjust it if your navbar uses a different height.Control open state
You can control the open state by using the open prop or the v-model:open directive. On desktop it controls the expanded/collapsed state, on mobile it opens/closes the sheet menu.
<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'
}, {
label: 'Contacts',
icon: 'i-lucide-users'
}]
</script>
<template>
<div class="flex h-full w-full contain-[paint]">
<USidebar v-model:open="open" collapsible="icon" :ui="{ container: 'absolute' }">
<template #header>
<span class="font-semibold text-sm">Navigation</span>
</template>
<UNavigationMenu
:collapsed="!open"
:items="items"
orientation="vertical"
/>
</USidebar>
<div class="flex-1 flex flex-col">
<div class="h-(--ui-header-height) shrink-0 flex items-center gap-2 px-4 border-b border-default">
<UButton
icon="i-lucide-panel-left"
color="neutral"
variant="ghost"
@click="open = !open"
/>
<span class="font-semibold text-sm">Page Title</span>
</div>
<div class="flex-1" />
</div>
</div>
</template>
defineShortcuts, you can toggle the open state of the Sidebar by pressing O.Persist open state
Use useLocalStorage from VueUse or useCookie instead of ref to persist the sidebar state across page reloads.
<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'
}, {
label: 'Contacts',
icon: 'i-lucide-users'
}]
</script>
<template>
<div class="flex h-full w-full contain-[paint]">
<USidebar v-model:open="open" collapsible="icon" :ui="{ container: 'absolute' }">
<template #header>
<span class="font-semibold text-sm">Navigation</span>
</template>
<UNavigationMenu
:collapsed="!open"
:items="items"
orientation="vertical"
/>
</USidebar>
<div class="flex-1 flex flex-col">
<div class="h-(--ui-header-height) shrink-0 flex items-center gap-2 px-4 border-b border-default">
<UButton
icon="i-lucide-panel-left"
color="neutral"
variant="ghost"
@click="open = !open"
/>
<span class="font-semibold text-sm">Page Title</span>
</div>
<div class="flex-1" />
</div>
</div>
</template>
ref(true) with useLocalStorage('sidebar-open', true).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 | 'none' | "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 |
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?: ClassNameValue; gap?: ClassNameValue; container?: ClassNameValue; inner?: ClassNameValue; header?: ClassNameValue; wrapper?: ClassNameValue; title?: ClassNameValue; description?: ClassNameValue; actions?: ClassNameValue; close?: ClassNameValue; body?: ClassNameValue; footer?: ClassNameValue; rail?: ClassNameValue; overlay?: ClassNameValue; } |
Slots
| Slot | Type |
|---|---|
header | { state: SidebarState; open: boolean; close: () => void; } |
title | {} |
description | {} |
actions | {} |
close | { ui: object; } |
body | { state: SidebarState; open: boolean; close: () => void; } |
default | { state: SidebarState; open: boolean; close: () => void; } |
footer | { state: SidebarState; open: boolean; close: () => void; } |
rail | { ui: object; } |
content | { close: () => void; } |
Theme
export default defineAppConfig({
ui: {
sidebar: {
slots: {
root: 'peer [--sidebar-width:16rem] [--sidebar-width-icon:4rem]',
gap: 'relative w-(--sidebar-width) bg-transparent transition-[width] duration-200 ease-linear',
container: 'fixed inset-y-0 z-10 hidden h-svh w-(--sidebar-width) transition-[left,right,width] duration-200 ease-linear lg:flex',
inner: 'flex size-full flex-col overflow-hidden bg-default divide-y divide-default',
header: 'flex items-center gap-1.5 p-4 min-h-16',
wrapper: '',
title: 'text-highlighted font-semibold',
description: 'text-muted text-sm',
actions: 'flex items-center gap-1.5 ms-auto absolute top-4 end-4',
close: '',
body: 'flex min-h-0 flex-1 flex-col gap-4 overflow-y-auto p-4',
footer: 'flex items-center gap-1.5 p-4',
rail: [
'absolute inset-y-0 z-20 hidden w-4 transition-all ease-linear after:absolute after:inset-y-0 after:left-1/2 after:w-px lg:flex hover:after:bg-(--ui-border-accented)',
'after:transition-colors'
],
overlay: 'lg:hidden'
},
variants: {
side: {
left: {
container: 'left-0 border-e border-default'
},
right: {
container: 'right-0 border-s border-default'
}
},
collapsible: {
offcanvas: {
root: 'group/sidebar hidden lg:block',
gap: 'data-[state=collapsed]:w-0',
container: '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)',
header: 'group-data-[state=collapsed]/sidebar:overflow-hidden group-data-[state=collapsed]/sidebar:p-2',
body: 'group-data-[state=collapsed]/sidebar:overflow-hidden',
footer: 'group-data-[state=collapsed]/sidebar:overflow-hidden group-data-[state=collapsed]/sidebar:p-2'
},
none: {
root: 'h-full w-(--sidebar-width)'
}
},
variant: {
sidebar: {},
floating: {
container: 'p-2 border-0',
inner: 'rounded-lg border border-default shadow-sm'
},
inset: {
container: 'p-2 border-0',
inner: 'rounded-lg'
}
}
},
compoundVariants: [
{
side: 'left',
collapsible: 'offcanvas',
class: {
rail: 'end-0 translate-x-1/2 data-[state=collapsed]:cursor-e-resize cursor-w-resize'
}
},
{
side: 'right',
collapsible: 'offcanvas',
class: {
rail: '-start-px -translate-x-1/2 data-[state=collapsed]:cursor-w-resize cursor-e-resize'
}
},
{
side: 'left',
collapsible: 'icon',
class: {
rail: 'end-0 translate-x-1/2 data-[state=collapsed]:cursor-e-resize cursor-w-resize'
}
},
{
side: 'right',
collapsible: 'icon',
class: {
rail: '-start-px -translate-x-1/2 data-[state=collapsed]:cursor-w-resize 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]:-left-(--sidebar-width)'
}
},
{
side: 'right',
collapsible: 'offcanvas',
class: {
container: 'data-[state=collapsed]:-right-(--sidebar-width)'
}
},
{
variant: 'floating',
collapsible: 'icon',
class: {
gap: 'data-[state=collapsed]:w-[calc(var(--sidebar-width-icon)+theme(spacing.4))]',
container: 'data-[state=collapsed]:w-[calc(var(--sidebar-width-icon)+theme(spacing.4)+2px)]'
}
},
{
variant: 'inset',
collapsible: 'icon',
class: {
gap: 'data-[state=collapsed]:w-[calc(var(--sidebar-width-icon)+theme(spacing.4))]',
container: 'data-[state=collapsed]:w-[calc(var(--sidebar-width-icon)+theme(spacing.4)+2px)]'
}
}
]
}
}
})
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 transition-[width] duration-200 ease-linear',
container: 'fixed inset-y-0 z-10 hidden h-svh w-(--sidebar-width) transition-[left,right,width] duration-200 ease-linear lg:flex',
inner: 'flex size-full flex-col overflow-hidden bg-default divide-y divide-default',
header: 'flex items-center gap-1.5 p-4 min-h-16',
wrapper: '',
title: 'text-highlighted font-semibold',
description: 'text-muted text-sm',
actions: 'flex items-center gap-1.5 ms-auto absolute top-4 end-4',
close: '',
body: 'flex min-h-0 flex-1 flex-col gap-4 overflow-y-auto p-4',
footer: 'flex items-center gap-1.5 p-4',
rail: [
'absolute inset-y-0 z-20 hidden w-4 transition-all ease-linear after:absolute after:inset-y-0 after:left-1/2 after:w-px lg:flex hover:after:bg-(--ui-border-accented)',
'after:transition-colors'
],
overlay: 'lg:hidden'
},
variants: {
side: {
left: {
container: 'left-0 border-e border-default'
},
right: {
container: 'right-0 border-s border-default'
}
},
collapsible: {
offcanvas: {
root: 'group/sidebar hidden lg:block',
gap: 'data-[state=collapsed]:w-0',
container: '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)',
header: 'group-data-[state=collapsed]/sidebar:overflow-hidden group-data-[state=collapsed]/sidebar:p-2',
body: 'group-data-[state=collapsed]/sidebar:overflow-hidden',
footer: 'group-data-[state=collapsed]/sidebar:overflow-hidden group-data-[state=collapsed]/sidebar:p-2'
},
none: {
root: 'h-full w-(--sidebar-width)'
}
},
variant: {
sidebar: {},
floating: {
container: 'p-2 border-0',
inner: 'rounded-lg border border-default shadow-sm'
},
inset: {
container: 'p-2 border-0',
inner: 'rounded-lg'
}
}
},
compoundVariants: [
{
side: 'left',
collapsible: 'offcanvas',
class: {
rail: 'end-0 translate-x-1/2 data-[state=collapsed]:cursor-e-resize cursor-w-resize'
}
},
{
side: 'right',
collapsible: 'offcanvas',
class: {
rail: '-start-px -translate-x-1/2 data-[state=collapsed]:cursor-w-resize cursor-e-resize'
}
},
{
side: 'left',
collapsible: 'icon',
class: {
rail: 'end-0 translate-x-1/2 data-[state=collapsed]:cursor-e-resize cursor-w-resize'
}
},
{
side: 'right',
collapsible: 'icon',
class: {
rail: '-start-px -translate-x-1/2 data-[state=collapsed]:cursor-w-resize 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]:-left-(--sidebar-width)'
}
},
{
side: 'right',
collapsible: 'offcanvas',
class: {
container: 'data-[state=collapsed]:-right-(--sidebar-width)'
}
},
{
variant: 'floating',
collapsible: 'icon',
class: {
gap: 'data-[state=collapsed]:w-[calc(var(--sidebar-width-icon)+theme(spacing.4))]',
container: 'data-[state=collapsed]:w-[calc(var(--sidebar-width-icon)+theme(spacing.4)+2px)]'
}
},
{
variant: 'inset',
collapsible: 'icon',
class: {
gap: 'data-[state=collapsed]:w-[calc(var(--sidebar-width-icon)+theme(spacing.4))]',
container: 'data-[state=collapsed]:w-[calc(var(--sidebar-width-icon)+theme(spacing.4)+2px)]'
}
}
]
}
}
})
]
})