Design Reference

Sidebar
Behaviour

How the sidebar navigates, animates, and responds to the user. Covers every state, transition, and interaction decision.

Reference

Glossary

Terms used throughout this document.

Term Meaning
L1 The narrow 64 px navigation column fixed to the left edge of the screen. It holds the logo, section buttons, and the profile button. It is always visible.
L2 panel The 256 px panel that slides out from behind the L1 column when the user hovers or clicks an L1 button. It shows the full menu for the active section.
L1 button One of the icon buttons in the L1 column. Each button corresponds to a section (e.g. Accounting, Banking) and opens that section in the L2 panel.
Rail The vertical chain of keyboard-focusable elements inside the L1 column: the logo, all L1 buttons, and the profile button. Arrow keys move focus along the rail.

Overview

Two-layer navigation

The navigation is split into two layers. A narrow 64 px L1 navigation is always visible on the left edge. When the user engages with it, a 256 px L2 panel slides in and reveals the full menu for that section.

The L2 panel is not a page or modal. It overlays the content area temporarily. Once the user clicks a destination, the L2 closes and the page updates.

Design intent

The sidebar should feel lightweight and fast. It is always visible and reachable, at minimum in its collapsed state.


Opening

How the L2 panel opens

The L2 panel opens on hover. When the cursor enters an L1 button, or anywhere in the vertical span between the first button and the last, the L2 panel appears. The relevant section scrolls into position aligned with the nearest button.

If the L2 panel is already open and a different L1 button is hovered, the L2 stays visible and smoothly scrolls to the new section. There is no close-and-reopen flicker.

Step by step

  1. User hovers an L1 button or the gap area between buttons.
  2. The L2 panel fades in and shifts right from an offset of translateX(−4px).
  3. The L2 panel scrolls so the first item of the section sits level with the button that was hovered.
  4. The first item of the active section receives a subtle background highlight, previewing where a click would land.

Clicking an L1 button

Clicking an L1 button immediately navigates to the first item of that section. The page title updates and the item becomes selected. It is equivalent to hovering the button and clicking the first menu item in one gesture.

When switching sections

If the L2 panel is already open, switching sections animates the scroll. The L2 does not close and reopen. Fast scanning feels fluid rather than jumpy.


Closing

Dismissal behaviour

The L2 panel closes as soon as the user moves their cursor away from both the sidebar and the L2 panel. It also closes when a menu item is clicked.

All dismissal triggers

Action Result
Cursor leaves the L2 panel L2 panel closes
Click a menu item L2 panel closes, title updates
Click the logo L2 panel closes, title resets to Dashboard
Unpin the menu L2 panel closes
Scroll reset

After closing, the L2 panel's scroll position resets silently after 240ms. This is safely after the closing animation finishes at 150ms. The next time the L2 panel opens, scroll is always at the correct position.


Animation

Transition timing and motion

The L2 panel animates on two properties at once: opacity and a horizontal shift. Together they make the L2 panel feel like it emerges from behind the L1 navigation rather than appearing from nowhere.

Timing

Opening is intentionally slower than closing. The L2 panel takes its time arriving so it feels considered. It gets out of the way quickly so it never feels like a blocker.

Opening
200ms ease-out
Closing
150ms ease-out

L2 Panel specifications

L2 panel
Hidden position translateX(−4px)
Visible position translateX(0)
Hidden opacity 0
Visible opacity 1
Open transition 200ms ease-out
Close transition 150ms ease-out
Scroll reset delay 240ms after close
Width 256px
Position fixed · left: 64px

Accessibility

When the operating system has "Reduce Motion" enabled, all L2 panel transitions are disabled. The L2 panel still appears and disappears, just instantly with no animation.


Scroll alignment

Scroll alignment

Each section in the L2 panel aligns with its corresponding button. When you hover the Accounting button, the first Accounting item lines up with that button, not the top of the L2 panel.

This creates a spatial relationship between the L1 button and the content. The user's eye does not have to travel. The relevant content is right next to the thing they pointed at.

Design intent

The alignment is not decorative. It reduces the cognitive gap between "I hovered this button" and "here is what belongs to it." The two things feel like one thing.

Animated scroll between sections

When the L2 panel is already open and the user moves to a different section, the scroll animates. Duration scales with distance. Short jumps feel quick and long jumps feel deliberate. It is capped so it never drags.

Scroll animation
Minimum duration 300ms
Maximum duration 600ms
Duration formula clamp(300ms, distance × 0.6, 600ms)
Easing ease-out (cubic)

Items near the bottom

Sections near the bottom of the sidebar need extra space below them so the L2 panel can scroll them into the aligned position. The L2 panel temporarily extends its own scrollable area to allow this, then resets silently when it closes.


Pinning

Pinning the L2 panel

A pin button in the top-right corner of the L2 panel lets users keep the menu permanently visible. Pinning changes both the behaviour and the visual presentation.

Unpinned (default)

Overlay

L2 panel floats over content. Appears and disappears on hover. Content remains full width behind it.

Pinned

Docked

L2 panel stays visible at all times. Content area shifts right by 256 px. Sidebar and L2 panel share one unified background.

Visual changes when pinned

  • The background of both the sidebar and L2 panel changes from #ffffff to #fafafa, visually merging them into one surface.
  • The L2 panel's drop shadow disappears. It is no longer floating.
  • The main content area gains a margin-left: 256px so nothing is obscured.
  • All transition animations on the L2 panel are bypassed. It is always fully visible.

Selection

Selected item state

Clicking a menu item commits the user to a destination. The selection is reflected in three places at once: the menu item itself, the L1 button for that section, and the page title.

Element Selected state
Menu item Refer to Figma for color values.
L1 button Refer to Figma for color values.
Page title Updates to the selected item's label

Pinned L2 panel behaviour

Hovering an L1 button while pinned still navigates to that section and scrolls to highlight it. Once the cursor moves into the L2 panel, the active state is cleared and all sections return to their neutral state. Moving the cursor out of the L2 panel without going toward the sidebar also clears any active state.


Page title

Page title updates

The page title updates when the user selects a destination. Hovering alone never changes it. Clicking an L1 button commits to the first item of that section, so the title updates on button click too.

Action Title becomes
Click the logo Dashboard
Click an L1 button The first item of that section, e.g. clicking Banking sets the title to Bank transactions
Click a menu item in the L2 panel The item name, e.g. Bank transactions
Note

Hovering an L1 button opens the L2 panel but does not update the title. Only an explicit click on an L1 button, a menu item, or the logo commits to a page.


Dropdowns

Profile and domain dropdowns

The profile dropdown and domain picker use the same opacity and directional shift pattern as the main L2 panel. Each has its own axis, anchor, and trigger.

Profile dropdown

Opens and closes by clicking the profile avatar. It slides upward as it appears. Clicking anywhere outside closes it.

Profile dropdown
Hidden position translateY(4px)
Visible position translateY(0)
Open transition 200ms ease-out
Close transition 150ms ease-out
Width 256px
Anchor fixed · bottom: 56px · left: 16px

Domain picker

Opens when the cursor enters the domain item inside the profile dropdown. It slides in from the left as it appears. Moving the cursor between the domain item and the picker keeps it open. There is no accidental dismissal during that transition.

Domain picker
Hidden position translateX(−1px)
Visible position translateX(0)
Open transition 200ms ease-out
Close transition 150ms ease-out
Width 256px
Trigger mouseenter / mouseleave
Position anchor right edge of domain row (dynamic)

Keyboard navigation

Keyboard navigation

The sidebar, L2 panel, profile dropdown, and domain picker are all fully keyboard-navigable. Tab moves between major interactive elements. Arrow keys move within a component once it is open.

L1 Navigation

The logo, all L1 buttons, and the profile button form a single vertical navigation chain. ↑ and ↓ move through this chain without opening the L2 panel. The L2 panel only opens on an explicit → or Enter.

Key Result
Tab Move focus between the logo, L1 buttons, and the profile button
↓ Arrow Down Focus next item in the rail (logo → L1 buttons → profile). Stops at the profile button and does not wrap
↑ Arrow Up Focus previous item in the rail. Stops at the logo and does not wrap
→ Arrow Right Open the L2 panel for the focused L1 button; focus moves to the first item in the section
Enter / Space Open the L2 panel for that L1 button; focus moves to the first item in the section
Enter / Space (L2 panel already open for same button) Close the L2 panel
Enter / Space (on the logo) Navigate to Dashboard, same as clicking the logo

L2 Panel

Once the L2 panel is open, arrow keys navigate across the pin button and all menu items, crossing section boundaries freely. The pin button sits at the top of the cycle. As focus moves, the active section updates to match. Navigating with arrows does not close the L2 panel.

Key Result
↓ Arrow Down Focus next item; from the last item wraps up to the pin button
↑ Arrow Up Focus previous item; from the first item moves up to the pin button
← Arrow Left Close L2 panel, return focus to the L1 button that opened it
Enter Select item and update the page title. If unpinned, the L2 panel closes and focus returns to the L1 button. If pinned, focus stays on the item.
Escape Close L2 panel, return focus to the L1 button that opened it
Tab Close L2 panel, move focus to the next focusable element in the page
Keyboard mode suppresses hover effects

Pressing any navigation key (arrows, Tab, Enter, Escape, or Space) enters keyboard mode. In keyboard mode, all CSS hover effects are suppressed. Button background highlights and icon colour changes are hidden so the mouse's physical position cannot interfere with keyboard navigation visuals. Keyboard mode ends the moment the mouse moves.

Focus stays in L2 when panel is pinned

When the L2 panel is pinned, pressing Enter on a menu item keeps focus on that item. Focus only returns to the L1 button when the panel is unpinned.

Profile dropdown

Key Result
Enter / Space Open dropdown; focus moves to first item
↓ / ↑ Navigate items; wraps at both ends
Escape Close dropdown, return focus to profile button
Tab (when open) Close dropdown, move focus out of the component

Domain picker

The domain picker is nested inside the profile dropdown. With a mouse, it opens on hover of the domain row. With a keyboard, it is opened deliberately.

Key Result
Enter / Space / → Open picker from the domain row; focus moves to the search input
↓ Arrow Down Move from search input to first domain item; then cycle forward
↑ Arrow Up Move to previous item; from the first item, returns focus to search input
Enter (on item) Select domain
Escape / ← Close picker, return focus to domain row

Focus & display

Focus and display

Keyboard focus is always clearly indicated. Display quality adapts to screen hardware.

Focus indicator

All interactive elements share the same focus ring: a soft dark shadow with no outline. It appears only on keyboard navigation (:focus-visible) so mouse users never see it.

Focus ring
Style box-shadow: 0 0 0 3px rgba(0,0,0,0.16)
Outline none
Trigger :focus-visible only
Applied to logo · L1 buttons · pin button · profile button · menu items · dropdown items
Logo border-radius 6px (matches L1 buttons)

L2 inert state

When the L2 panel is not visible, it carries the inert attribute. This removes all its children, including all menu items, from the tab order and accessibility tree entirely. Tab navigation never silently cycles through hidden items.

Cursor styles

Element Cursor
All interactive elements (buttons, items, profile, dropdowns) pointer
Pin button when L2 panel is pinned (unpin action) w-resize, communicates "collapse left"

Font smoothing

Antialiasing is applied only on high-DPI displays (at least 2x pixel ratio / 192 dpi). On standard-density screens the browser's default subpixel rendering is preserved. This produces sharper results at lower pixel density.

Font smoothing
Condition min-resolution: 192dpi · min-device-pixel-ratio: 2
WebKit / Blink -webkit-font-smoothing: antialiased
Firefox -moz-osx-font-smoothing: grayscale