Sidebar
Sidebar de app-shell componível com estado persistido, colapso só de ícones, drawer mobile e uma API composta de 24 peças.
Visão geral
Sidebar é o primitivo de navegação pilar do registry: uma sidebar de app-shell componível com regiões de header / content / footer, estado colapsado persistido (via cookie), atalho de teclado (Cmd / Ctrl + B), modo de colapso só de ícones com fallbacks de tooltip, drawer mobile automático via Sheet, e 24 subcomponentes que compõem em menus de nav, badges, actions, skeletons e sub-menus.
Use a Sidebar quando a aplicação tem mais de uma tela cheia de navegação - workspaces, seções de configuração, categorias de content. O primitivo é dono do chrome de layout (o rail, o gap, a área main inset) para que quem chama foque na estrutura do menu. Para nav de site de marketing use NavigationMenu; para troca de seção dentro da página use Tabs.
Preview
'use client';import { Sidebar, SidebarContent, SidebarFooter, SidebarGroup, SidebarGroupContent, SidebarGroupLabel, SidebarHeader, SidebarInset, SidebarMenu, SidebarMenuBadge, SidebarMenuButton, SidebarMenuItem, SidebarProvider, SidebarTrigger,} from '@gremorie/rx-navigation';import { CalendarIcon, HomeIcon, InboxIcon, SearchIcon, SettingsIcon,} from 'lucide-react';const SIDEBAR_NAV: { title: string; icon: typeof HomeIcon; badge?: string }[] = [ { title: 'Home', icon: HomeIcon }, { title: 'Inbox', icon: InboxIcon, badge: '12' }, { title: 'Calendar', icon: CalendarIcon }, { title: 'Search', icon: SearchIcon }, { title: 'Settings', icon: SettingsIcon }, ];export function SidebarPreview() { return ( // transform-gpu makes this box the containing block for the sidebar's // fixed-positioned panel, so the whole app shell stays inside the card. <div className="relative h-[420px] w-full transform-gpu overflow-hidden rounded-md border"> <SidebarProvider className="!min-h-0 h-full min-h-full"> <Sidebar collapsible="icon"> <SidebarHeader> <div className="flex items-center gap-2 px-2 py-1 font-semibold"> <div className="flex size-6 items-center justify-center rounded bg-sidebar-primary text-sidebar-primary-foreground"> G </div> <span className="group-data-[collapsible=icon]:hidden"> Gremorie </span> </div> </SidebarHeader> <SidebarContent> <SidebarGroup> <SidebarGroupLabel>Application</SidebarGroupLabel> <SidebarGroupContent> <SidebarMenu> {SIDEBAR_NAV.map((item, i) => ( <SidebarMenuItem key={item.title}> <SidebarMenuButton isActive={i === 0} tooltip={item.title} > <item.icon /> <span>{item.title}</span> </SidebarMenuButton> {item.badge ? ( <SidebarMenuBadge>{item.badge}</SidebarMenuBadge> ) : null} </SidebarMenuItem> ))} </SidebarMenu> </SidebarGroupContent> </SidebarGroup> </SidebarContent> <SidebarFooter> <SidebarMenu> <SidebarMenuItem> <SidebarMenuButton tooltip="Account"> <SettingsIcon /> <span>Account</span> </SidebarMenuButton> </SidebarMenuItem> </SidebarMenu> </SidebarFooter> </Sidebar> <SidebarInset> <header className="flex h-12 items-center gap-2 border-b px-4"> <SidebarTrigger /> <span className="font-medium text-sm">Dashboard</span> </header> <div className="p-6 text-muted-foreground text-sm"> Main content area. Use the trigger to toggle the sidebar. </div> </SidebarInset> </SidebarProvider> </div> );}Anatomia
SidebarProvider context + CSS width vars + Cmd/Ctrl+B shortcut
├─ Sidebar the panel; variant / side / collapsible
│ ├─ SidebarHeader top region (brand, switcher, search)
│ │ └─ SidebarInput sidebar-density input
│ ├─ SidebarContent scrollable middle region
│ │ ├─ SidebarGroup a labelled section block
│ │ │ ├─ SidebarGroupLabel small uppercase section label
│ │ │ ├─ SidebarGroupAction action button in the group header
│ │ │ ├─ SidebarGroupContent group body; hosts the menu
│ │ │ │ └─ SidebarMenu <ul> of nav rows
│ │ │ │ └─ SidebarMenuItem <li> row wrapper
│ │ │ │ ├─ SidebarMenuButton the clickable nav entry
│ │ │ │ ├─ SidebarMenuAction per-row action button
│ │ │ │ ├─ SidebarMenuBadge count / status slot
│ │ │ │ ├─ SidebarMenuSkeleton loading placeholder row
│ │ │ │ └─ SidebarMenuSub <ul> of sub-items
│ │ │ │ └─ SidebarMenuSubItem sub-row <li> wrapper
│ │ │ │ └─ SidebarMenuSubButton nested link row
│ │ │ └─ SidebarSeparator rule between regions / groups
│ ├─ SidebarFooter bottom region (user menu, actions)
│ └─ SidebarRail thin grab handle to toggle
└─ SidebarInset main content area beside the sidebarInstalação
bash npx gremorie@latest add rx-sidebar bash pnpm dlx gremorie@latest add rx-sidebar bash yarn dlx gremorie@latest add rx-sidebar bash bunx --bun gremorie@latest add rx-sidebar Uso
import { HomeIcon, InboxIcon, SettingsIcon } from "lucide-react";
import {
Sidebar,
SidebarContent,
SidebarGroup,
SidebarGroupContent,
SidebarGroupLabel,
SidebarHeader,
SidebarInset,
SidebarMenu,
SidebarMenuButton,
SidebarMenuItem,
SidebarProvider,
SidebarTrigger,
} from "@gremorie/rx-navigation";
export function AppShell() {
return (
<SidebarProvider>
<Sidebar>
<SidebarHeader>Workspace</SidebarHeader>
<SidebarContent>
<SidebarGroup>
<SidebarGroupLabel>Navigation</SidebarGroupLabel>
<SidebarGroupContent>
<SidebarMenu>
<SidebarMenuItem>
<SidebarMenuButton tooltip="Home">
<HomeIcon />
<span>Home</span>
</SidebarMenuButton>
</SidebarMenuItem>
<SidebarMenuItem>
<SidebarMenuButton tooltip="Inbox">
<InboxIcon />
<span>Inbox</span>
</SidebarMenuButton>
</SidebarMenuItem>
<SidebarMenuItem>
<SidebarMenuButton tooltip="Settings">
<SettingsIcon />
<span>Settings</span>
</SidebarMenuButton>
</SidebarMenuItem>
</SidebarMenu>
</SidebarGroupContent>
</SidebarGroup>
</SidebarContent>
</Sidebar>
<SidebarInset>
<header className="flex items-center gap-2 border-b p-4">
<SidebarTrigger />
<h1 className="font-semibold">Page title</h1>
</header>
<main className="p-6">...</main>
</SidebarInset>
</SidebarProvider>
);
}A edição Angular deste componente hoje é distribuída a partir do source (veja o side-by-side no workbench); sua entrada no registry vem a seguir.
Envolva cada story do Storybook e cada preview das docs no seu próprio <SidebarProvider>. Um provider global montado em apps/docs conflitaria com a sidebar do Fumadocs.
API
O primitivo Sidebar expõe 24 exports mais um hook useSidebar(). A API é organizada por papel: container de estado, frame raiz, regiões estruturais, blocos de grupo, itens de menu, sub-menus e controles auxiliares.
Container de estado
<SidebarProvider>
O provider de context que é dono do estado de aberto / colapsado, do estado mobile-open, do atalho de teclado e da persistência em cookie. Toda subárvore que usa Sidebar deve ser envolvida.
| Prop | Type | Default | Description |
|---|---|---|---|
defaultOpen | boolean | true | Estado inicial de aberto quando não controlado. Lido de um cookie server-side para persistência em SSR. |
open | boolean | - | Estado de aberto controlado. |
onOpenChange | (open: boolean) => void | - | Dispara quando o estado de aberto muda. |
style | React.CSSProperties | - | Mesclado no wrapper. Use para sobrescrever --sidebar-width / --sidebar-width-icon. |
className | string | - | Classes extras para o wrapper. |
O provider grava sidebar_state=<true|false> num cookie de 7 dias a cada mudança, então o estado colapsado sobrevive a reloads.
useSidebar()
Hook que expõe o valor do context. Lança um erro se chamado fora de um <SidebarProvider>.
const {
state, // "expanded" | "collapsed"
open, // boolean
setOpen, // (open: boolean) => void
openMobile, // boolean
setOpenMobile, // (open: boolean) => void
isMobile, // boolean (true below md breakpoint)
toggleSidebar, // () => void (toggles desktop or mobile depending on viewport)
} = useSidebar();Frame raiz
<Sidebar>
O shell da sidebar. Escolhe entre três caminhos de renderização automaticamente: um painel estático (collapsible="none"), uma sidebar desktop com o layout de rail + gap + container, ou um <Sheet> mobile.
| Prop | Type | Default | Description |
|---|---|---|---|
side | "left" | "right" | "left" | Em qual borda do viewport a sidebar acopla. |
variant | "sidebar" | "floating" | "inset" | "sidebar" | "sidebar" é rente à borda com um border. "floating" é um card arredondado com sombra. "inset" é uma sidebar rente pareada com <SidebarInset> para renderizar a área main dentro de um card. |
collapsible | "offcanvas" | "icon" | "none" | "offcanvas" | "offcanvas" desliza a sidebar inteira para fora. "icon" colapsa para a largura só de ícones (3 rem). "none" desabilita o colapso e sempre renderiza aberta. |
<SidebarTrigger>
O botão de toggle. Envolve o <Button variant="ghost" size="icon"> de Forms com um ícone PanelLeft e um label "Toggle Sidebar" visualmente oculto. Encaminha onClick para quem chama poder encadear analytics.
| Prop | Type | Default | Description |
|---|---|---|---|
...props | React.ComponentProps<typeof Button> | - | Encaminhado para o botão subjacente. |
<SidebarRail>
Uma alça fina de agarrar ao longo da borda interna da sidebar. Clique para alternar. Oculta no mobile, oculta quando collapsible="none", oculta no estado colapsado offcanvas.
<SidebarInset>
A área <main> pareada com a sidebar. Com variant="inset", renderiza o main como um card arredondado com inset das bordas do viewport. Use no lugar de um <main> simples quando você quiser que o chrome visual combine com as variants floating / inset.
Regiões estruturais
| Component | Role |
|---|---|
<SidebarHeader> | Região do topo da sidebar. Renderiza flex flex-col gap-2 p-2. Coloque a marca, o switcher de workspace ou um input de busca aqui. |
<SidebarContent> | Região do meio scrollável. Renderiza flex min-h-0 flex-1 flex-col gap-2 overflow-auto. Hospeda um ou mais blocos <SidebarGroup>. |
<SidebarFooter> | Região de baixo. Renderiza flex flex-col gap-2 p-2. Coloque o menu do usuário, o toggle de tema ou ações rápidas aqui. |
<SidebarSeparator> | Um <Separator> estilizado com mx-2 w-auto bg-sidebar-border para uso entre regiões. |
<SidebarInput> | O <Input> de Forms estilizado para a densidade da sidebar (h-8, sombra transparente). |
Blocos de grupo
| Component | Role |
|---|---|
<SidebarGroup> | Um bloco lógico dentro de <SidebarContent>. Renderiza relative flex w-full min-w-0 flex-col p-2. |
<SidebarGroupLabel> | Um pequeno label maiúsculo no topo de um grupo. Oculto automaticamente no estado colapsado collapsible="icon". Suporta asChild. |
<SidebarGroupAction> | Um botão de action posicionado absolutamente no topo-direito de um label de grupo (ex.: um "+" para adicionar um novo projeto). Oculto no estado colapsado collapsible="icon". Suporta asChild. |
<SidebarGroupContent> | O corpo do grupo. Hospeda um <SidebarMenu>. |
Itens de menu
<SidebarMenu> e <SidebarMenuItem>
<SidebarMenu> é um <ul> de itens. <SidebarMenuItem> é o wrapper <li> que é dono do estado de hover e focus via a classe group/menu-item.
<SidebarMenuButton>
A entrada de menu clicável. Envolve um <button> (ou qualquer elemento via asChild).
| Prop | Type | Default | Description |
|---|---|---|---|
asChild | boolean | false | Quando true, delega a renderização ao único filho (use com o Link do framework). |
isActive | boolean | false | Marca a página atual; estiliza via data-active="true". |
variant | "default" | "outline" | "default" | "outline" adiciona um ring de 1 px (com tint de background para ler contra fundos complexos). |
size | "default" | "sm" | "lg" | "default" | "default" tem 32 px de altura, "sm" 28 px, "lg" 48 px (compacta, densa e linha de marca respectivamente). |
tooltip | string | TooltipContentProps | - | Quando definido e a sidebar está no estado colapsado só de ícones, o botão é envolvido num <Tooltip> mostrando este content no hover. Obrigatório para menus só de ícones manterem os labels alcançáveis. |
<SidebarMenuAction>
Um botão pequeno posicionado absolutamente no topo-direito de um item de menu (ex.: um ellipsis para abrir um context menu). Oculto no estado colapsado só de ícones.
| Prop | Type | Default | Description |
|---|---|---|---|
asChild | boolean | false | Delega a um único filho. |
showOnHover | boolean | false | Quando true, visível apenas no hover, focus ou quando ativo. |
<SidebarMenuBadge>
Um badge numérico ou de status pequeno posicionado absolutamente na linha do menu. Oculto no estado colapsado só de ícones.
| Prop | Type | Default | Description |
|---|---|---|---|
...props | React.ComponentProps<"div"> | - | Atributos padrão de div. |
<SidebarMenuSkeleton>
Uma linha placeholder que combina com a altura do <SidebarMenuButton>. Use durante carregamentos iniciais de dados. A largura é randomizada entre 50 por cento e 90 por cento para que uma pilha de skeletons pareça natural.
| Prop | Type | Default | Description |
|---|---|---|---|
showIcon | boolean | false | Quando true, renderiza um skeleton quadrado no slot de ícone também. |
Sub-menus
| Component | Role |
|---|---|
<SidebarMenuSub> | Um <ul> de sub-itens. Oculto no estado colapsado só de ícones. |
<SidebarMenuSubItem> | Um wrapper <li> para sub-itens. |
<SidebarMenuSubButton> | Uma linha de 28 px estilizada para aninhamento; renderiza um <a> por default. |
Props de SidebarMenuSubButton:
| Prop | Type | Default | Description |
|---|---|---|---|
asChild | boolean | false | Delega a um único filho (use com o Link do framework). |
isActive | boolean | false | Marca a localização atual. |
size | "sm" | "md" | "md" | "sm" é texto de 12 px; "md" é texto de 14 px. |
Composição
Uma árvore típica de Sidebar tem esta aparência:
<SidebarProvider>
<Sidebar>
<SidebarHeader>{/* Brand or workspace switcher */}</SidebarHeader>
<SidebarContent>
<SidebarGroup>
<SidebarGroupLabel>Workspace</SidebarGroupLabel>
<SidebarGroupContent>
<SidebarMenu>
<SidebarMenuItem>
<SidebarMenuButton tooltip="Home">
<HomeIcon />
<span>Home</span>
</SidebarMenuButton>
</SidebarMenuItem>
<SidebarMenuItem>
<SidebarMenuButton tooltip="Inbox">
<InboxIcon />
<span>Inbox</span>
</SidebarMenuButton>
<SidebarMenuBadge>12</SidebarMenuBadge>
</SidebarMenuItem>
</SidebarMenu>
</SidebarGroupContent>
</SidebarGroup>
<SidebarSeparator />
<SidebarGroup>
<SidebarGroupLabel>Projects</SidebarGroupLabel>
<SidebarGroupAction>
<PlusIcon />
</SidebarGroupAction>
<SidebarGroupContent>
<SidebarMenu>
{projects.map((p) => (
<SidebarMenuItem key={p.id}>
<SidebarMenuButton tooltip={p.name}>{p.name}</SidebarMenuButton>
<SidebarMenuSub>
{p.sections.map((s) => (
<SidebarMenuSubItem key={s.id}>
<SidebarMenuSubButton href={s.href}>
{s.name}
</SidebarMenuSubButton>
</SidebarMenuSubItem>
))}
</SidebarMenuSub>
</SidebarMenuItem>
))}
</SidebarMenu>
</SidebarGroupContent>
</SidebarGroup>
</SidebarContent>
<SidebarFooter>{/* User menu, theme toggle */}</SidebarFooter>
<SidebarRail />
</Sidebar>
<SidebarInset>
<header>
<SidebarTrigger />
...
</header>
<main>...</main>
</SidebarInset>
</SidebarProvider>O provider é dono do estado. O frame raiz é dono do chrome de layout. As regiões, grupos e peças de menu são a superfície de composição do consumidor.
Variações
Sidebar básica
<SidebarProvider>
<Sidebar>
<SidebarHeader>
<span className="font-semibold">My App</span>
</SidebarHeader>
<SidebarContent>
<SidebarMenu>
<SidebarMenuItem>
<SidebarMenuButton>
<HomeIcon />
<span>Home</span>
</SidebarMenuButton>
</SidebarMenuItem>
<SidebarMenuItem>
<SidebarMenuButton>
<InboxIcon />
<span>Inbox</span>
</SidebarMenuButton>
</SidebarMenuItem>
</SidebarMenu>
</SidebarContent>
</Sidebar>
<SidebarInset>
<SidebarTrigger />
<main>...</main>
</SidebarInset>
</SidebarProvider>Use como a sidebar mínima viável: marca no header, menu de itens de topo, main inset.
Com grupos, badges e actions
<SidebarContent>
<SidebarGroup>
<SidebarGroupLabel>Mail</SidebarGroupLabel>
<SidebarGroupContent>
<SidebarMenu>
<SidebarMenuItem>
<SidebarMenuButton isActive>
<InboxIcon />
<span>Inbox</span>
</SidebarMenuButton>
<SidebarMenuBadge>12</SidebarMenuBadge>
</SidebarMenuItem>
<SidebarMenuItem>
<SidebarMenuButton>
<ArchiveIcon />
<span>Archive</span>
</SidebarMenuButton>
</SidebarMenuItem>
</SidebarMenu>
</SidebarGroupContent>
</SidebarGroup>
<SidebarGroup>
<SidebarGroupLabel>Labels</SidebarGroupLabel>
<SidebarGroupAction aria-label="Add label">
<PlusIcon />
</SidebarGroupAction>
<SidebarGroupContent>
<SidebarMenu>
{labels.map((label) => (
<SidebarMenuItem key={label.id}>
<SidebarMenuButton>{label.name}</SidebarMenuButton>
<SidebarMenuAction showOnHover>
<MoreHorizontalIcon />
</SidebarMenuAction>
</SidebarMenuItem>
))}
</SidebarMenu>
</SidebarGroupContent>
</SidebarGroup>
</SidebarContent>Use quando a sidebar carrega navegação categorizada com affordances por grupo (adicionar, contagem) e actions por linha.
Modo colapso de ícones
<SidebarProvider>
<Sidebar collapsible="icon">
<SidebarContent>
<SidebarMenu>
<SidebarMenuItem>
<SidebarMenuButton tooltip="Home">
<HomeIcon />
<span>Home</span>
</SidebarMenuButton>
</SidebarMenuItem>
<SidebarMenuItem>
<SidebarMenuButton tooltip="Settings">
<SettingsIcon />
<span>Settings</span>
</SidebarMenuButton>
</SidebarMenuItem>
</SidebarMenu>
</SidebarContent>
</Sidebar>
</SidebarProvider>Use quando a sidebar deve colapsar para a largura só de ícones (3 rem) em vez de deslizar off-canvas. A prop tooltip em <SidebarMenuButton> é obrigatória neste modo - ela expõe o label num tooltip alinhado ao lado quando colapsado.
Variant floating
'use client';import { Sidebar, SidebarContent, SidebarGroup, SidebarGroupContent, SidebarGroupLabel, SidebarHeader, SidebarInset, SidebarMenu, SidebarMenuButton, SidebarMenuItem, SidebarProvider, SidebarTrigger,} from '@gremorie/rx-navigation';import { CalendarIcon, HomeIcon, InboxIcon, SettingsIcon } from 'lucide-react';const SIDEBAR_NAV: { title: string; icon: typeof HomeIcon }[] = [ { title: 'Home', icon: HomeIcon }, { title: 'Inbox', icon: InboxIcon }, { title: 'Calendar', icon: CalendarIcon }, { title: 'Settings', icon: SettingsIcon },];export function SidebarFloatingPreview() { return ( // transform-gpu makes this box the containing block for the sidebar's // fixed-positioned panel, so the whole app shell stays inside the card. <div className="relative h-[420px] w-full transform-gpu overflow-hidden rounded-md border bg-sidebar"> <SidebarProvider className="!min-h-0 h-full min-h-full"> <Sidebar variant="floating" collapsible="icon"> <SidebarHeader> <div className="flex items-center gap-2 px-2 py-1 font-semibold"> <div className="flex size-6 items-center justify-center rounded bg-sidebar-primary text-sidebar-primary-foreground"> G </div> <span className="group-data-[collapsible=icon]:hidden"> Gremorie </span> </div> </SidebarHeader> <SidebarContent> <SidebarGroup> <SidebarGroupLabel>Workspace</SidebarGroupLabel> <SidebarGroupContent> <SidebarMenu> {SIDEBAR_NAV.map((item, i) => ( <SidebarMenuItem key={item.title}> <SidebarMenuButton isActive={i === 0} tooltip={item.title} > <item.icon /> <span>{item.title}</span> </SidebarMenuButton> </SidebarMenuItem> ))} </SidebarMenu> </SidebarGroupContent> </SidebarGroup> </SidebarContent> </Sidebar> <SidebarInset> <header className="flex h-12 items-center gap-2 border-b px-4"> <SidebarTrigger /> <span className="font-medium text-sm">Overview</span> </header> <div className="p-6 text-muted-foreground text-sm"> The floating variant detaches the panel with a rounded border and shadow. Toggle to see the icon-only collapsed state. </div> </SidebarInset> </SidebarProvider> </div> );}Passe variant="floating" para destacar o painel da borda do viewport com um border arredondado e sombra. Ela combina naturalmente com collapsible="icon". As outras variants são "sidebar" (default, rente à borda) e "inset" (que empurra o <SidebarInset> pareado para dentro como um card arredondado).
Com sub-menus
<SidebarMenuItem>
<SidebarMenuButton>
<FolderIcon />
<span>Projects</span>
</SidebarMenuButton>
<SidebarMenuSub>
<SidebarMenuSubItem>
<SidebarMenuSubButton href="/projects/alpha">Alpha</SidebarMenuSubButton>
</SidebarMenuSubItem>
<SidebarMenuSubItem>
<SidebarMenuSubButton href="/projects/beta" isActive>
Beta
</SidebarMenuSubButton>
</SidebarMenuSubItem>
<SidebarMenuSubItem>
<SidebarMenuSubButton href="/projects/gamma">Gamma</SidebarMenuSubButton>
</SidebarMenuSubItem>
</SidebarMenuSub>
</SidebarMenuItem>Use para navegação de dois níveis - uma lista de projetos com seções, um grupo de configurações com sub-páginas. Sub-menus se auto-ocultam no modo colapsado só de ícones.
Estado controlado com hidratação por cookie
function AppShell({ defaultOpen }: { defaultOpen: boolean }) {
const [open, setOpen] = useState(defaultOpen);
useEffect(() => {
// sync to cookie / server preference if desired
}, [open]);
return (
<SidebarProvider open={open} onOpenChange={setOpen}>
<Sidebar>...</Sidebar>
<SidebarInset>...</SidebarInset>
</SidebarProvider>
);
}
// Server: read the sidebar_state cookie and pass as defaultOpen.Use quando o resto do app precisa observar ou sobrescrever o estado da sidebar (analytics, layouts multi-painel).
Controles cientes de mobile
function MyHeader() {
const { isMobile, toggleSidebar, state } = useSidebar();
return (
<header className="flex items-center gap-2">
<SidebarTrigger />
{!isMobile && <span>State: {state}</span>}
</header>
);
}Use para ramificar a UI conforme o viewport. Abaixo de md (768 px) isMobile é true e toggleSidebar abre / fecha o <Sheet> mobile em vez do painel desktop.
Acessibilidade
- Contrato do provider:
useSidebar()lança um erro quando chamado fora de<SidebarProvider>, expondo o requisito de wiring já na primeira renderização. - Rotulagem do trigger:
<SidebarTrigger>carrega um label "Toggle Sidebar" visualmente oculto. O rail (<SidebarRail>) carregaaria-label="Toggle Sidebar"etitle="Toggle Sidebar"; ele é removido da ordem de tab (tabIndex={-1}) porque o botão trigger é o ponto de entrada descobrível por teclado. - Atalho de teclado:
Cmd+B(Mac) ouCtrl+B(Windows / Linux) alterna a sidebar globalmente. O atalho é adicionado aowindowenquanto um provider está montado e removido no unmount. Documente o atalho na superfície de ajuda do seu app. - Drawer mobile: abaixo de 768 px a sidebar renderiza dentro de um
<Sheet>com um<SheetTitle>Sidebar</SheetTitle>e<SheetDescription>(ambossr-only) para que usuários de leitor de tela recebam um anúncio de dialog adequado. - Tooltips no modo só de ícones: quando
collapsible="icon"está colapsado,<SidebarMenuButton>envolve seu content num<Tooltip>alinhado ao lado oposto (side="right"para uma sidebar acoplada à esquerda). Forneçatooltip="..."em cada botão de menu no modo de ícones - do contrário os usuários veem ícones sem labels. - Estado ativo:
isActiveem<SidebarMenuButton>e<SidebarMenuSubButton>definedata-active="true"para estilização; combine comaria-current="page"(via o elemento de link host quando usandoasChild). - Persistência em cookie: o cookie
sidebar_statetempath=/emax-age=7 days. O cookie não é sensível para segurança, mas documente-o na sua divulgação de privacidade se você lista cookies.
Relacionados
- Sheet - o primitivo de dialog sobre o qual a Sidebar é montada no mobile.
- NavigationMenu - nav primária de site de marketing.
- Tabs - troca de seção dentro da página, não navegação de app-shell.
- Tooltip - expõe labels no modo colapsado só de ícones.