Component Porchlight CSS

Nav

Vertical sidebar navigation with icon+label items, active states, collapsible sections, and icons-only variant.

52 components 51 stable 1 experimental

Command

Search Porchlight

Nav

The .c-nav renders a vertical navigation list for sidebars. Icon + label items with active state via aria-current="page". Collapsible sub-sections via native <details>. Icons-only variant for collapsed sidebars.

It also includes compact sidebar footer patterns for SaaS apps: metadata such as account identity, workspace, tenant, environment, or session status, plus small full-width actions that can be rendered as either links or buttons.

For nav-launched menus, compose .c-menu with .c-nav__menu. Use this for actions, flyouts, and destination lists. Use .c-dropdown only when the nav control is choosing a value rather than opening a navigation/action menu.

Semantic HTML

<nav class="c-nav" aria-label="Main">
  <a class="c-nav__item" aria-current="page">
    <svg class="c-nav__icon">...</svg>
    <span class="c-nav__label">Dashboard</span>
  </a>
  <div class="c-nav__group">
    <span class="c-nav__group-label">Workspace</span>
  </div>
  <details class="c-nav__section" open>
    <summary class="c-nav__section-trigger">
      <svg class="c-nav__icon">...</svg>
      <span>Projects</span>
      <svg class="c-nav__chevron">...</svg>
    </summary>
    <a class="c-nav__item c-nav__item--child">
      <span class="c-nav__label">Active</span>
    </a>
  </details>
</nav>

Groups and Dividers

Use .c-nav__group-label for quiet, non-interactive grouping. Use .c-nav__divider when a separator is enough and a label would add noise.

<nav class="c-nav" aria-label="Main">
  <div class="c-nav__group">
    <span class="c-nav__group-label">Workspace</span>
  </div>
  <a class="c-nav__item" href="/dashboard">Dashboard</a>
  <a class="c-nav__item" href="/accounts">Accounts</a>
  <hr class="c-nav__divider" />
  <a class="c-nav__item" href="/settings">Settings</a>
</nav>

Use .c-nav__menu on a .c-menu wrapper when a sidebar row opens a menu or flyout. The trigger uses .c-menu__trigger, but Porchlight gives that trigger the same density, icon alignment, current state, and icon-rail behavior as other nav rows.

<nav class="c-nav" aria-label="Main">
  <div
    class="c-menu c-nav__menu"
    data-placement="inline-end"
    style="--c-menu-anchor: --nav-reports;"
  >
    <button
      class="c-menu__trigger"
      type="button"
      popovertarget="nav-reports"
      aria-haspopup="menu"
    >
      <svg class="c-nav__icon" aria-hidden="true">...</svg>
      <span class="c-nav__label">Reports</span>
      <svg class="c-nav__chevron" aria-hidden="true">...</svg>
    </button>
    <div class="c-menu__popover" id="nav-reports" popover>
      <a class="c-menu__item" href="/reports" aria-current="page">
        <span class="c-menu__item-body">
          <span class="c-menu__item-label">Overview</span>
          <span class="c-menu__item-description">Executive KPI rollup</span>
        </span>
        <kbd class="c-menu__item-shortcut">R</kbd>
      </a>
      <button class="c-menu__item" type="button">Export CSV</button>
    </div>
  </div>
</nav>

data-placement="inline-end" places the menu beside the sidebar row, which is the usual desktop rail/flyout pattern. Keep aria-haspopup="menu" on triggers that open action menus. If your app adds roving focus, typeahead, or submenu keyboard behavior, own that JavaScript in the app layer; the CSS kit keeps native links and buttons reachable by Tab.

Use .c-nav__footer at the end of a sidebar nav for compact metadata. The metadata block keeps long labels truncated, and .c-nav__action provides a full-width action surface for links or buttons without the weight of a regular .c-button.

<nav class="c-nav" aria-label="Main">
  ...
  <div class="c-nav__footer">
    <div class="c-nav__meta">
      <span class="c-nav__meta-icon">
        <svg aria-hidden="true">...</svg>
      </span>
      <span class="c-nav__meta-body">
        <span class="c-nav__meta-label">avery.long.email@example.com</span>
        <span class="c-nav__meta-description">
          <span class="c-badge" data-tone="success">Admin</span>
          Online
        </span>
      </span>
    </div>
    <div class="c-nav__actions">
      <a class="c-nav__action" href="/switch-user">
        <svg class="c-nav__action-icon" aria-hidden="true">...</svg>
        <span class="c-nav__action-label">Switch user</span>
      </a>
      <button class="c-nav__action" type="button">
        <svg class="c-nav__action-icon" aria-hidden="true">...</svg>
        <span class="c-nav__action-label">Sign out</span>
      </button>
    </div>
  </div>
</nav>

The footer metadata selectors are intentionally generic. Use them for account or session state when that is your product language, but the public API does not require the metadata to represent a user account.

Icons-only (collapsed sidebar)

<nav class="c-nav" data-variant="icons" aria-label="Quick nav">
  <a class="c-nav__item" aria-current="page" aria-label="Dashboard">
    <svg class="c-nav__icon">...</svg>
  </a>
  <div class="c-menu c-nav__menu" data-placement="inline-end">
    <button
      class="c-menu__trigger"
      aria-label="Reports"
      popovertarget="reports"
    >
      <svg class="c-nav__icon">...</svg>
      <span class="c-nav__label">Reports</span>
    </button>
    <div class="c-menu__popover" id="reports" popover>...</div>
  </div>
</nav>

In icon-only nav, every trigger or link needs a durable accessible name (aria-label, visible tooltip trigger text, or equivalent). Labels, badges, group labels, and chevrons are visually hidden by the variant.

Class contract

SelectorRole
.c-navContainer (flex column).
.c-nav__itemA navigation link.
.c-nav__item--childNested item inside a section.
.c-nav__iconLeading icon.
.c-nav__labelText label.
.c-nav__badgeTrailing badge (count).
.c-nav__groupNon-interactive item group.
.c-nav__group-labelQuiet group label.
.c-nav__dividerSeparator between item groups.
.c-nav__menu.c-menu wrapper inside nav.
.c-nav__footerBottom metadata/action region.
.c-nav__metaCompact metadata block.
.c-nav__meta-iconMetadata icon/avatar slot.
.c-nav__meta-bodyMetadata text stack.
.c-nav__meta-labelTruncated metadata label.
.c-nav__meta-descriptionSecondary metadata row.
.c-nav__actionsCompact action stack.
.c-nav__actionSmall full-width link/button.
.c-nav__action-iconAction icon slot.
.c-nav__action-labelTruncated action label.
.c-nav__sectionCollapsible <details> group.
.c-nav__section-triggerThe <summary> (click target).
.c-nav__chevronExpand/collapse arrow.
[aria-current]Current item, except "false".
[data-variant="icons"]Icons-only mode (hides labels).