Sidebar
The app's main navigation, down the left edge. Named groups of links, the current page marked, counts for what is waiting, and a rail of icons when space is short.
Installation
Set up Camaleo once (Installation), then import what you need. It renders on the client.
import { Sidebar, SidebarHeader, SidebarNav, SidebarGroup, SidebarItem, SidebarFooter, SidebarToggle } from "@camaleo/react";Anatomy
An <aside> with three rows. Only the nav scrolls, so the brand and the footer stay in view. SidebarBrand and SidebarContent are other names for SidebarHeader and SidebarNav.
Examples
Every example is a real file you can copy. They all wear the skin, mode and brand from the bar above. Press Tab to walk through the links.
Collapse to a rail
Press the button at the bottom. Labels hide but still name each link for screen readers, and a tooltip shows them on hover and focus.
Current page
Click a link. current adds aria-current="page", and the bar beside it shows the page without relying on color.
With your router
Pass render={<NextLink href="…" />}. The router handles the click without reloading the page, and the item keeps its style. Here current comes from the path, so the link to this page is marked.
Counts that make sense when heard
The count is read as part of the link's name, so add the unit in VisuallyHidden: "Inbox 12 unread", not "Inbox 12".
Nested sections
A project with its own pages opens in place with a Collapsible. Its header is a button, because it only shows links and goes nowhere.
Account menu in the footer
The person and their settings sit at the bottom, out of the way of the pages. The menu opens upward when there is no room below.
On phones, in a drawer
The Sidebar has no phone mode of its own. Below a breakpoint, hide it and put the same links in a left Drawer, which traps focus and returns it to the menu button.
In context
These are whole pieces of UI you can copy, in the skin you picked.
Guidelines
Two or three headings let people skip to the part of the app they need.
Without groups, people read every link to find one, and related pages end up far apart.
Short names fit the column and are quick to scan. The page itself can have a longer title.
Long labels get cut off, and the words that tell them apart are the ones that disappear.
Props
onCollapseChange.SidebarToggle (or useSidebar().setCollapsed) changes it.SidebarItem takes href, icon, count and current, plus render to use your router's link instead of the a. SidebarGroup takes label. SidebarToggle takes collapseLabel and expandLabel; pass them in the page's language (defaults "Collapse sidebar" and "Expand sidebar"). useSidebar() reads and sets the state from any part inside.
Styling hooks
Restyle it without forking: target the attributes, or set the tokens on a parent, a skin or the sidebar itself.
Attributes
Tokens it reads
Color
Shape & size
Type & motion
This component only
/* a wider sidebar and a slimmer rail */
.app [data-slot="sidebar"] {
--cml-sidebar-width: 16rem;
--cml-sidebar-rail-width: 3.25rem;
}Accessibility
- The links sit in a <nav>. Give it an aria-label, like "Main", so it is listed by name among the page's landmarks.
- Groups are role="group" and take their name from the label, so a screen reader says "Workspace, group" when you enter one.
- The toggle is a button with aria-expanded and a name that says what it will do. In the rail, each link keeps its full name and only the label is hidden.
- The current page gets aria-current="page" and a bar and a weight change, not only a color.