Components · Navigation
Sidebar
BetaThe app's main navigation.
- Source
components/ui/sidebar.tsx - shadcn/ui Sidebar
- Figma: not linked yet (Q13)
When to use
- As coach-admin's main navigation between top-level areas: Dashboard, Clients, Programs, Nutrition, Messages, Payments, Settings.
When not to use
- To switch views inside a page: use Tabs.
- For filters or secondary settings: use a sheet or a side panel.
Anatomy
The preview shows every part: the header (product name), group labels ("Coaching", "Business"), menu items with an icon and a label, the active item, count badges at the end of an item, and the footer (the signed-in coach).
Variants
| Behavior | When |
|---|---|
| Expanded | Desktop, from md. The default. |
Collapsed to icons (collapsible="icon") | When the coach collapses it (⌘B / Ctrl+B or the trigger); labels show as tooltips |
| Sheet | Below md: opens from the start edge as a sheet |
Static (collapsible="none") | Embedded layouts and this preview |
SuperCoach's sidebar is brand Black with the active item in Electric Green and black text, as in the logo tile. Coach uses neutral-900 with an orange active item. Switch the preview's theme to compare.
States
| State | What changes | Token |
|---|---|---|
| Rest | Light text on the sidebar color | bg-sidebar, text-sidebar-foreground |
| Hover | Lighter fill | bg-sidebar-accent, text-sidebar-accent-foreground |
| Active (current page) | Brand fill, dark text | bg-sidebar-primary, text-sidebar-primary-foreground |
| Focus-visible | Ring in the active color | ring-sidebar-ring |
The active state is changed from shadcn's grey sidebar-accent to sidebar-primary, because the tokens define the active item as the brand color ($nav-active-color).
Usage guidelines
- 5 to 9 items, grouped by job ("Coaching", "Business"), in the order coaches use them.
- Exactly one active item: the section of the current page.
- Count badges only for things that need action (unread messages, pending clients), not totals.
Content guidelines
- One-word nouns where possible: "Clients", "Programs", "Payments".
- Group labels are short and plain: "Coaching", not "Coaching tools & features".
Accessibility
| Key | Does |
|---|---|
| Tab | Moves through the items |
| Enter | Opens the item |
| ⌘B / Ctrl+B | Collapses or expands the sidebar |
| Escape | Closes the mobile sheet |
- Wrap it in
<nav aria-label="Main">in the app. The current item hasaria-current="page", announced as "Clients, current page". - Count badges get an accessible label ("24 new"), not just the number.
- Lime on black and orange on neutral-900 both pass contrast; see the matrix.
RTL
The sidebar sits on the right in RTL: the component picks its side from the direction (Radix useDirection) unless side is set. Item icons, labels and count badges follow start and end. This is a change from upstream, which always defaulted to the left.
Tokens used
Read from components/ui/sidebar.tsx at build time, so this list changes when the component does. Opacity modifiers such as /90 are shadcn hover and focus tints on top of the token.
| Token | Utilities | SuperCoach | Coach |
|---|---|---|---|
| --background | bg-background | white | white |
| --sidebar | bg-sidebar | neutral-950 | neutral-900 |
| --sidebar-accent | bg-sidebar-accent | neutral-800 | neutral-800 |
| --sidebar-accent-foreground | text-sidebar-accent-foreground | white | white |
| --sidebar-border | border-sidebar-border bg-sidebar-border | neutral-800 | neutral-700 |
| --sidebar-foreground | text-sidebar-foreground text-sidebar-foreground/70 | white | white |
| --sidebar-primary | bg-sidebar-primary | lime-300 | orange-500 |
| --sidebar-primary-foreground | text-sidebar-primary-foreground | neutral-950 | neutral-950 |
| --sidebar-ring | ring-sidebar-ring | lime-300 | orange-500 |
- Layer
z-(--z-sticky) - Motion
duration-(--duration-base) - Motion
ease-standard - Opacity
opacity-(--opacity-disabled) - Radius
rounded-lg - Radius
rounded-md - Radius
rounded-xl - Shadow
shadow-sm - Type
font-medium - Type
text-sm - Type
text-xs
Props
<SidebarProvider>
Also takes every prop of <div>
| Prop | Type | Default |
|---|---|---|
| defaultOpen? | boolean | true |
| open? | boolean | — |
| onOpenChange? | (open: boolean) => void | — |
<Sidebar>
Also takes every prop of <div>
| Prop | Type | Default |
|---|---|---|
| side? | "left" | "right" | — |
| variant? | "sidebar" | "floating" | "inset" | "sidebar" |
| collapsible? | "offcanvas" | "icon" | "none" | "offcanvas" |
<SidebarTrigger>
Also takes every prop of <Button>
No props of its own.
<SidebarGroupLabel>
Also takes every prop of <div>
| Prop | Type | Default |
|---|---|---|
| asChild? | boolean | false |
<SidebarMenuButton>
Also takes every prop of <button>
| Prop | Type | Default |
|---|---|---|
| variant | "default" | "outline" | "default" |
| size | "default" | "sm" | "lg" | "default" |
| asChild? | boolean | false |
| isActive? | boolean | false |
| tooltip? | string | React.ComponentProps<typeof TooltipContent> | — |
<SidebarMenuBadge>
Also takes every prop of <div>
No props of its own.
Replaces in coach-admin
From the read-only audit of coach-admin: 100 style values, 129 uses.
coach-admin's sidebar is CoreUI's, with a navy-to-black gradient ($nav-color). The system uses one sidebar color per theme.
Classes and selectors
.nav-link27.sidebar23.nav-item8.sidebar .nav-link .sidebar-todos-count-badge7.sidebar .sidebar-nav5.sidebar-minimized .sidebar .nav-item:hover > .nav-link, .si4.sidebar .nav4.sidebar-nav4.sidebar .sidebar-minimizer4.sidebar-close3.sidebar-form .form-control3.nav-dropdown.open3
SCSS variables in these styles
$sidebar-widthnot a color: spacing, size or layout$navbar-heightnot a color: spacing, size or layout$mobile-sidebar-widthnot a color: spacing, size or layout$sidebar-minimized-widthnot a color: spacing, size or layout$zindex-stickynot a color: spacing, size or layout$nav-active-txt-colornot a color: spacing, size or layout$sidebar-compact-widthnot a color: spacing, size or layout$sidebar-colornot a color: spacing, size or layout$nav-btn-colornot a color: spacing, size or layout$border-radiusnot a color: spacing, size or layout$body-colornot a color: spacing, size or layout$text-mutednot a color: spacing, size or layout