Sidebar
The macOS 26 source-list sidebar — frosted vibrancy panel, three densities, neutral-pill selection, collapsible.
shadcn's Sidebar composition restyled to the Apple macOS 26 UI Kit. The panel is
the kit's 240px frosted-vibrancy surface (--acr-surface + backdrop-blur),
so the desktop shows softly through it. Row geometry is lifted from the kit's three
sidebar densities — Small (24 / 11px), Medium (32 / 13px), Large (40 / 15px) — and
the selected row is the macOS neutral pill (a gray fill, monochrome label — the
source-list keeps selection monochrome, no System Blue tint). The full
shadcn API is intact: a SidebarProvider with cookie-persisted open state and the
⌘B keyboard toggle, off-canvas / icon / none collapse modes, a mobile Sheet,
groups, menu + sub-menu, badges, a rail, and an inset content area.
Installation
npx shadcn add https://acrylic-ui.vercel.app/r/sidebar.jsonThis also pulls the pieces it composes from — Button, Input, Separator, Sheet,
Tooltip, Skeleton — and the use-mobile hook.
Usage
Wrap your app in SidebarProvider, then place a Sidebar next to a
SidebarInset (your main content).
"use client"import { Archive, ChevronRight, ChevronsUpDown, Folder, Inbox, Send, Settings, Star, Trash2,} from "lucide-react"import { Avatar, AvatarFallback, AvatarImage } from "@/registry/acrylic/avatar"import { ExampleBackdrop } from "@/components/example-backdrop"import { Searchbar } from "@/registry/acrylic/searchbar"import { Sidebar, SidebarContent, SidebarFooter, SidebarGroup, SidebarGroupContent, SidebarGroupLabel, SidebarHeader, SidebarMenu, SidebarMenuBadge, SidebarMenuButton, SidebarMenuItem, SidebarMenuSub, SidebarMenuSubButton, SidebarMenuSubItem, SidebarProvider, SidebarSeparator,} from "@/registry/acrylic/sidebar"export default function SidebarDemo() { return ( <ExampleBackdrop> <SidebarProvider className="h-[26rem] w-auto min-h-0"> <Sidebar collapsible="none" className="h-full overflow-hidden rounded-xl shadow-[0_8px_24px_rgba(0,0,0,0.28)]" > <SidebarHeader> <div className="flex items-center gap-2 px-2 py-1"> <div className="flex size-7 items-center justify-center rounded-md bg-[var(--primary)] text-[var(--primary-foreground)]"> <Send className="size-4" /> </div> <span className="text-[13px] font-semibold text-foreground"> Acrylic Mail </span> </div> <div className="px-1"> <Searchbar size="large" placeholder="Search" /> </div> </SidebarHeader> <SidebarContent className="scrollbar-mac"> <SidebarGroup> <SidebarGroupLabel>Mailboxes</SidebarGroupLabel> <SidebarGroupContent> <SidebarMenu> <SidebarMenuItem> <SidebarMenuButton isActive> <Inbox /> <span>Inbox</span> </SidebarMenuButton> <SidebarMenuBadge>24</SidebarMenuBadge> </SidebarMenuItem> <SidebarMenuItem> <SidebarMenuButton> <Star /> <span>Starred</span> </SidebarMenuButton> </SidebarMenuItem> <SidebarMenuItem> <SidebarMenuButton> <Send /> <span>Sent</span> </SidebarMenuButton> </SidebarMenuItem> <SidebarMenuItem> <SidebarMenuButton> <Archive /> <span>Archive</span> </SidebarMenuButton> </SidebarMenuItem> <SidebarMenuItem> <SidebarMenuButton> <Trash2 /> <span>Trash</span> </SidebarMenuButton> </SidebarMenuItem> </SidebarMenu> </SidebarGroupContent> </SidebarGroup> <SidebarSeparator /> <SidebarGroup> <SidebarGroupLabel>Folders</SidebarGroupLabel> <SidebarGroupContent> <SidebarMenu> <SidebarMenuItem> <SidebarMenuButton> <Folder /> <span>Projects</span> <ChevronRight className="ml-auto" /> </SidebarMenuButton> <SidebarMenuSub> <SidebarMenuSubItem> <SidebarMenuSubButton isActive> Acrylic UI </SidebarMenuSubButton> </SidebarMenuSubItem> <SidebarMenuSubItem> <SidebarMenuSubButton>Roadmap</SidebarMenuSubButton> </SidebarMenuSubItem> </SidebarMenuSub> </SidebarMenuItem> <SidebarMenuItem> <SidebarMenuButton> <Folder /> <span>Receipts</span> </SidebarMenuButton> </SidebarMenuItem> </SidebarMenu> </SidebarGroupContent> </SidebarGroup> </SidebarContent> <SidebarFooter> <SidebarMenu> <SidebarMenuItem> <SidebarMenuButton size="lg" className="h-12"> <Avatar className="size-8 rounded-lg"> <AvatarImage src="https://avatar.vercel.sh/jagger" alt="Jagger" /> <AvatarFallback className="rounded-lg">JH</AvatarFallback> </Avatar> <div className="grid flex-1 text-left leading-tight"> <span className="truncate text-[13px] font-semibold">Jagger H</span> <span className="truncate text-[11px] text-muted-foreground">jagger@acrylic.dev</span> </div> <ChevronsUpDown className="ml-auto size-4 text-muted-foreground" /> </SidebarMenuButton> </SidebarMenuItem> </SidebarMenu> </SidebarFooter> </Sidebar> </SidebarProvider> </ExampleBackdrop> )}Collapsible
The collapsible preview uses intentionally long workspace, navigation, and sub-menu labels so truncation can be checked in both the expanded sidebar and the collapsed hover-card flyout.
"use client"import { useState } from "react"import { Calendar, ChevronRight, ChevronsUpDown, GalleryVerticalEnd, Home, Inbox, Search, Settings2,} from "lucide-react"import { ExampleBackdrop } from "@/components/example-backdrop"import { Avatar, AvatarFallback, AvatarImage,} from "@/registry/acrylic/avatar"import { Separator } from "@/registry/acrylic/separator"import { Sidebar, SidebarContent, SidebarFooter, SidebarGroup, SidebarGroupContent, SidebarGroupLabel, SidebarHeader, SidebarInset, SidebarMenu, SidebarMenuButton, SidebarMenuItem, SidebarMenuSub, SidebarMenuSubButton, SidebarMenuSubItem, SidebarProvider, SidebarTrigger,} from "@/registry/acrylic/sidebar"const NAV = [ { id: "home", title: "Home dashboard with an extremely long primary navigation label", icon: Home, }, { id: "inbox", title: "Inbox", icon: Inbox }, { id: "calendar", title: "Calendar", icon: Calendar }, { id: "search", title: "Search", icon: Search },]const PROJECT_ITEMS = [ { id: "tokens", title: "Tokens And Semantic Color Variables With A Long Label", }, { id: "components", title: "Components And Interaction Patterns With A Long Label", },]export default function SidebarApp() { const [activeItem, setActiveItem] = useState("home") const activeLabel = NAV.find((item) => item.id === activeItem)?.title ?? PROJECT_ITEMS.find((item) => item.id === activeItem)?.title ?? "Tokens" return ( <ExampleBackdrop> <div className="flex h-[30rem] w-full max-w-3xl overflow-hidden rounded-xl shadow-[0_8px_24px_rgba(0,0,0,0.28)]"> <SidebarProvider className="h-full w-full min-h-0"> {/* collapsible="icon": the SidebarTrigger in the header collapses the rail to icons and back — works inside this contained box with no extra CSS (the Sidebar positions itself within the provider). */} <Sidebar collapsible="icon" className="rounded-l-xl"> {/* Header — team / workspace switcher */} <SidebarHeader> <SidebarMenu> <SidebarMenuItem> <SidebarMenuButton size="lg" className="h-12"> <div className="flex aspect-square size-8 items-center justify-center rounded-lg bg-[var(--primary)] text-[var(--primary-foreground)]"> <GalleryVerticalEnd className="size-4" /> </div> <div className="grid flex-1 text-left leading-tight"> <span className="overflow-hidden text-[13px] font-semibold"> Acrylic Design Systems Incorporated With A Very Long Name </span> <span className="overflow-hidden text-[11px] text-muted-foreground"> Enterprise workspace description that should fade out </span> </div> <ChevronsUpDown className="ml-auto size-4 text-muted-foreground" /> </SidebarMenuButton> </SidebarMenuItem> </SidebarMenu> </SidebarHeader> <SidebarContent className="scrollbar-mac"> <SidebarGroup> <SidebarGroupLabel>Platform</SidebarGroupLabel> <SidebarGroupContent> <SidebarMenu> {NAV.map((item) => ( <SidebarMenuItem key={item.id}> <SidebarMenuButton isActive={activeItem === item.id} onClick={() => setActiveItem(item.id)} > <item.icon /> <span>{item.title}</span> </SidebarMenuButton> </SidebarMenuItem> ))} </SidebarMenu> </SidebarGroupContent> </SidebarGroup> <SidebarGroup> <SidebarGroupLabel>Projects</SidebarGroupLabel> <SidebarGroupContent> <SidebarMenu> <SidebarMenuItem> <SidebarMenuButton isActive={activeItem === "design-system"} onClick={() => setActiveItem("design-system")} > <Settings2 /> <span>Design System Navigation Item With A Very Long Label</span> <ChevronRight className="ml-auto" /> </SidebarMenuButton> <SidebarMenuSub> {PROJECT_ITEMS.map((item) => ( <SidebarMenuSubItem key={item.id}> <SidebarMenuSubButton isActive={activeItem === item.id} onClick={() => setActiveItem(item.id)} > <span>{item.title}</span> </SidebarMenuSubButton> </SidebarMenuSubItem> ))} </SidebarMenuSub> </SidebarMenuItem> </SidebarMenu> </SidebarGroupContent> </SidebarGroup> </SidebarContent> {/* Footer — user item with avatar */} <SidebarFooter> <SidebarMenu> <SidebarMenuItem> <SidebarMenuButton size="lg" className="h-12"> <Avatar className="size-8 rounded-lg"> <AvatarImage src="https://avatar.vercel.sh/jagger" alt="Jagger" /> <AvatarFallback className="rounded-lg">JH</AvatarFallback> </Avatar> <div className="grid flex-1 text-left leading-tight"> <span className="overflow-hidden text-[13px] font-semibold"> Jagger H With A Very Long Display Name </span> <span className="overflow-hidden text-[11px] text-muted-foreground"> jagger.with.a.very.long.email.address@acrylic.dev </span> </div> <ChevronsUpDown className="ml-auto size-4 text-muted-foreground" /> </SidebarMenuButton> </SidebarMenuItem> </SidebarMenu> </SidebarFooter> </Sidebar> {/* Inset — the main content pane */} <SidebarInset className="bg-background"> <header className="flex h-12 shrink-0 items-center gap-2 border-b border-[var(--acr-border)] px-4"> <SidebarTrigger /> <Separator orientation="vertical" className="mr-1 !h-4 data-[orientation=vertical]:!h-4" /> <span className="text-[13px] text-muted-foreground shrink-0"> Design System </span> <ChevronRight className="size-3.5 text-muted-foreground shrink-0" /> <span className="min-w-0 truncate text-[13px] font-medium text-foreground"> {activeLabel} </span> </header> <div className="flex flex-1 flex-col gap-4 overflow-auto p-4"> <div className="grid auto-rows-min gap-4 md:grid-cols-3"> <div className="aspect-video rounded-xl bg-[var(--acr-chip)]" /> <div className="aspect-video rounded-xl bg-[var(--acr-chip)]" /> <div className="aspect-video rounded-xl bg-[var(--acr-chip)]" /> </div> <div className="min-h-40 flex-1 rounded-xl bg-[var(--acr-chip)]" /> </div> </SidebarInset> </SidebarProvider> </div> </ExampleBackdrop> )}Densities
The three kit sidebar sizes map onto the SidebarMenuButton size prop —
sm / default / lg give the Small / Medium / Large row height + font.
"use client"import { Inbox, Send, Star } from "lucide-react"import { ExampleBackdrop } from "@/components/example-backdrop"import { Sidebar, SidebarContent, SidebarGroup, SidebarGroupContent, SidebarGroupLabel, SidebarMenu, SidebarMenuButton, SidebarMenuItem, SidebarProvider,} from "@/registry/acrylic/sidebar"const SIZES = [ { size: "sm" as const, label: "Small · 24 / 11px" }, { size: "default" as const, label: "Medium · 32 / 13px" }, { size: "lg" as const, label: "Large · 40 / 15px" },]export default function SidebarSizes() { return ( <ExampleBackdrop className="gap-4 !flex-row flex-wrap items-start justify-center"> {SIZES.map(({ size, label }) => ( <SidebarProvider key={size} className="w-auto min-h-0"> <Sidebar collapsible="none" className="overflow-hidden rounded-xl shadow-[0_8px_24px_rgba(0,0,0,0.28)]" > <SidebarContent> <SidebarGroup> <SidebarGroupLabel>{label}</SidebarGroupLabel> <SidebarGroupContent> <SidebarMenu> <SidebarMenuItem> <SidebarMenuButton size={size} isActive> <Inbox /> <span>Inbox</span> </SidebarMenuButton> </SidebarMenuItem> <SidebarMenuItem> <SidebarMenuButton size={size}> <Star /> <span>Starred</span> </SidebarMenuButton> </SidebarMenuItem> <SidebarMenuItem> <SidebarMenuButton size={size}> <Send /> <span>Sent</span> </SidebarMenuButton> </SidebarMenuItem> </SidebarMenu> </SidebarGroupContent> </SidebarGroup> </SidebarContent> </Sidebar> </SidebarProvider> ))} </ExampleBackdrop> )}Anatomy
| part | role |
|---|---|
SidebarProvider | holds open/collapsed state (cookie-persisted), ⌘B toggle, mobile detection |
Sidebar | the frosted panel plus app-navigation layout adapter; side, variant (sidebar / floating / inset), collapsible (offcanvas / icon / none) |
SidebarTrigger / SidebarRail | toggle controls (button / draggable edge rail) |
SidebarInset | the main content area beside the sidebar |
SidebarHeader / SidebarFooter | pinned top / bottom regions |
SidebarContent | the scrolling middle (add scrollbar-mac for the thin macOS scrollbar) |
SidebarGroup / SidebarGroupLabel / SidebarGroupContent | a labelled section |
SidebarMenu / SidebarMenuItem / SidebarMenuButton | the rows; isActive paints the accent pill, size sets density, tooltip shows a label when collapsed to icons |
SidebarMenuBadge / SidebarMenuAction | a trailing count / hover action on a row |
SidebarMenuSub / SidebarMenuSubItem / SidebarMenuSubButton | a nested, indented list |
SidebarSeparator | a hairline divider between groups |
Notes
- The panel uses the frosted vibrancy material (
--acr-surface+backdrop-blur-xl). On desktop the real macOS sidebar shows the wallpaper through it — overlay it on content (not a flat page) to read as glass. - Selected rows are the neutral pill (
--acr-chip, a gray fill with a monochrome label — no--primary/System Blue tint). Unselected rows show no hover fill; only the selected row is highlighted, matching the macOS source list where hovering an item shows nothing (a mouse-downactive:press is still fed back). collapsible="icon"shrinks the sidebar to a 3rem icon rail; passtooltipon eachSidebarMenuButtonso the label appears on hover in that mode. Usecollapsible="none"for a static, always-open panel (as in the previews above).- Inside
Shell, do not set the source-list width only with aSidebarclassName. Put the width on Shell (sidebarWidth,sidebarCollapsedWidth) so Sidebar's internal gap and visible rail stay aligned. - State is restored across reloads via the
sidebar_statecookie.