Skip to main content

Sidebar

The side navigation of an app, open while people work.

In Dense data

See it live​

Start Storybook to see it live: npm run storybook

When to use it​

In Dense data: App navigation that stays open all shift.

  • Use it when: Apps with several sections people move between all session.
  • Not when: A website's main navigation (navigation menu), or a few sections that fit in tabs.

Instead:

Variants​

What each variant is for in any design system, then what Dense data decided and why. Each sample is the live story with that variant set.

defaultvariantUsed in Dense data
Start Storybook to see it live

What it is for

Menu items with no border; hover shows the item.

✓Most apps: quiet next to the content.
✕Items that need extra separation.

In Dense data

Menu items with no border, quiet beside dense data.

outlinevariantNot used in Dense data
Start Storybook to see it live

What it is for

Menu items with a border.

✓Items that need extra separation from a busy background.
✕A quiet sidebar next to dense content.

In Dense data

Borders on every item would compete with the data.

defaultsizeUsed in Dense data
Start Storybook to see it live

What it is for

The normal row height.

✓Most menu items.
✕Very long lists.

In Dense data

Every section of the app.

smsizeUsed in Dense data
Start Storybook to see it live

What it is for

A smaller row.

✓Long or secondary lists.
✕Touch screens.

In Dense data

Long secondary lists, at the denser size.

lgsizeNot used in Dense data
Start Storybook to see it live

What it is for

A taller row with room for an icon and two lines.

✓A workspace or account switcher at the top.
✕Ordinary menu items.

In Dense data

Rows stay small so the whole navigation fits the screen all shift.

Why it works this way​

Sidebar looks the way it does because of 4 answers about this product. Each card is one answer, in three steps: the answer, what it decided for Sidebar, and what would move if it changed.

What is the main content?content.q1
1The answer
Numbers and records

Most of the screen is numbers and records.

2So Sidebar
  • ✓uses default: Menu items with no border, quiet beside dense data.
  • –does not use outline: Borders on every item would compete with the data.
It also shapes Badge: neutral Badge: info Badge: success Badge: warning Badge: danger Empty: default Item: default Item: outline Toggle: default Toggle: outline
3If it changed
4
Sidebar decisions would reopen

and 44 answers that follow from it: A lot, Scan and compare, People keep their place, and 41 more.

How often do people repeat actions?tasks.q2
1The answer
Constantly

The same actions repeat many times an hour.

2So Sidebar
  • ✓uses default: Every section of the app.
It also shapes Button: ghost Input group: inline-end Alert: default Toast: success Toggle: default
3If it changed
1
Sidebar decision would reopen

and 11 answers that follow from it: Time on task, All day, Three, and 8 more.

How many densities exist?spacing.count
1The answer
Two

A second, denser size serves tables and toolbars.

2So Sidebar
  • ✓uses sm: Long secondary lists, at the denser size.
It also shapes Field: horizontal Item: default Item: sm
3If it changed
1
Sidebar decision would reopen

No other answer follows from it.

How much is on one screen?content.q2
1The answer
A lot

Experts want many records on one screen.

2So Sidebar
  • –does not use lg: Rows stay small so the whole navigation fits the screen all shift.
It also shapes Button: default Button: compact Button: large Input group: block-start Input group: block-end Field: horizontal Toggle: default Toggle: sm Toggle: lg
3If it changed
1
Sidebar decision would reopen

and 14 answers that follow from it: Quiet, Only the steps the screens need, Small, and 11 more.

Words​

  • Lead with the object, then the state: "Alert 2041 contained."
  • Use numbers, not words for numbers.
  • One verb per button. No "please", no exclamation marks.
  • Use: Investigate, Filter, Assign, Escalated, Resolved. Avoid: Oops, Awesome, Please, Simply.

The labels components use, from the voice tokens:

  • content.action.commit: Save
  • content.action.cancel: Cancel
  • content.action.destroy: Delete
  • content.action.retry: Retry
  • content.action.empty-action: Change filters

See Voice and tone for the whole voice.

Accessibility and keyboard​

Test before you ship:

  • It is a navigation landmark; the current section is marked; collapsing keeps names for icons. undefined

Use it in code​

npx open-ds add sidebar

The component reads only component keys and semantic tokens, so it follows the brand and mode on <html>.

Tokens it reads​

Sidebar reads 113 tokens of its own, each an alias of a semantic token. Each row is one part: its own token, the semantic it aliases, and the value it ends at. Change a semantic and every component that reads it follows; override one of Sidebar's tokens to change Sidebar alone.

Changes with theme

Dense data decides what changes when you switch theme. Here a switch changes colors only, so the marked rows differ between light and dark; everything else holds steady.

PartIts token and the semantic it aliasesValue
Wrapper
Fill
Changes with theme
component.sidebar.wrapper.backgroundcolor.surface.defaultvar(--sidebar-wrapper-background)Lightslate.light.0:#FFFFFFDarkslate.dark.50:#161B23
Sidebar
Fill
Changes with theme
component.sidebar.backgroundcolor.surface.defaultvar(--sidebar-background)Lightslate.light.0:#FFFFFFDarkslate.dark.50:#161B23
Text
Changes with theme
component.sidebar.labelcolor.text.primaryvar(--sidebar-label)Lightslate.light.900:#161B23Darkslate.dark.900:#F7F8FA
Paddingcomponent.sidebar.paddingcomponent-spacing-map.x2var(--sidebar-padding)space.8:0.5rem
Background hover
Changes with theme
on hover
component.sidebar.background-hovercolor.surface.defaultvar(--sidebar-background-hover)Lightslate.light.0:#FFFFFFDarkslate.dark.50:#161B23
Cornercomponent.sidebar.radiusradius.controlvar(--sidebar-radius)radius.2:0.125rem
Text sizecomponent.sidebar.font-sizetype.body.font-sizevar(--sidebar-font-size)font.size.14:0.875rem
Focus ring
Changes with theme
component.sidebar.focus-ringcolor.border.focusvar(--sidebar-focus-ring)Lightviolet.light.600:#4B3FD9Darkviolet.dark.600:#A9A3F6
Padding rightcomponent.sidebar.padding-rightcomponent-spacing-map.x8var(--sidebar-padding-right)space.32:2rem
Sizecomponent.sidebar.sizesize.control.defaultvar(--sidebar-size)size.32:2rem
Background hover 2
Changes with theme
on hover
component.sidebar.background-hover-2color.surface.sunkenvar(--sidebar-background-hover-2)Lightslate.light.100:#EEF1F5Darkslate.dark.100:#262D38
Label hover
Changes with theme
on hover
component.sidebar.label-hovercolor.text.primaryvar(--sidebar-label-hover)Lightslate.light.900:#161B23Darkslate.dark.900:#F7F8FA
Background active
Changes with theme
component.sidebar.background-activecolor.surface.sunkenvar(--sidebar-background-active)Lightslate.light.100:#EEF1F5Darkslate.dark.100:#262D38
Label active
Changes with theme
component.sidebar.label-activecolor.text.primaryvar(--sidebar-label-active)Lightslate.light.900:#161B23Darkslate.dark.900:#F7F8FA
Disabled opacity disabled
when disabled
component.sidebar.disabled-opacity-disabledopacity.disabledvar(--sidebar-disabled-opacity-disabled)opacity.40:0.4
Background 2
Changes with theme
component.sidebar.background-2color.surface.sunkenvar(--sidebar-background-2)Lightslate.light.100:#EEF1F5Darkslate.dark.100:#262D38
Weightcomponent.sidebar.font-weighttype.label.font-weightvar(--sidebar-font-weight)font.weight.500:500
Focuscomponent.sidebar.focusborder-width.focusvar(--sidebar-focus)border-width.2:2px
Background open hover
Changes with theme
when open, on hover
component.sidebar.background-open-hovercolor.surface.sunkenvar(--sidebar-background-open-hover)Lightslate.light.100:#EEF1F5Darkslate.dark.100:#262D38
Label open hover
Changes with theme
when open, on hover
component.sidebar.label-open-hovercolor.text.primaryvar(--sidebar-label-open-hover)Lightslate.light.900:#161B23Darkslate.dark.900:#F7F8FA
Icon sizecomponent.sidebar.icon-sizesize.iconvar(--sidebar-icon-size)size.16:1rem
Focus ring focus
Changes with theme
on focus
component.sidebar.focus-ring-focuscolor.border.focusvar(--sidebar-focus-ring-focus)Lightviolet.light.600:#4B3FD9Darkviolet.dark.600:#A9A3F6
Focus ring offset focus
Changes with theme
on focus
component.sidebar.focus-ring-offset-focuscolor.surface.canvasvar(--sidebar-focus-ring-offset-focus)Lightslate.light.50:#F7F8FADarkslate.dark.0:#0D1117
Heightcomponent.sidebar.heightsize.iconvar(--sidebar-height)size.16:1rem
Font size 2component.sidebar.font-size-2type.caption.font-sizevar(--sidebar-font-size-2)font.size.12:0.75rem
Gap
Speedcomponent.sidebar.gap.durationmotion.duration.feedbackvar(--sidebar-gap-duration)duration.fast:120ms
Valuecomponent.sidebar.gapspace.gap.defaultvar(--sidebar-gap)space.8:0.5rem
Container
Speedcomponent.sidebar.container.durationmotion.duration.feedbackvar(--sidebar-container-duration)duration.fast:120ms
Inner
Fill
Changes with theme
component.sidebar.inner.backgroundcolor.surface.defaultvar(--sidebar-inner-background)Lightslate.light.0:#FFFFFFDarkslate.dark.50:#161B23
Cornercomponent.sidebar.inner.radiusradius.containervar(--sidebar-inner-radius)radius.4:0.25rem
Border
Changes with theme
component.sidebar.inner.bordercolor.border.defaultvar(--sidebar-inner-border)Lightslate.light.200:#DDE2EADarkslate.dark.100:#262D38
Shadowcomponent.sidebar.inner.shadowelevation.cardvar(--sidebar-inner-shadow)shadow.none
Rail
Background hover
Changes with theme
on hover
component.sidebar.rail.background-hovercolor.border.defaultvar(--sidebar-rail-background-hover)Lightslate.light.200:#DDE2EADarkslate.dark.100:#262D38
Inset
Fill
Changes with theme
component.sidebar.inset.backgroundcolor.surface.canvasvar(--sidebar-inset-background)Lightslate.light.50:#F7F8FADarkslate.dark.0:#0D1117
Cornercomponent.sidebar.inset.radiusradius.containervar(--sidebar-inset-radius)radius.4:0.25rem
Shadowcomponent.sidebar.inset.shadowelevation.cardvar(--sidebar-inset-shadow)shadow.none
Input
Heightcomponent.sidebar.input.heightsize.control.defaultvar(--sidebar-input-height)size.32:2rem
Fill
Changes with theme
component.sidebar.input.backgroundcolor.surface.canvasvar(--sidebar-input-background)Lightslate.light.50:#F7F8FADarkslate.dark.0:#0D1117
Header
Gapcomponent.sidebar.header.gapspace.gap.defaultvar(--sidebar-header-gap)space.8:0.5rem
Paddingcomponent.sidebar.header.paddingcomponent-spacing-map.x2var(--sidebar-header-padding)space.8:0.5rem
Footer
Gapcomponent.sidebar.footer.gapspace.gap.defaultvar(--sidebar-footer-gap)space.8:0.5rem
Paddingcomponent.sidebar.footer.paddingcomponent-spacing-map.x2var(--sidebar-footer-padding)space.8:0.5rem
Separator
Fill
Changes with theme
component.sidebar.separator.backgroundcolor.border.defaultvar(--sidebar-separator-background)Lightslate.light.200:#DDE2EADarkslate.dark.100:#262D38
Content
Gapcomponent.sidebar.content.gapspace.gap.defaultvar(--sidebar-content-gap)space.8:0.5rem
Group
Paddingcomponent.sidebar.group.paddingcomponent-spacing-map.x2var(--sidebar-group-padding)space.8:0.5rem
Label heightcomponent.sidebar.group.label.heightsize.control.defaultvar(--sidebar-group-label-height)size.32:2rem
Label radiuscomponent.sidebar.group.label.radiusradius.controlvar(--sidebar-group-label-radius)radius.2:0.125rem
Label padding xcomponent.sidebar.group.label.padding-xcomponent-spacing-map.x2var(--sidebar-group-label-padding-x)space.8:0.5rem
Label font sizecomponent.sidebar.group.label.font-sizetype.caption.font-sizevar(--sidebar-group-label-font-size)font.size.12:0.75rem
Label font weightcomponent.sidebar.group.label.font-weighttype.label.font-weightvar(--sidebar-group-label-font-weight)font.weight.500:500
Label label
Changes with theme
component.sidebar.group.label.labelcolor.text.primaryvar(--sidebar-group-label-label)Lightslate.light.900:#161B23Darkslate.dark.900:#F7F8FA
Label focus ring
Changes with theme
component.sidebar.group.label.focus-ringcolor.border.focusvar(--sidebar-group-label-focus-ring)Lightviolet.light.600:#4B3FD9Darkviolet.dark.600:#A9A3F6
Label durationcomponent.sidebar.group.label.durationmotion.duration.feedbackvar(--sidebar-group-label-duration)duration.fast:120ms
Label icon sizecomponent.sidebar.group.label.icon-sizesize.iconvar(--sidebar-group-label-icon-size)size.16:1rem
Label focus
on focus
component.sidebar.group.label.focusborder-width.focusvar(--sidebar-group-label-focus)border-width.2:2px
Label focus ring focus
Changes with theme
on focus
component.sidebar.group.label.focus-ring-focuscolor.border.focusvar(--sidebar-group-label-focus-ring-focus)Lightviolet.light.600:#4B3FD9Darkviolet.dark.600:#A9A3F6
Label focus ring offset focus
Changes with theme
on focus
component.sidebar.group.label.focus-ring-offset-focuscolor.surface.canvasvar(--sidebar-group-label-focus-ring-offset-focus)Lightslate.light.50:#F7F8FADarkslate.dark.0:#0D1117
Action radiuscomponent.sidebar.group.action.radiusradius.controlvar(--sidebar-group-action-radius)radius.2:0.125rem
Action label
Changes with theme
component.sidebar.group.action.labelcolor.text.primaryvar(--sidebar-group-action-label)Lightslate.light.900:#161B23Darkslate.dark.900:#F7F8FA
Action focus ring
Changes with theme
component.sidebar.group.action.focus-ringcolor.border.focusvar(--sidebar-group-action-focus-ring)Lightviolet.light.600:#4B3FD9Darkviolet.dark.600:#A9A3F6
Action background hover
Changes with theme
on hover
component.sidebar.group.action.background-hovercolor.surface.sunkenvar(--sidebar-group-action-background-hover)Lightslate.light.100:#EEF1F5Darkslate.dark.100:#262D38
Action label hover
Changes with theme
on hover
component.sidebar.group.action.label-hovercolor.text.primaryvar(--sidebar-group-action-label-hover)Lightslate.light.900:#161B23Darkslate.dark.900:#F7F8FA
Action icon sizecomponent.sidebar.group.action.icon-sizesize.iconvar(--sidebar-group-action-icon-size)size.16:1rem
Action focus
on focus
component.sidebar.group.action.focusborder-width.focusvar(--sidebar-group-action-focus)border-width.2:2px
Action focus ring focus
Changes with theme
on focus
component.sidebar.group.action.focus-ring-focuscolor.border.focusvar(--sidebar-group-action-focus-ring-focus)Lightviolet.light.600:#4B3FD9Darkviolet.dark.600:#A9A3F6
Action focus ring offset focus
Changes with theme
on focus
component.sidebar.group.action.focus-ring-offset-focuscolor.surface.canvasvar(--sidebar-group-action-focus-ring-offset-focus)Lightslate.light.50:#F7F8FADarkslate.dark.0:#0D1117
Content font sizecomponent.sidebar.group.content.font-sizetype.body.font-sizevar(--sidebar-group-content-font-size)font.size.14:0.875rem
Menu
Gapcomponent.sidebar.menu.gapspace.gap.tightvar(--sidebar-menu-gap)space.4:0.25rem
Action radiuscomponent.sidebar.menu.action.radiusradius.controlvar(--sidebar-menu-action-radius)radius.2:0.125rem
Action label
Changes with theme
component.sidebar.menu.action.labelcolor.text.primaryvar(--sidebar-menu-action-label)Lightslate.light.900:#161B23Darkslate.dark.900:#F7F8FA
Action focus ring
Changes with theme
component.sidebar.menu.action.focus-ringcolor.border.focusvar(--sidebar-menu-action-focus-ring)Lightviolet.light.600:#4B3FD9Darkviolet.dark.600:#A9A3F6
Action background hover
Changes with theme
on hover
component.sidebar.menu.action.background-hovercolor.surface.sunkenvar(--sidebar-menu-action-background-hover)Lightslate.light.100:#EEF1F5Darkslate.dark.100:#262D38
Action label hover
Changes with theme
on hover
component.sidebar.menu.action.label-hovercolor.text.primaryvar(--sidebar-menu-action-label-hover)Lightslate.light.900:#161B23Darkslate.dark.900:#F7F8FA
Action icon sizecomponent.sidebar.menu.action.icon-sizesize.iconvar(--sidebar-menu-action-icon-size)size.16:1rem
Action focus
on focus
component.sidebar.menu.action.focusborder-width.focusvar(--sidebar-menu-action-focus)border-width.2:2px
Action focus ring focus
Changes with theme
on focus
component.sidebar.menu.action.focus-ring-focuscolor.border.focusvar(--sidebar-menu-action-focus-ring-focus)Lightviolet.light.600:#4B3FD9Darkviolet.dark.600:#A9A3F6
Action focus ring offset focus
Changes with theme
on focus
component.sidebar.menu.action.focus-ring-offset-focuscolor.surface.canvasvar(--sidebar-menu-action-focus-ring-offset-focus)Lightslate.light.50:#F7F8FADarkslate.dark.0:#0D1117
Badge radiuscomponent.sidebar.menu.badge.radiusradius.controlvar(--sidebar-menu-badge-radius)radius.2:0.125rem
Badge padding xcomponent.sidebar.menu.badge.padding-xcomponent-spacing-map.x1var(--sidebar-menu-badge-padding-x)space.4:0.25rem
Badge font sizecomponent.sidebar.menu.badge.font-sizetype.caption.font-sizevar(--sidebar-menu-badge-font-size)font.size.12:0.75rem
Badge font weightcomponent.sidebar.menu.badge.font-weighttype.label.font-weightvar(--sidebar-menu-badge-font-weight)font.weight.500:500
Badge label
Changes with theme
component.sidebar.menu.badge.labelcolor.text.primaryvar(--sidebar-menu-badge-label)Lightslate.light.900:#161B23Darkslate.dark.900:#F7F8FA
Skeleton heightcomponent.sidebar.menu.skeleton.heightsize.control.defaultvar(--sidebar-menu-skeleton-height)size.32:2rem
Skeleton gapcomponent.sidebar.menu.skeleton.gapspace.gap.defaultvar(--sidebar-menu-skeleton-gap)space.8:0.5rem
Skeleton radiuscomponent.sidebar.menu.skeleton.radiusradius.controlvar(--sidebar-menu-skeleton-radius)radius.2:0.125rem
Skeleton padding xcomponent.sidebar.menu.skeleton.padding-xcomponent-spacing-map.x2var(--sidebar-menu-skeleton-padding-x)space.8:0.5rem
Sub gapcomponent.sidebar.menu.sub.gapspace.gap.tightvar(--sidebar-menu-sub-gap)space.4:0.25rem
Sub border
Changes with theme
component.sidebar.menu.sub.bordercolor.border.defaultvar(--sidebar-menu-sub-border)Lightslate.light.200:#DDE2EADarkslate.dark.100:#262D38
Sub padding xcomponent.sidebar.menu.sub.padding-xcomponent-spacing-map.x3var(--sidebar-menu-sub-padding-x)space.12:0.75rem
Sub padding ycomponent.sidebar.menu.sub.padding-ycomponent-spacing-map.hairlinevar(--sidebar-menu-sub-padding-y)space.2:0.125rem
Sub button gapcomponent.sidebar.menu.sub.button.gapspace.gap.defaultvar(--sidebar-menu-sub-button-gap)space.8:0.5rem
Sub button radiuscomponent.sidebar.menu.sub.button.radiusradius.controlvar(--sidebar-menu-sub-button-radius)radius.2:0.125rem
Sub button padding xcomponent.sidebar.menu.sub.button.padding-xcomponent-spacing-map.x2var(--sidebar-menu-sub-button-padding-x)space.8:0.5rem
Sub button label
Changes with theme
component.sidebar.menu.sub.button.labelcolor.text.primaryvar(--sidebar-menu-sub-button-label)Lightslate.light.900:#161B23Darkslate.dark.900:#F7F8FA
Sub button focus ring
Changes with theme
component.sidebar.menu.sub.button.focus-ringcolor.border.focusvar(--sidebar-menu-sub-button-focus-ring)Lightviolet.light.600:#4B3FD9Darkviolet.dark.600:#A9A3F6
Sub button background hover
Changes with theme
on hover
component.sidebar.menu.sub.button.background-hovercolor.surface.sunkenvar(--sidebar-menu-sub-button-background-hover)Lightslate.light.100:#EEF1F5Darkslate.dark.100:#262D38
Sub button label hover
Changes with theme
on hover
component.sidebar.menu.sub.button.label-hovercolor.text.primaryvar(--sidebar-menu-sub-button-label-hover)Lightslate.light.900:#161B23Darkslate.dark.900:#F7F8FA
Sub button background active
Changes with theme
component.sidebar.menu.sub.button.background-activecolor.surface.sunkenvar(--sidebar-menu-sub-button-background-active)Lightslate.light.100:#EEF1F5Darkslate.dark.100:#262D38
Sub button label active
Changes with theme
component.sidebar.menu.sub.button.label-activecolor.text.primaryvar(--sidebar-menu-sub-button-label-active)Lightslate.light.900:#161B23Darkslate.dark.900:#F7F8FA
Sub button disabled opacity disabled
when disabled
component.sidebar.menu.sub.button.disabled-opacity-disabledopacity.disabledvar(--sidebar-menu-sub-button-disabled-opacity-disabled)opacity.40:0.4
Sub button icon sizecomponent.sidebar.menu.sub.button.icon-sizesize.iconvar(--sidebar-menu-sub-button-icon-size)size.16:1rem
Sub button focus
on focus
component.sidebar.menu.sub.button.focusborder-width.focusvar(--sidebar-menu-sub-button-focus)border-width.2:2px
Sub button focus ring focus
Changes with theme
on focus
component.sidebar.menu.sub.button.focus-ring-focuscolor.border.focusvar(--sidebar-menu-sub-button-focus-ring-focus)Lightviolet.light.600:#4B3FD9Darkviolet.dark.600:#A9A3F6
Sub button focus ring offset focus
Changes with theme
on focus
component.sidebar.menu.sub.button.focus-ring-offset-focuscolor.surface.canvasvar(--sidebar-menu-sub-button-focus-ring-offset-focus)Lightslate.light.50:#F7F8FADarkslate.dark.0:#0D1117
Default
Background hover
Changes with theme
component.sidebar.default.background-hovercolor.surface.sunkenvar(--sidebar-default-background-hover)Lightslate.light.100:#EEF1F5Darkslate.dark.100:#262D38
Label hover
Changes with theme
on hover
component.sidebar.default.label-hovercolor.text.primaryvar(--sidebar-default-label-hover)Lightslate.light.900:#161B23Darkslate.dark.900:#F7F8FA
Heightcomponent.sidebar.default.heightsize.control.defaultvar(--sidebar-default-height)size.32:2rem
Text sizecomponent.sidebar.default.font-sizetype.body.font-sizevar(--sidebar-default-font-size)font.size.14:0.875rem
Outline
Fill
Changes with theme
component.sidebar.outline.backgroundcolor.surface.canvasvar(--sidebar-outline-background)Lightslate.light.50:#F7F8FADarkslate.dark.0:#0D1117
Background hover
Changes with theme
on hover
component.sidebar.outline.background-hovercolor.surface.sunkenvar(--sidebar-outline-background-hover)Lightslate.light.100:#EEF1F5Darkslate.dark.100:#262D38
Label hover
Changes with theme
on hover
component.sidebar.outline.label-hovercolor.text.primaryvar(--sidebar-outline-label-hover)Lightslate.light.900:#161B23Darkslate.dark.900:#F7F8FA
Sm
Text sizecomponent.sidebar.sm.font-sizetype.caption.font-sizevar(--sidebar-sm-font-size)font.size.12:0.75rem
Lg
Text sizecomponent.sidebar.lg.font-sizetype.body.font-sizevar(--sidebar-lg-font-size)font.size.14:0.875rem
From the system
Edge
Changes with theme
color.border.default
straight from the system, through --border
Lightslate.light.200 · #DDE2EADarkslate.dark.100 · #262D38