An app sidebar holds three things, top to bottom: which workspace you are in, where you can go in it, and who you are. This block fills the shell-sidebar region of an app shell with them.
Sidebar
<div class="shell bg-surface-tray fg-text-body text-sm"> <aside class="shell-sidebar gap-sm p-sm"> <button type="button" class="btn btn-quiet justify-start" popovertarget="sb-workspace"> <span class="avatar avatar-xs avatar-square" aria-hidden="true">A</span>Atlas Labs <span class="ml-auto fg-text-muted"> <svg class="icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"> <path d="m8 9 4-4 4 4M16 15l-4 4-4-4" /> </svg> </span> </button> <div class="menu" id="sb-workspace" popover> <div class="menu-label">Workspaces</div> <button type="button" class="menu-item" popovertarget="sb-workspace" popovertargetaction="hide">Atlas Labs</button> <button type="button" class="menu-item" popovertarget="sb-workspace" popovertargetaction="hide">Personal</button> <hr class="menu-separator"> <button type="button" class="menu-item" popovertarget="sb-workspace" popovertargetaction="hide"> <svg class="icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"> <path d="M12 5v14M5 12h14" /> </svg>New workspace</button> </div> <label class="input-group input-group-sm"> <svg class="icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"> <path d="M4 11a7 7 0 1 0 14 0a7 7 0 0 0-14 0M20 20l-4-4" /> </svg> <input class="input-group-field" type="search" placeholder="Search" aria-label="Search"> <kbd class="kbd">⌘</kbd> <kbd class="kbd">K</kbd> </label> <nav class="nav" aria-label="Workspace"> <a class="nav-item" href="#"> <svg class="icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"> <path d="M5 12 12 5l7 7M7 10v9h10v-9" /> </svg>Overview</a> <a class="nav-item" href="#" aria-current="page"> <svg class="icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"> <path d="M4 4h7v7H4zM13 4h7v7h-7zM4 13h7v7H4zM13 13h7v7h-7z" /> </svg>Projects <span class="nav-end">12</span> </a> <details class="nav-group" open> <summary class="nav-item nav-toggle"> <svg class="icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"> <path d="M5 14c0-5 4-10 11-10 0 7-5 11-10 11zM5 14l-2 1 1 3M10 19l-1 2-3-1M14.5 9.5h.01" /> </svg>Deployments</summary> <div class="nav-sub"> <a class="nav-item" href="#">Production</a> <a class="nav-item" href="#">Preview <span class="nav-end">3</span> </a> </div> </details> <a class="nav-item" href="#"> <svg class="icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"> <path d="M3 12a9 9 0 1 0 18 0a9 9 0 0 0-18 0M3 12h18M12 3a14 14 0 0 1 0 18M12 3a14 14 0 0 0 0 18" /> </svg>Domains</a> <a class="nav-item" href="#"> <svg class="icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"> <path d="M4 19h16M7 16V9M12 16V5M17 16v-4" /> </svg>Analytics</a> <div class="nav-label">Team</div> <a class="nav-item" href="#"> <svg class="icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"> <path d="M5 7a4 4 0 1 0 8 0a4 4 0 0 0-8 0M3 21v-2a4 4 0 0 1 4-4h4a4 4 0 0 1 4 4v2M16 3.1a4 4 0 0 1 0 7.8M21 21v-2a4 4 0 0 0-3-3.85" /> </svg>Members</a> <a class="nav-item" href="#"> <svg class="icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"> <path d="M3 8a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2v9a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2zM3 10h18" /> </svg>Billing</a> <a class="nav-item" href="#"> <svg class="icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"> <path d="M14 6a2 2 0 1 0 4 0a2 2 0 0 0-4 0M4 6h10M18 6h2M6 12a2 2 0 1 0 4 0a2 2 0 0 0-4 0M4 12h2M10 12h10M15 18a2 2 0 1 0 4 0a2 2 0 0 0-4 0M4 18h11M19 18h1" /> </svg>Settings</a> </nav> <div class="flex flex-col gap-sm mt-auto"> <section class="well flex flex-col gap-xs p-sm" aria-labelledby="sb-usage"> <div class="flex items-baseline justify-between gap-sm text-xs"> <span class="fg-text-strong" id="sb-usage">Build minutes</span> <span class="fg-text-muted">296 of 400</span> </div> <meter class="meter meter-sm" min="0" max="400" low="240" high="360" optimum="0" value="296" aria-labelledby="sb-usage">296 of 400 build minutes</meter> <a class="link text-xs self-start" href="#">Upgrade</a> </section> <button type="button" class="btn btn-quiet justify-start" popovertarget="sb-account"> <span class="avatar avatar-xs"> <img class="avatar-image" src="/demo/portrait-1.svg" alt> </span>Ada Lovelace <span class="ml-auto fg-text-muted"> <svg class="icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"> <path d="M4 12a1 1 0 1 0 2 0a1 1 0 1 0-2 0M11 12a1 1 0 1 0 2 0a1 1 0 1 0-2 0M18 12a1 1 0 1 0 2 0a1 1 0 1 0-2 0" /> </svg> </span> </button> <div class="menu menu-top" id="sb-account" popover> <a class="menu-item" href="#">Account settings</a> <a class="menu-item" href="#">Keyboard shortcuts</a> <hr class="menu-separator"> <button type="button" class="menu-item" popovertarget="sb-account" popovertargetaction="hide"> <svg class="icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"> <path d="M14 8V6a2 2 0 0 0-2-2H6a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h6a2 2 0 0 0 2-2v-2M9 12h12M18 9l3 3-3 3" /> </svg>Sign out</button> </div> </div> </aside> <div class="shell-main flex flex-col gap(8) p(8) pxs(0)@container(--3xl)"> <button type="button" class="btn btn-sm shell-toggle self-start" commandfor="sb-drawer" command="show-modal"> <svg class="icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"> <path d="M4 7h16M4 12h16M4 17h16" /> </svg>Menu</button> <main class="sheet grow(1) flex items-center justify-center p-xl"> <p class="m(0) fg-text-muted text-center max-w(320)">The sidebar is the block. The sheet beside it is where the page goes.</p> </main> </div> <dialog class="dialog dialog-drawer dialog-drawer-start" id="sb-drawer" closedby="any" aria-label="Navigation"> <div class="dialog-body flex flex-col gap-sm"> <p class="m(0) px-xs h(32) flex items-center fg-text-strong font-medium">Atlas Labs</p> <nav class="nav" aria-label="Workspace, in the drawer"> <a class="nav-item" href="#"> <svg class="icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"> <path d="M5 12 12 5l7 7M7 10v9h10v-9" /> </svg>Overview</a> <a class="nav-item" href="#" aria-current="page"> <svg class="icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"> <path d="M4 4h7v7H4zM13 4h7v7h-7zM4 13h7v7H4zM13 13h7v7h-7z" /> </svg>Projects <span class="nav-end">12</span> </a> <details class="nav-group" open> <summary class="nav-item nav-toggle"> <svg class="icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"> <path d="M5 14c0-5 4-10 11-10 0 7-5 11-10 11zM5 14l-2 1 1 3M10 19l-1 2-3-1M14.5 9.5h.01" /> </svg>Deployments</summary> <div class="nav-sub"> <a class="nav-item" href="#">Production</a> <a class="nav-item" href="#">Preview <span class="nav-end">3</span> </a> </div> </details> <a class="nav-item" href="#"> <svg class="icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"> <path d="M3 12a9 9 0 1 0 18 0a9 9 0 0 0-18 0M3 12h18M12 3a14 14 0 0 1 0 18M12 3a14 14 0 0 0 0 18" /> </svg>Domains</a> <a class="nav-item" href="#"> <svg class="icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"> <path d="M4 19h16M7 16V9M12 16V5M17 16v-4" /> </svg>Analytics</a> <div class="nav-label">Team</div> <a class="nav-item" href="#"> <svg class="icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"> <path d="M5 7a4 4 0 1 0 8 0a4 4 0 0 0-8 0M3 21v-2a4 4 0 0 1 4-4h4a4 4 0 0 1 4 4v2M16 3.1a4 4 0 0 1 0 7.8M21 21v-2a4 4 0 0 0-3-3.85" /> </svg>Members</a> <a class="nav-item" href="#"> <svg class="icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"> <path d="M3 8a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2v9a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2zM3 10h18" /> </svg>Billing</a> <a class="nav-item" href="#"> <svg class="icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"> <path d="M14 6a2 2 0 1 0 4 0a2 2 0 0 0-4 0M4 6h10M18 6h2M6 12a2 2 0 1 0 4 0a2 2 0 0 0-4 0M4 12h2M10 12h10M15 18a2 2 0 1 0 4 0a2 2 0 0 0-4 0M4 18h11M19 18h1" /> </svg>Settings</a> </nav> </div> <button type="button" class="btn btn-quiet btn-square btn-sm dialog-close" commandfor="sb-drawer" command="close" aria-label="Close navigation"> <svg class="icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"> <path d="M6 6l12 12M18 6 6 18" /> </svg> </button> </dialog></div>The block is everything inside <aside class="shell-sidebar">. The shell decides where the sidebar stands, how tall it is and when it steps aside; the block decides what is in it.
#What is in it
- The workspace is a quiet button that opens a menu.
justify-startsets its content at the start andml-autopushes the mark to the end. The letter beside the name is an avatar, hidden from assistive technology since the name says it. - Search is a small input group. The shortcut is two keycaps, one to a key; wire it to a command palette.
- The navigation is a nav with icons, a count, a group and a label. A group is a native
<details>: it opens without script. - Usage is a well with a meter. One thing worth watching, the plan's build minutes, and a link to change it.
- The account is a quiet button that opens a menu above itself,
menu-top. It is the last thing in the column, pushed down bymt-auto. - One column, one gap. The sidebar is a flex column;
gap-smseparates its pieces andp-smholds them off the edge.mt-autoon the last group sends it to the foot. - The sidebar scrolls by itself when the navigation is longer than the viewport; that comes from
shell-sidebar. - No surface of its own. It stands on the shell's tray. Its rows rise as small sheets when current, which is the nav's own doing.
#In the drawer
Below 48rem of shell the sidebar leaves the layout, and a block that only left would strand a phone. The example above keeps the way in: a button with shell-toggle, which exists only while the sidebar is away, opens a drawer with dialog-drawer-start that holds the same navigation under a second label. See the app shell for the frame. In the drawer, leave the search out: a phone has its own.
#Accessibility
- The sidebar is an
<aside>; the links are in a<nav>with a label. Mark the current page witharia-current="page". - The workspace and account buttons open menus of buttons and links. They are plain popovers: Tab moves through them and Escape closes them.
- The avatar beside the name is decoration, with an empty
alt. - The meter carries its value as text and is named by the label beside it.
- The drawer is a
<dialog>: focus moves into it, its close button, a click outside or Esc closes it, and focus returns to Menu.
#Related
- App shellA sidebar that stays while the main region scrolls, and a drawer when it is narrow.
- DashboardFigures across the top and panels in a grid that reflows without queries.
- ButtonA raised control that lifts on hover and sinks when pressed.
- MenuA list of actions in a popover, placed against its button.
- AvatarA person or a team as a photo or initials, alone or stacked.
- InputText fields cut into the surface, with labels, hints, errors and groups.
- KbdA keycap for shortcuts in text and menus.
- NavA vertical list of destinations for a sidebar.
- SurfaceTrays, sheets and wells: the material ladder every component is built on.
- MeterA measurement within a known range.
- LinkText that goes somewhere, in the ink of its sentence.
- DialogA native modal on an overlay sheet, opened and closed without script.