v1.3.0
  1. ds
  2. Agent
  3. Activity rail

Activity rail

The 44px icon strip at the edge of a workbench: areas, toggles, badges and a 2px current bar.

Light
sidebar
Dark
sidebar
390px, dark
sidebar
Code
<nav class="rail" aria-label="Areas">
  <div class="rail-top">
    <button class="rail-item" type="button" aria-label="Sessions" aria-current="true" data-tip="Sessions  Ctrl 1" data-tip-pos="right"><svg class="i" viewBox="0 0 24 24" aria-hidden="true"><path d="M22 17a2 2 0 0 1-2 2H6.828a2 2 0 0 0-1.414.586l-2.202 2.202A.71.71 0 0 1 2 21.286V5a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2z"/></svg></button>
    <button class="rail-item" type="button" aria-label="Files" data-tip="Files  Ctrl 2" data-tip-pos="right"><svg class="i" viewBox="0 0 24 24" aria-hidden="true"><path d="M20 20a2 2 0 0 0 2-2V8a2 2 0 0 0-2-2h-7.9a2 2 0 0 1-1.69-.9L9.6 3.9A2 2 0 0 0 7.93 3H4a2 2 0 0 0-2 2v13a2 2 0 0 0 2 2Z"/></svg></button>
    <button class="rail-item" type="button" aria-label="Search" data-tip="Search  Ctrl Shift F" data-tip-pos="right"><svg class="i" viewBox="0 0 24 24" aria-hidden="true"><path d="m21 21-4.34-4.34"/><circle cx="11" cy="11" r="8"/></svg></button>
    <button class="rail-item" type="button" aria-label="Agents, 2 running" data-tip="Agents" data-tip-pos="right"><svg class="i" viewBox="0 0 24 24" aria-hidden="true"><path d="M12 8V4H8"/><rect width="16" height="12" x="4" y="8" rx="2"/><path d="M2 14h2"/><path d="M20 14h2"/><path d="M15 13v2"/><path d="M9 13v2"/></svg><span class="rail-badge" aria-hidden="true">2</span></button>
    <button class="rail-item" type="button" aria-label="Problems, 3 errors" data-tip="Problems" data-tip-pos="right"><svg class="i" viewBox="0 0 24 24" aria-hidden="true"><path d="m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3"/><path d="M12 9v4"/><path d="M12 17h.01"/></svg><span class="rail-badge down" aria-hidden="true">3</span></button>
  </div>
  <span class="rail-spacer"></span>
  <button class="rail-item" type="button" aria-label="Sidebar" aria-pressed="true" data-tip="Hide the sidebar  Ctrl B" data-tip-pos="right"><svg class="i" viewBox="0 0 24 24" aria-hidden="true"><rect width="18" height="18" x="3" y="3" rx="2"/><path d="M9 3v18"/></svg></button>
  <button class="rail-item" type="button" aria-label="Settings" data-tip="Settings  Ctrl ," data-tip-pos="right"><svg class="i" viewBox="0 0 24 24" aria-hidden="true"><path d="M21 16V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16z"/><circle cx="12" cy="12" r="4"/></svg></button>
</nav>

The rail is the strip at the far left of a workbench: one 32px icon button per area, on a 44px column that never hides. The current area has a 2px accent bar on the outer edge and full-contrast ink; the rest are muted until hovered. Because the rail is always there, whatever it toggles can always come back: pressing the current area, or the sidebar toggle, shows or hides the sidebar.

Every item has an aria-label and a tooltip on the right. Put the shortcut in the tooltip, not in the label.

States

Light
sidebar
Dark
sidebar
Code
<nav class="rail" aria-label="Rail states" style="height:auto">
  <button class="rail-item" type="button" aria-label="Current area" aria-current="true" data-tip="Current area" data-tip-pos="right"><svg class="i" viewBox="0 0 24 24" aria-hidden="true"><path d="M22 17a2 2 0 0 1-2 2H6.828a2 2 0 0 0-1.414.586l-2.202 2.202A.71.71 0 0 1 2 21.286V5a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2z"/></svg></button>
  <button class="rail-item" type="button" aria-label="Idle" data-tip="Idle" data-tip-pos="right"><svg class="i" viewBox="0 0 24 24" aria-hidden="true"><path d="M20 20a2 2 0 0 0 2-2V8a2 2 0 0 0-2-2h-7.9a2 2 0 0 1-1.69-.9L9.6 3.9A2 2 0 0 0 7.93 3H4a2 2 0 0 0-2 2v13a2 2 0 0 0 2 2Z"/></svg></button>
  <button class="rail-item" type="button" aria-label="Toggle, on" aria-pressed="true" data-tip="Toggle, on" data-tip-pos="right"><svg class="i" viewBox="0 0 24 24" aria-hidden="true"><rect width="18" height="18" x="3" y="3" rx="2"/><path d="M15 3v18"/><path d="m10 15-3-3 3-3"/></svg></button>
  <button class="rail-item" type="button" aria-label="Agents, one running" data-tip="Running work" data-tip-pos="right"><svg class="i" viewBox="0 0 24 24" aria-hidden="true"><path d="M12 8V4H8"/><rect width="16" height="12" x="4" y="8" rx="2"/><path d="M2 14h2"/><path d="M20 14h2"/><path d="M15 13v2"/><path d="M9 13v2"/></svg><span class="rail-badge dot" aria-hidden="true"></span></button>
  <button class="rail-item" type="button" aria-label="Notes, 12 unread" data-tip="12 unread" data-tip-pos="right"><svg class="i" viewBox="0 0 24 24" aria-hidden="true"><polyline points="22 12 16 12 14 15 10 15 8 12 2 12"/><path d="M5.45 5.11 2 12v6a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2v-6l-3.45-6.89A2 2 0 0 0 16.76 4H7.24a2 2 0 0 0-1.79 1.11z"/></svg><span class="rail-badge plain" aria-hidden="true">12</span></button>
  <button class="rail-item" type="button" aria-label="Problems, 128 errors" data-tip="128 errors" data-tip-pos="right"><svg class="i" viewBox="0 0 24 24" aria-hidden="true"><path d="m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3"/><path d="M12 9v4"/><path d="M12 17h.01"/></svg><span class="rail-badge down" aria-hidden="true">99+</span></button>
  <span class="rail-sep"></span>
  <button class="rail-item" type="button" aria-label="Extensions, not available offline" disabled><svg class="i" viewBox="0 0 24 24" aria-hidden="true"><path d="M12 22v-5"/><path d="M15 8V2"/><path d="M17 8a1 1 0 0 1 1 1v4a4 4 0 0 1-4 4h-4a4 4 0 0 1-4-4V9a1 1 0 0 1 1-1z"/><path d="M9 8V2"/></svg></button>
</nav>

aria-current marks the area shown in the sidebar. aria-pressed="true" marks a toggle that is on, such as the inspector; it gets the wash but not the bar, so an area and a toggle never look the same. A badge carries a count (accent), errors (.down), a neutral count (.plain) or just activity (.dot). The count is decoration for sighted people: say it in the label too. A disabled item dims and drops out of the pointer.

Horizontal strip

Light
Dark
390px, dark
Code
<nav class="rail h" aria-label="Areas">
  <div class="rail-top">
    <button class="rail-item" type="button" aria-label="Sessions" aria-current="true"><svg class="i" viewBox="0 0 24 24" aria-hidden="true"><path d="M22 17a2 2 0 0 1-2 2H6.828a2 2 0 0 0-1.414.586l-2.202 2.202A.71.71 0 0 1 2 21.286V5a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2z"/></svg></button>
    <button class="rail-item" type="button" aria-label="Files"><svg class="i" viewBox="0 0 24 24" aria-hidden="true"><path d="M20 20a2 2 0 0 0 2-2V8a2 2 0 0 0-2-2h-7.9a2 2 0 0 1-1.69-.9L9.6 3.9A2 2 0 0 0 7.93 3H4a2 2 0 0 0-2 2v13a2 2 0 0 0 2 2Z"/></svg></button>
    <button class="rail-item" type="button" aria-label="Search"><svg class="i" viewBox="0 0 24 24" aria-hidden="true"><path d="m21 21-4.34-4.34"/><circle cx="11" cy="11" r="8"/></svg></button>
    <button class="rail-item" type="button" aria-label="Agents, 2 running"><svg class="i" viewBox="0 0 24 24" aria-hidden="true"><path d="M12 8V4H8"/><rect width="16" height="12" x="4" y="8" rx="2"/><path d="M2 14h2"/><path d="M20 14h2"/><path d="M15 13v2"/><path d="M9 13v2"/></svg><span class="rail-badge" aria-hidden="true">2</span></button>
  </div>
  <span class="rail-spacer"></span>
  <button class="rail-item" type="button" aria-label="Settings"><svg class="i" viewBox="0 0 24 24" aria-hidden="true"><path d="M21 16V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16z"/><circle cx="12" cy="12" r="4"/></svg></button>
</nav>

.rail.h lays the same items in a 36px row, with the bar on the bottom edge and tooltips below. Use it at the top of a sheet on a phone, where a 44px column would cost too much width.

API

ClassOnWhat it does
.railnavThe 44px column. Sunk background, a 1px rule on the inner edge. --rail-w and --rail-i set the column and item sizes.
.rail-topdivThe group of areas at the top.
.rail-spacerspanPushes what follows to the bottom: the sidebar toggle, settings, the account.
.rail-sepspanA 20px rule between groups.
.rail-itembutton, aA 32px icon button with a 16px icon.
aria-current="true".rail-itemThe area on show. Ink, and the 2px accent bar on the outer edge.
aria-pressed="true".rail-itemA toggle that is on. A wash, no bar.
:disabled, aria-disabled="true".rail-itemDims and removes the pointer target.
.rail-badgespanA 16px count on the top corner. .down for errors, .plain for neutral, .dot for activity with no number.
.h.railThe 36px horizontal strip. The bar moves to the bottom edge and tooltips open below.
Do
  • Five areas at most at the top; settings and the account at the bottom.
  • Put the count in the label: "Problems, 3 errors".
  • Let a press on the current area hide the sidebar, and a second press bring it back.
Don't
  • Text labels in the rail. That is the sidebar.
  • Hiding the rail to save 44px. Then a hidden sidebar has no way back.
  • A badge with no matching words in the label.

Accessibility

  • Use a nav with an aria-label, and real buttons or links. The order in the DOM is the order on screen.
  • The bar is a shape and the icon turns full ink, so the current area is clear without colour. Screen readers hear aria-current.
  • Focus shows a 2px accent ring inside the button. The tooltip opens on focus as well as hover, after the same short delay.
  • Under coarse pointers the column grows to 48px and the items to 40px.
  • The badge is hidden from assistive tech; its number is in the label.