Spec version v0.10.0

Sidebar Navigation

Purpose

The sidebar navigation occupies the left chrome of the application shell and is present in every Perforce product. Its role depends on the shell variant. In the page shell (with topbar), the sidebar provides secondary navigation within a product area — the topbar handles top-level product area switching, the app switcher, and global utilities. In the sidebar-only shell (no topbar), the sidebar is the sole navigation surface, carrying all routes, the product wordmark, and global utilities (user avatar, settings) in its footer. In both cases, the sidebar is the primary wayfinding mechanism for moving between sections (for example, between Streams, Changelists, Jobs, and Settings within Helix Core). Cross-product switching (between Helix Core, Helix DAM, etc.) is handled by the app switcher in the TopBar, not the sidebar. The sidebar is always present for authenticated application views and is never used as a temporary or contextual panel.

Structure

The sidebar is a fixed-position vertical strip. From top to bottom, it contains four zones: a header zone, a navigation content zone (scrollable), an optional footer utility zone, and the collapse/expand control.

+------------------------+
|  [Logo / Product Name] |  ← SidebarHeader (64px, aligns with TopBar)
+------------------------+
|  [Section Label]       |  ← group label (xs, uppercase, tertiary color)
|  [Icon]  Nav Item      |  ← nav item (default state)
|  [Icon]  Nav Item      |
|  ▼ [Icon]  Nav Group   |  ← collapsible group (collapsed)
|                        |
|  [Section Label]       |
|  [Icon]  Nav Item ●    |  ← nav item (active state, indigo tint + border)
|    [Icon]  Sub-item    |  ← sub-item (indented, no icon or small icon)
|    [Icon]  Sub-item    |
|  [Icon]  Nav Item      |
|                        |
|  (scrollable zone)     |
|                        |
+------------------------+
|  [Icon]  User Profile  |  ← SidebarFooter
|  [Compact menu btn]    |  ← collapse/expand trigger
+------------------------+

The header zone is fixed-height at --force-layout-header-height (64px). In the full page shell (with TopBar), this zone aligns with the TopBar and is typically empty or contains a secondary label — the product wordmark lives in the TopBar. In the sidebar-only shell (no TopBar), this zone contains the product wordmark, logo, or a combination. This zone does not scroll.

The navigation content zone fills all remaining height between the header and footer. It is the only internally scrollable region of the sidebar. Items in this zone are organized into sections and groups.

The footer utility zone sits at the bottom of the sidebar, fixed. Its contents depend on whether the product uses the page-shell (topbar + sidebar) or the sidebar-only-shell:

  • In page-shell: the footer contains only the sidebar compact-mode toggle (SidebarShrinkTrigger), which switches the sidebar between its expanded (256px) and shrunk (64px) icon-only state. The global user utilities — user avatar and name, account settings, notifications — live in the topbar-right cluster, NOT in this footer. Do NOT duplicate them here.
  • In sidebar-only-shell: because there is no topbar, the footer additionally contains the signed-in user avatar and name, a link to account settings, and any other persistent low-priority global utilities. The compact toggle still lives here too.

Navigation Sections

A section is a labeled grouping of related nav items. The section label is rendered as non-interactive text in --force-font-size-xs (12px), uppercase, letter-spacing wide (--force-font-letterSpacing-wide), --force-color-text-tertiary. Section labels receive a top margin of --force-spacing-4 (16px) from the previous item or section end, except for the very first section in the list which has no additional top spacing. Section labels are not themselves focusable or clickable.

Sections without labels (unlabeled sections) may be used when the structure is obvious from context, but avoid having more than one unlabeled section per sidebar — they lose their organizing purpose.

Navigation Items

Each navigation item consists of an icon on the left and a label text to its right. In expanded mode, both are visible. In shrunk mode, only the icon is visible and a tooltip appears on hover to show the label.

Item anatomy:

  • Icon: Material Symbols Rounded, size md (20px), color --force-color-icon-default in default state
  • Label text: --force-font-size-sm (14px), font weight medium (--force-font-weight-medium, 500), --force-color-text-secondary in default state
  • Padding: --force-spacing-2 (8px) vertical, --force-spacing-4 (16px) horizontal
  • Border radius: --force-radius-md (6px)
  • Full width of the sidebar minus --force-spacing-2 (8px) horizontal margin on each side

Item states:

  • Default: transparent background, --force-color-text-secondary label
  • Hover: --force-color-bg-interactive-hover background (neutral-50), --force-color-text-primary label — transition in --force-duration-fast (150ms) with --force-easing-standard
  • Active/selected: --force-color-bg-interactive-active background (indigo-50), --force-color-text-interactive-active label (indigo-700), icon color shifts to --force-color-icon-interactive (indigo-500), and a 4px left-side accent border in --force-color-border-primary (indigo-500)
  • Focus (keyboard): --force-color-border-focus outline with --force-shadow-focus-ring — the focus ring sits outside the item's border radius

Only one item may be in the active state at any time across the entire sidebar. The active item represents the current page or section. It is never set programmatically to "active" without a corresponding current route match.

Collapsible Groups

A collapsible group is a nav item that, when expanded, reveals one or more sub-items indented beneath it. The group trigger behaves like a regular nav item but carries a chevron icon on its right edge that rotates 90 degrees when the group is open. The chevron uses --force-color-icon-default.

The animation for expanding and collapsing a group uses --force-duration-normal (250ms) with --force-easing-standard. The content height animates from zero to its natural height; avoid using display: none toggled immediately, as this prevents the transition.

Sub-items within a collapsible group are indented by --force-spacing-6 (24px) from the left edge of the parent item's icon. Sub-items may optionally carry a small icon (size sm, 16px) or simply use label text alone. Sub-items have no section label, only the parent group as their organizational container. Sub-items use the same state styles as top-level nav items.

A collapsible group whose sub-item is currently active must remain open and show the parent group item in a "contains active" visual state: the group label uses --force-color-text-primary but the group itself does not receive the active indigo background — only the active sub-item does. This distinguishes "I am active" from "I contain what is active."

Groups that are open by default should be limited to one, typically the group containing the current active item. Do not open all groups by default; it defeats the purpose of collapsing.

Shrunk (Icon-Only) Mode

When the sidebar is in its shrunk state at --force-layout-sidebar-collapsed (64px), section labels are hidden entirely. Nav item labels are hidden. Only the 20px icon remains centered in the 64px strip. A tooltip — using the Force UI Tooltip component with side="right" — appears on hover to display the full item label and serves as the accessible label for the icon.

In shrunk mode, collapsible groups cannot be expanded inline. Hovering or clicking a group item opens a popover flyout to the right of the sidebar showing the sub-items. This flyout uses --force-shadow-sm and --force-color-bg-surface with a 1px --force-color-border-default border.

The sidebar collapse/expand transition animates the width between 256px and 64px over --force-duration-normal (250ms). Content inside the sidebar that would overflow at 64px is clipped (overflow: hidden on the container during the transition).

Responsive Behavior

At and above --force-breakpoint-lg (992px), the sidebar displays in expanded mode by default. Users may switch to shrunk mode via the compact toggle in the footer zone. The preference is persisted in a cookie so it survives page loads.

Between --force-breakpoint-md (768px) and --force-breakpoint-lg (992px), the sidebar automatically enters shrunk mode. The compact toggle is hidden at this breakpoint because the shrunk state is not user-controlled — it is enforced by the viewport. The sidebar cannot be expanded at this size.

Below --force-breakpoint-md (768px), the sidebar is fully removed from the document flow. It becomes an off-canvas Sheet (drawer) opened by a toggle in the TopBar. When open on mobile, the sidebar displays at full expanded width (288px on mobile). The nav items render identically to the desktop expanded state. The sheet overlay covers the content area but not the TopBar.

Token Usage

Element Token
Sidebar background --force-color-bg-emphasis
Sidebar right border None — the inset content panel provides visual separation
Sidebar expanded width --force-layout-sidebar-width (256px)
Sidebar shrunk width --force-layout-sidebar-collapsed (64px)
Nav item padding (vertical) --force-spacing-2 (8px)
Nav item padding (horizontal) --force-spacing-4 (16px)
Nav item border radius --force-radius-md (6px)
Nav item label — default --force-color-text-secondary
Nav item label — hover --force-color-text-primary
Nav item label — active --force-color-text-interactive-active
Nav item background — hover --force-color-bg-interactive-hover
Nav item background — active --force-color-bg-interactive-active
Nav item active left border --force-color-border-primary (4px width)
Nav item icon — default --force-color-icon-default
Nav item icon — active --force-color-icon-interactive
Section label size --force-font-size-xs (12px)
Section label color --force-color-text-tertiary
Section label letter spacing --force-font-letterSpacing-wide
Section top spacing --force-spacing-4 (16px)
Sub-item indent --force-spacing-6 (24px)
Hover transition duration --force-duration-fast (150ms)
Group expand transition --force-duration-normal (250ms)
Transition easing --force-easing-standard
Flyout shadow (shrunk mode) --force-shadow-sm
Focus ring --force-shadow-focus-ring

Accessibility

The sidebar navigation region is a <nav> element with role="navigation" and aria-label="Application navigation". If additional navigation landmarks exist on the page (for example, a tab list within the content area), each must have a distinct aria-label to allow screen reader users to distinguish them.

All nav items are rendered as <a> anchor elements (not <button> elements), because they navigate to routes. The active nav item carries aria-current="page". Do not set aria-selected on nav items — that attribute is for tabs and listbox options.

Collapsible group triggers are an exception: they do not navigate to a page, they toggle a group open or closed. They are rendered as <button> elements carrying aria-expanded (true/false) and aria-controls pointing to the id of the sub-item list. The sub-item list carries role="list" and is hidden from assistive technology via aria-hidden="true" only when in the collapsed state.

Section labels (non-interactive group headings) should use role="presentation" or be rendered as <div> elements with a unique id, referenced via aria-labelledby on the corresponding sub-list so screen readers can announce the section name when navigating into it.

In shrunk mode, all nav item labels are visually hidden but must remain in the DOM as accessible text (not removed via display: none). Use sr-only styling (visually hidden, accessible) for these labels. The tooltip is a supplementary affordance for sighted users, not a replacement for the accessible name.

Keyboard navigation within the sidebar must support Tab to move forward through all interactive items and Shift+Tab to move backward. Arrow key navigation (up/down arrows to move between items) is optional but recommended for power users. Home and End keys should jump to the first and last items when arrow navigation is implemented.

Guidance

The sidebar carries navigation items only. Do not embed form controls, status indicators, metric summaries, or data content in the sidebar. Those elements belong in the main content area.

Do not add more than three levels of navigation hierarchy. The maximum depth is: section label > collapsible group > sub-item. Sub-items do not have their own collapsible children. If a product requires deeper nesting, reconsider the information architecture.

Do NOT use the active state style (indigo background, left border) for items that are merely "parent of active" or "related to active." Reserve the active treatment strictly for the exact item that maps to the current page. The containing group may use a bolder label color to indicate it contains the active child, but no background tint.

Do NOT use badges or notification counts on nav items unless they represent genuinely actionable alerts requiring the user's attention. Decorative counts (such as total item counts) add noise and do not justify the visual weight.

Do NOT use custom icons that are not from the Material Symbols Rounded set. Icon inconsistency across a sidebar creates visual disorder.

Limit sections to five or fewer items each. If a section grows beyond five items, split it into two sections with distinct headings rather than making it a single long unlabeled list.

The sidebar background (--force-color-bg-emphasis) is distinctly different from the content area (--force-color-bg-surface). Do not override this background with white or any other color — the contrast between the chrome and the content area is an intentional structural signal.

Composition

The sidebar uses the SidebarProvider, Sidebar, SidebarHeader, SidebarContent, SidebarGroup, SidebarGroupLabel, SidebarMenu, SidebarMenuItem, SidebarMenuButton, SidebarMenuSub, SidebarMenuSubItem, SidebarMenuSubButton, SidebarFooter, and SidebarShrinkTrigger components from the Force UI Sidebar component family. Tooltips for shrunk-mode items use the Tooltip component with side="right" and delayDuration={300}. Icons within nav items use Material Symbols Rounded at size md (20px). The collapse trigger button in the footer uses the tertiary button variant.