Layout

The frame every one of the 73 pages in this kit sits inside: navigation, the bar above the content, the strip below it, and the theme panel. None of them read as loudly as a button or a chart, but a shell that gets them wrong is the first thing anyone notices.

App shell

The menu isn't part of any component — it all comes from one file, nav.config.ts. Rearranging the sidebar means editing this file, not digging into LayoutSidebar.vue. Active-route matching also lives separately as a pure function in utils/nav.ts/users never highlights /user.

export const NAV_SECTIONS: NavSection[] = [
  {
    title: 'Main',
    items: [
      { label: 'Dashboard', icon: 'lucide:layout-dashboard', to: '/dashboard' },
    ],
  },
]

Three sidebar modes, side by side

expanded and mini toggle via the topbar button; hidden can only be picked directly from ThemeCustomizer — that's why the rightmost box below is completely empty, not a bug. currentPath here is injected as a fixed /dashboard/analytics, not from the router, so the preview doesn't change as you navigate between docs pages.

PageHeader

The banner every page opens with: a title, where you are, and the one or two actions that matter most on that screen. The actions slot is optional — a page with nothing to do up there just omits it, rather than everyone reinventing the same flex row.

With breadcrumb and actions

Invoices

Every invoice raised this quarter.

<UiPageHeader title="Invoices" description="Every invoice raised this quarter." :breadcrumb="[{ label: 'Home', to: '/' }, { label: 'Invoices' }]">
  <template #actions>
    <UiButton variant="secondary" icon="lucide:download">Export</UiButton>
    <UiButton icon="lucide:plus">New invoice</UiButton>
  </template>
</UiPageHeader>

Without breadcrumb

Settings

Manage your workspace.

<UiPageHeader title="Settings" description="Manage your workspace." />
PropTypeDefaultDescription
titlestringRequired. The H1.
descriptionstringundefinedOne line under the title.
breadcrumbCrumbItem[]undefinedRenders UiBreadcrumb above the title when given.

ThemeCustomizer

The skin panel, light/dark mode, and sidebar mode — mounted once in layouts/app.vue, opened via the palette button in the topbar. Skin and mode are saved in a cookie via useSkin(); sidebar mode is shared via useAppShell().

Open the panel

<UiThemeCustomizer v-model:open="open" v-model:sidebar-mode="sidebarMode" />
PropTypeDefaultDescription
v-model:openbooleanfalsePanel open/closed — rendered via UiDrawer on the right side.
v-model:sidebarMode'expanded' | 'mini' | 'hidden''expanded'The only direct way to pick hidden mode — the topbar toggle button only cycles expanded/mini.