Navigation

v0.0.4
Application blocks

Sidebar

An app sidebar: the workspace, the navigation and the account.

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>
Open the workspace at the top and the account at the foot: each is a menu anchored to its button. Choose Phone: the sidebar leaves and Menu opens the same navigation in a drawer.

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-start sets its content at the start and ml-auto pushes 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 by mt-auto.
  • One column, one gap. The sidebar is a flex column; gap-sm separates its pieces and p-sm holds them off the edge. mt-auto on 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 with aria-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.