Sidebar
The side navigation of an app, open while people work.
Left out of Accessible first Sidebar is for apps people move around all session. Here people do this once or twice a year, and breadcrumb shows where they are. Use instead: Breadcrumb, Progress
See it live
npm run storybookWhen to use it
- 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:
- Navigation menu: Website navigation.
- Tabs: Views of one thing.
Variants
What each variant is for in any design system, then what Accessible first decided and why. Each sample is the live story with that variant set.
defaultvariantNot decided yetWhat it is for
Menu items with no border; hover shows the item.
In Accessible first
Not decided yet.
outlinevariantNot decided yetWhat it is for
Menu items with a border.
In Accessible first
Not decided yet.
defaultsizeNot decided yetWhat it is for
The normal row height.
In Accessible first
Not decided yet.
smsizeNot decided yetWhat it is for
A smaller row.
In Accessible first
Not decided yet.
lgsizeNot decided yetWhat it is for
A taller row with room for an icon and two lines.
In Accessible first
Not decided yet.
Words
- Use common words. Explain any term you must use.
- One idea per sentence. Put the action first.
- Never blame the person.
- Use: Continue, Change, Start, You. Avoid: Invalid, Utilise, Submit, Error code.
The labels components use, from the voice tokens:
content.action.commit: Continuecontent.action.cancel: Go backcontent.action.destroy: Deletecontent.action.retry: Try againcontent.action.empty-action: Start a new application
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.
Accessible first 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.
| Part | Its token and the semantic it aliases | Value |
|---|---|---|
| Wrapper | ||
| Fill Changes with theme | component.sidebar.wrapper.backgroundcolor.surface.defaultvar(--sidebar-wrapper-background) | Lightgray.light.0:#FFFFFFDarkgray.dark.50:#141414 |
| Sidebar | ||
| Fill Changes with theme | component.sidebar.backgroundcolor.surface.defaultvar(--sidebar-background) | Lightgray.light.0:#FFFFFFDarkgray.dark.50:#141414 |
| Text Changes with theme | component.sidebar.labelcolor.text.primaryvar(--sidebar-label) | Lightgray.light.900:#141414Darkgray.dark.900:#F7F7F7 |
| Padding | component.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) | Lightgray.light.0:#FFFFFFDarkgray.dark.50:#141414 |
| Corner | component.sidebar.radiusradius.controlvar(--sidebar-radius) | radius.6:0.375rem |
| Text size | component.sidebar.font-sizetype.body.font-sizevar(--sidebar-font-size) | font.size.19:1.1875rem |
| Focus ring Changes with theme | component.sidebar.focus-ringcolor.border.focusvar(--sidebar-focus-ring) | Lightblue.light.600:#0B4FC0Darkblue.dark.600:#8AB4F8 |
| Padding right | component.sidebar.padding-rightcomponent-spacing-map.x8var(--sidebar-padding-right) | space.56:3.5rem |
| Size | component.sidebar.sizesize.control.defaultvar(--sidebar-size) | size.52:3.25rem |
| Background hover 2 Changes with theme on hover | component.sidebar.background-hover-2color.surface.sunkenvar(--sidebar-background-hover-2) | Lightgray.light.100:#EDEDEDDarkgray.dark.100:#222222 |
| Label hover Changes with theme on hover | component.sidebar.label-hovercolor.text.primaryvar(--sidebar-label-hover) | Lightgray.light.900:#141414Darkgray.dark.900:#F7F7F7 |
| Background active Changes with theme | component.sidebar.background-activecolor.surface.sunkenvar(--sidebar-background-active) | Lightgray.light.100:#EDEDEDDarkgray.dark.100:#222222 |
| Label active Changes with theme | component.sidebar.label-activecolor.text.primaryvar(--sidebar-label-active) | Lightgray.light.900:#141414Darkgray.dark.900:#F7F7F7 |
| Disabled opacity disabled when disabled | component.sidebar.disabled-opacity-disabledopacity.disabledvar(--sidebar-disabled-opacity-disabled) | opacity.50:0.5 |
| Background 2 Changes with theme | component.sidebar.background-2color.surface.sunkenvar(--sidebar-background-2) | Lightgray.light.100:#EDEDEDDarkgray.dark.100:#222222 |
| Weight | component.sidebar.font-weighttype.label.font-weightvar(--sidebar-font-weight) | font.weight.700:700 |
| Focus | component.sidebar.focusborder-width.focusvar(--sidebar-focus) | border-width.3:3px |
| Background open hover Changes with theme when open, on hover | component.sidebar.background-open-hovercolor.surface.sunkenvar(--sidebar-background-open-hover) | Lightgray.light.100:#EDEDEDDarkgray.dark.100:#222222 |
| Label open hover Changes with theme when open, on hover | component.sidebar.label-open-hovercolor.text.primaryvar(--sidebar-label-open-hover) | Lightgray.light.900:#141414Darkgray.dark.900:#F7F7F7 |
| Icon size | component.sidebar.icon-sizesize.iconvar(--sidebar-icon-size) | size.20:1.25rem |
| Focus ring focus Changes with theme on focus | component.sidebar.focus-ring-focuscolor.border.focusvar(--sidebar-focus-ring-focus) | Lightblue.light.600:#0B4FC0Darkblue.dark.600:#8AB4F8 |
| Focus ring offset focus Changes with theme on focus | component.sidebar.focus-ring-offset-focuscolor.surface.canvasvar(--sidebar-focus-ring-offset-focus) | Lightgray.light.50:#F7F7F7Darkgray.dark.0:#0A0A0A |
| Height | component.sidebar.heightsize.iconvar(--sidebar-height) | size.20:1.25rem |
| Font size 2 | component.sidebar.font-size-2type.caption.font-sizevar(--sidebar-font-size-2) | font.size.16:1rem |
| Gap | ||
| Speed | component.sidebar.gap.durationmotion.duration.feedbackvar(--sidebar-gap-duration) | duration.moderate:200ms |
| Value | component.sidebar.gapspace.gap.defaultvar(--sidebar-gap) | space.16:1rem |
| Container | ||
| Speed | component.sidebar.container.durationmotion.duration.feedbackvar(--sidebar-container-duration) | duration.moderate:200ms |
| Inner | ||
| Fill Changes with theme | component.sidebar.inner.backgroundcolor.surface.defaultvar(--sidebar-inner-background) | Lightgray.light.0:#FFFFFFDarkgray.dark.50:#141414 |
| Corner | component.sidebar.inner.radiusradius.containervar(--sidebar-inner-radius) | radius.8:0.5rem |
| Border Changes with theme | component.sidebar.inner.bordercolor.border.defaultvar(--sidebar-inner-border) | Lightgray.light.200:#D6D6D6Darkgray.dark.100:#222222 |
| Shadow | component.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) | Lightgray.light.200:#D6D6D6Darkgray.dark.100:#222222 |
| Inset | ||
| Fill Changes with theme | component.sidebar.inset.backgroundcolor.surface.canvasvar(--sidebar-inset-background) | Lightgray.light.50:#F7F7F7Darkgray.dark.0:#0A0A0A |
| Corner | component.sidebar.inset.radiusradius.containervar(--sidebar-inset-radius) | radius.8:0.5rem |
| Shadow | component.sidebar.inset.shadowelevation.cardvar(--sidebar-inset-shadow) | shadow.none |
| Input | ||
| Height | component.sidebar.input.heightsize.control.defaultvar(--sidebar-input-height) | size.52:3.25rem |
| Fill Changes with theme | component.sidebar.input.backgroundcolor.surface.canvasvar(--sidebar-input-background) | Lightgray.light.50:#F7F7F7Darkgray.dark.0:#0A0A0A |
| Header | ||
| Gap | component.sidebar.header.gapspace.gap.defaultvar(--sidebar-header-gap) | space.16:1rem |
| Padding | component.sidebar.header.paddingcomponent-spacing-map.x2var(--sidebar-header-padding) | space.8:0.5rem |
| Footer | ||
| Gap | component.sidebar.footer.gapspace.gap.defaultvar(--sidebar-footer-gap) | space.16:1rem |
| Padding | component.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) | Lightgray.light.200:#D6D6D6Darkgray.dark.100:#222222 |
| Content | ||
| Gap | component.sidebar.content.gapspace.gap.defaultvar(--sidebar-content-gap) | space.16:1rem |
| Group | ||
| Padding | component.sidebar.group.paddingcomponent-spacing-map.x2var(--sidebar-group-padding) | space.8:0.5rem |
| Label height | component.sidebar.group.label.heightsize.control.defaultvar(--sidebar-group-label-height) | size.52:3.25rem |
| Label radius | component.sidebar.group.label.radiusradius.controlvar(--sidebar-group-label-radius) | radius.6:0.375rem |
| Label padding x | component.sidebar.group.label.padding-xcomponent-spacing-map.x2var(--sidebar-group-label-padding-x) | space.8:0.5rem |
| Label font size | component.sidebar.group.label.font-sizetype.caption.font-sizevar(--sidebar-group-label-font-size) | font.size.16:1rem |
| Label font weight | component.sidebar.group.label.font-weighttype.label.font-weightvar(--sidebar-group-label-font-weight) | font.weight.700:700 |
| Label label Changes with theme | component.sidebar.group.label.labelcolor.text.primaryvar(--sidebar-group-label-label) | Lightgray.light.900:#141414Darkgray.dark.900:#F7F7F7 |
| Label focus ring Changes with theme | component.sidebar.group.label.focus-ringcolor.border.focusvar(--sidebar-group-label-focus-ring) | Lightblue.light.600:#0B4FC0Darkblue.dark.600:#8AB4F8 |
| Label duration | component.sidebar.group.label.durationmotion.duration.feedbackvar(--sidebar-group-label-duration) | duration.moderate:200ms |
| Label icon size | component.sidebar.group.label.icon-sizesize.iconvar(--sidebar-group-label-icon-size) | size.20:1.25rem |
| Label focus on focus | component.sidebar.group.label.focusborder-width.focusvar(--sidebar-group-label-focus) | border-width.3:3px |
| Label focus ring focus Changes with theme on focus | component.sidebar.group.label.focus-ring-focuscolor.border.focusvar(--sidebar-group-label-focus-ring-focus) | Lightblue.light.600:#0B4FC0Darkblue.dark.600:#8AB4F8 |
| 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) | Lightgray.light.50:#F7F7F7Darkgray.dark.0:#0A0A0A |
| Action radius | component.sidebar.group.action.radiusradius.controlvar(--sidebar-group-action-radius) | radius.6:0.375rem |
| Action label Changes with theme | component.sidebar.group.action.labelcolor.text.primaryvar(--sidebar-group-action-label) | Lightgray.light.900:#141414Darkgray.dark.900:#F7F7F7 |
| Action focus ring Changes with theme | component.sidebar.group.action.focus-ringcolor.border.focusvar(--sidebar-group-action-focus-ring) | Lightblue.light.600:#0B4FC0Darkblue.dark.600:#8AB4F8 |
| Action background hover Changes with theme on hover | component.sidebar.group.action.background-hovercolor.surface.sunkenvar(--sidebar-group-action-background-hover) | Lightgray.light.100:#EDEDEDDarkgray.dark.100:#222222 |
| Action label hover Changes with theme on hover | component.sidebar.group.action.label-hovercolor.text.primaryvar(--sidebar-group-action-label-hover) | Lightgray.light.900:#141414Darkgray.dark.900:#F7F7F7 |
| Action icon size | component.sidebar.group.action.icon-sizesize.iconvar(--sidebar-group-action-icon-size) | size.20:1.25rem |
| Action focus on focus | component.sidebar.group.action.focusborder-width.focusvar(--sidebar-group-action-focus) | border-width.3:3px |
| Action focus ring focus Changes with theme on focus | component.sidebar.group.action.focus-ring-focuscolor.border.focusvar(--sidebar-group-action-focus-ring-focus) | Lightblue.light.600:#0B4FC0Darkblue.dark.600:#8AB4F8 |
| 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) | Lightgray.light.50:#F7F7F7Darkgray.dark.0:#0A0A0A |
| Content font size | component.sidebar.group.content.font-sizetype.body.font-sizevar(--sidebar-group-content-font-size) | font.size.19:1.1875rem |
| Menu | ||
| Gap | component.sidebar.menu.gapspace.gap.tightvar(--sidebar-menu-gap) | space.12:0.75rem |
| Action radius | component.sidebar.menu.action.radiusradius.controlvar(--sidebar-menu-action-radius) | radius.6:0.375rem |
| Action label Changes with theme | component.sidebar.menu.action.labelcolor.text.primaryvar(--sidebar-menu-action-label) | Lightgray.light.900:#141414Darkgray.dark.900:#F7F7F7 |
| Action focus ring Changes with theme | component.sidebar.menu.action.focus-ringcolor.border.focusvar(--sidebar-menu-action-focus-ring) | Lightblue.light.600:#0B4FC0Darkblue.dark.600:#8AB4F8 |
| Action background hover Changes with theme on hover | component.sidebar.menu.action.background-hovercolor.surface.sunkenvar(--sidebar-menu-action-background-hover) | Lightgray.light.100:#EDEDEDDarkgray.dark.100:#222222 |
| Action label hover Changes with theme on hover | component.sidebar.menu.action.label-hovercolor.text.primaryvar(--sidebar-menu-action-label-hover) | Lightgray.light.900:#141414Darkgray.dark.900:#F7F7F7 |
| Action icon size | component.sidebar.menu.action.icon-sizesize.iconvar(--sidebar-menu-action-icon-size) | size.20:1.25rem |
| Action focus on focus | component.sidebar.menu.action.focusborder-width.focusvar(--sidebar-menu-action-focus) | border-width.3:3px |
| Action focus ring focus Changes with theme on focus | component.sidebar.menu.action.focus-ring-focuscolor.border.focusvar(--sidebar-menu-action-focus-ring-focus) | Lightblue.light.600:#0B4FC0Darkblue.dark.600:#8AB4F8 |
| 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) | Lightgray.light.50:#F7F7F7Darkgray.dark.0:#0A0A0A |
| Badge radius | component.sidebar.menu.badge.radiusradius.controlvar(--sidebar-menu-badge-radius) | radius.6:0.375rem |
| Badge padding x | component.sidebar.menu.badge.padding-xcomponent-spacing-map.x1var(--sidebar-menu-badge-padding-x) | space.4:0.25rem |
| Badge font size | component.sidebar.menu.badge.font-sizetype.caption.font-sizevar(--sidebar-menu-badge-font-size) | font.size.16:1rem |
| Badge font weight | component.sidebar.menu.badge.font-weighttype.label.font-weightvar(--sidebar-menu-badge-font-weight) | font.weight.700:700 |
| Badge label Changes with theme | component.sidebar.menu.badge.labelcolor.text.primaryvar(--sidebar-menu-badge-label) | Lightgray.light.900:#141414Darkgray.dark.900:#F7F7F7 |
| Skeleton height | component.sidebar.menu.skeleton.heightsize.control.defaultvar(--sidebar-menu-skeleton-height) | size.52:3.25rem |
| Skeleton gap | component.sidebar.menu.skeleton.gapspace.gap.defaultvar(--sidebar-menu-skeleton-gap) | space.16:1rem |
| Skeleton radius | component.sidebar.menu.skeleton.radiusradius.controlvar(--sidebar-menu-skeleton-radius) | radius.6:0.375rem |
| Skeleton padding x | component.sidebar.menu.skeleton.padding-xcomponent-spacing-map.x2var(--sidebar-menu-skeleton-padding-x) | space.8:0.5rem |
| Sub gap | component.sidebar.menu.sub.gapspace.gap.tightvar(--sidebar-menu-sub-gap) | space.12:0.75rem |
| Sub border Changes with theme | component.sidebar.menu.sub.bordercolor.border.defaultvar(--sidebar-menu-sub-border) | Lightgray.light.200:#D6D6D6Darkgray.dark.100:#222222 |
| Sub padding x | component.sidebar.menu.sub.padding-xcomponent-spacing-map.x3var(--sidebar-menu-sub-padding-x) | space.20:1.25rem |
| Sub padding y | component.sidebar.menu.sub.padding-ycomponent-spacing-map.hairlinevar(--sidebar-menu-sub-padding-y) | space.2:0.125rem |
| Sub button gap | component.sidebar.menu.sub.button.gapspace.gap.defaultvar(--sidebar-menu-sub-button-gap) | space.16:1rem |
| Sub button radius | component.sidebar.menu.sub.button.radiusradius.controlvar(--sidebar-menu-sub-button-radius) | radius.6:0.375rem |
| Sub button padding x | component.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) | Lightgray.light.900:#141414Darkgray.dark.900:#F7F7F7 |
| Sub button focus ring Changes with theme | component.sidebar.menu.sub.button.focus-ringcolor.border.focusvar(--sidebar-menu-sub-button-focus-ring) | Lightblue.light.600:#0B4FC0Darkblue.dark.600:#8AB4F8 |
| Sub button background hover Changes with theme on hover | component.sidebar.menu.sub.button.background-hovercolor.surface.sunkenvar(--sidebar-menu-sub-button-background-hover) | Lightgray.light.100:#EDEDEDDarkgray.dark.100:#222222 |
| Sub button label hover Changes with theme on hover | component.sidebar.menu.sub.button.label-hovercolor.text.primaryvar(--sidebar-menu-sub-button-label-hover) | Lightgray.light.900:#141414Darkgray.dark.900:#F7F7F7 |
| Sub button background active Changes with theme | component.sidebar.menu.sub.button.background-activecolor.surface.sunkenvar(--sidebar-menu-sub-button-background-active) | Lightgray.light.100:#EDEDEDDarkgray.dark.100:#222222 |
| Sub button label active Changes with theme | component.sidebar.menu.sub.button.label-activecolor.text.primaryvar(--sidebar-menu-sub-button-label-active) | Lightgray.light.900:#141414Darkgray.dark.900:#F7F7F7 |
| Sub button disabled opacity disabled when disabled | component.sidebar.menu.sub.button.disabled-opacity-disabledopacity.disabledvar(--sidebar-menu-sub-button-disabled-opacity-disabled) | opacity.50:0.5 |
| Sub button icon size | component.sidebar.menu.sub.button.icon-sizesize.iconvar(--sidebar-menu-sub-button-icon-size) | size.20:1.25rem |
| Sub button focus on focus | component.sidebar.menu.sub.button.focusborder-width.focusvar(--sidebar-menu-sub-button-focus) | border-width.3:3px |
| 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) | Lightblue.light.600:#0B4FC0Darkblue.dark.600:#8AB4F8 |
| 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) | Lightgray.light.50:#F7F7F7Darkgray.dark.0:#0A0A0A |
| Default | ||
| Background hover Changes with theme | component.sidebar.default.background-hovercolor.surface.sunkenvar(--sidebar-default-background-hover) | Lightgray.light.100:#EDEDEDDarkgray.dark.100:#222222 |
| Label hover Changes with theme on hover | component.sidebar.default.label-hovercolor.text.primaryvar(--sidebar-default-label-hover) | Lightgray.light.900:#141414Darkgray.dark.900:#F7F7F7 |
| Height | component.sidebar.default.heightsize.control.defaultvar(--sidebar-default-height) | size.52:3.25rem |
| Text size | component.sidebar.default.font-sizetype.body.font-sizevar(--sidebar-default-font-size) | font.size.19:1.1875rem |
| Outline | ||
| Fill Changes with theme | component.sidebar.outline.backgroundcolor.surface.canvasvar(--sidebar-outline-background) | Lightgray.light.50:#F7F7F7Darkgray.dark.0:#0A0A0A |
| Background hover Changes with theme on hover | component.sidebar.outline.background-hovercolor.surface.sunkenvar(--sidebar-outline-background-hover) | Lightgray.light.100:#EDEDEDDarkgray.dark.100:#222222 |
| Label hover Changes with theme on hover | component.sidebar.outline.label-hovercolor.text.primaryvar(--sidebar-outline-label-hover) | Lightgray.light.900:#141414Darkgray.dark.900:#F7F7F7 |
| Sm | ||
| Text size | component.sidebar.sm.font-sizetype.caption.font-sizevar(--sidebar-sm-font-size) | font.size.16:1rem |
| Lg | ||
| Text size | component.sidebar.lg.font-sizetype.body.font-sizevar(--sidebar-lg-font-size) | font.size.19:1.1875rem |
| From the system | ||
| Edge Changes with theme | color.border.default straight from the system, through --border | Lightgray.light.200 · #D6D6D6Darkgray.dark.100 · #222222 |