Sidebar
UiSidebar is a navigation drawer composed from region sub-components. It tracks two independent states:
rail— compact, icons-only mode (a desktop concern). One-way; the parent controls it.open— the responsive mobile slide-in. Supportsv-model:open; on desktop the drawer is always visible.
Build a sidebar
<script setup lang="ts">
import { NuxtLink } from '#components'
import { ref } from 'vue'
const rail = ref(false) // compact (desktop)
const open = ref(false) // mobile drawer
</script>
<template>
<UiSidebar bordered :rail="rail" v-model:open="open" aria-label="Main navigation">
<template #header>
<UiSidebarDropdown title="Acme" subtitle="Workspace" />
</template>
<template #body>
<UiSidebarText text="Platform" />
<UiSidebarLink :as="NuxtLink" to="/" text="Home" icon="" tooltip-text="Home" />
<UiSidebarLink :as="NuxtLink" to="/projects" text="Projects" icon="" />
<UiSidebarGroup text="Account" collapsible :default-open="true" icon="">
<UiSidebarLink :as="NuxtLink" to="/account" text="Profile" icon="" child />
<UiSidebarLink :as="NuxtLink" to="/notifications" text="Notifications" icon="" child />
</UiSidebarGroup>
</template>
<template #footer>
<UiBadge text="v1.0.0" variant="outline" size="sm" />
</template>
</UiSidebar>
</template>
Toggle the mobile drawer
Wire the sidebar to a UiNavbarToggle in your navbar. The toggle is hidden below 1024px unless show-toggle-button is set. Its collapsed prop follows the rail, and rail only applies from 1024px while open only applies below it, so one boolean can drive both:
<template>
<UiSidebar :rail="collapsed" v-model:open="collapsed">…</UiSidebar>
<UiNavbarToggle show-toggle-button :collapsed="collapsed" @toggle="collapsed = !collapsed" />
</template>
UiSidebar props
| Prop | Type | Default | Description |
|---|---|---|---|
bordered | boolean | false | Right border instead of shadow. |
rail | boolean | false | Compact icons-only mode (one-way). |
open | boolean | false | Mobile open state (v-model:open). |
width | string | null | null | Sets --cffy-sidebar-width. |
ariaLabel | string | 'Main navigation' | Landmark name. |
customClass | ClassValue | null | '' | Extra classes for the sidebar container. |
headerClass / bodyClass / footerClass | ClassValue | null | null | Extra classes for the region wrappers. |
Slots: header, body and footer, each rendered in its .drawer-header / .drawer-body / .drawer-footer region; a region without content is not rendered.
Emits: update:open.
Item components
| Component | Key props |
|---|---|
UiSidebarText | text |
UiSidebarLink | as, to / href, text, icon, tooltipText, tooltipPlacement, child, active, disabled, ariaLabelledby; #badge slot after the text |
UiSidebarGroup | text, icon, collapsible, defaultOpen |
UiSidebarDropdown | title, subtitle, interactive, placement |
CSS variables
The drawer (.navigation-drawer) and its mobile overlay read these variables. Set them on :root or on a wrapper. bordered keeps its square corners and no shadow, and the rail keeps its larger brand image. Set the width with the width prop or --cffy-sidebar-width. Hover and pressed backgrounds come from the shared state layer.
:root {
--cffy-sidebar-bg-color: var(--cffy-surface-container-low);
--cffy-sidebar-item-active-bg-color: var(--cffy-primary-a10);
--cffy-sidebar-item-active-color: var(--cffy-on-primary);
--cffy-sidebar-item-active-icon-color: var(--cffy-on-primary);
}
| Variable | Default | Controls |
|---|---|---|
--cffy-sidebar-width | --cffy-container-2xs (18rem) | Drawer width (the rail is half of it) |
--cffy-sidebar-bg-color / --cffy-sidebar-color | --cffy-background / --cffy-on-background | Background and text |
--cffy-sidebar-border-color | --cffy-outline-surface | Edge border of bordered, and the line beside child items |
--cffy-sidebar-shadow | --cffy-shadow-lg | Shadow |
--cffy-sidebar-radius | --cffy-radius-xl | Inner-edge corners (the screen edge stays square) |
--cffy-sidebar-padding | --cffy-space-16 | Drawer padding |
--cffy-sidebar-spacing | --cffy-space-8 | Padding and gaps of header, body, footer and items |
--cffy-sidebar-brand-size | 2.5rem | Brand image and icon size |
--cffy-sidebar-easing / --cffy-sidebar-duration | --cffy-ease-emphasized / --cffy-duration-600 | Open, close and rail transitions |
--cffy-sidebar-item-color | muted text tone | Item and section-label text |
--cffy-sidebar-item-font-size / --cffy-sidebar-item-font-weight | --cffy-fs-3xs / --cffy-fw-500 | Item text |
--cffy-sidebar-item-radius | --cffy-shape-control | Item corners |
--cffy-sidebar-item-active-color / --cffy-sidebar-item-active-bg-color | --cffy-on-background / --cffy-surface-body-pane | Current-route item |
--cffy-sidebar-item-active-icon-color | --cffy-primary-a10 | Leading icon of the current-route item |
--cffy-sidebar-overlay-color / --cffy-sidebar-overlay-blur | black at 25% / 2px | Overlay behind the mobile drawer |