Navbar

Summary

The application header: the banner bar at the top of the shell. Three conventional regions: before (an app switcher ahead of the brand), the brand + global nav (links, some of which may open dropdown menus), and secondary (the end-aligned search / help / user cluster). With data-soma-responsive, primary nav items that no longer fit collapse into a generated, localised More menu. The bare bar is transparent on the tinted page; .soma-navbar-primary is the branded alternative, a primary-colored 48px strip with theme-aware foreground (this docs site wears it).

When to use

SlotUse it for
Before
.soma-navbar-before
Pre-brand content. Conventionally an app-switcher dropdown for jumping between products.
Brand
.soma-navbar-brand
Product name, with an optional <img> logo mark before or instead of the text.
Primary nav
.soma-navbar-nav
Global navigation links. Any item can be a dropdown trigger; add data-soma-responsive on the navbar for overflow handling.
Search
.soma-navbar-search / .soma-quicksearch
A compact search field; use quicksearch when results render live.
Secondary
.soma-navbar-secondary
The end-aligned cluster: notifications, help, the user menu, usually .soma-navbar-action icon buttons.

Examples

All slots

App switcher · brand · global nav with a dropdown · search, help and user menu — every slot live:

Nware Portal

Open the Projects item or the avatar. Dropdown menus position under their navbar trigger, and the nav item keeps its pill while its menu is open.

Responsive overflow

This navbar is constrained to 560px, so data-soma-responsive pushes the items that don't fit into the generated More menu. Resize the window and the split re-computes live:

The More label comes from the string catalog (navbar.more) and follows the active locale.

Branded variant

One class turns the bar into the primary-colored strip — text, links and icons flip to the theme-aware foreground:

Nware Portal

App switcher

The before slot's conventional content: an icon button opening a dropdown of sibling products. It is a composition, not a component — a .soma-navbar-action trigger plus a standard dropdown:

<div class="soma-navbar-before">
  <button class="soma-navbar-action soma-dropdown2-trigger"
          aria-controls="apps-menu" aria-label="Switch product">
    <span class="soma-icon soma-icon-apps"></span>
  </button>
</div>
…
<div id="apps-menu" class="soma-dropdown2" aria-hidden="true">
  <ul>
    <li><a href="…">Nware Portal</a></li>
    <li><a href="…">Nware Cortex</a></li>
    <li><a href="…">Soma docs</a></li>
  </ul>
</div>

Responsive overflow

With data-soma-responsive the navbar measures its primary nav on every resize (a ResizeObserver, so container-driven layouts work too, not just the viewport) and moves the rightmost items into a generated trigger + dropdown labelled from the navbar.more catalog string. All items return when space allows. The original elements are moved, never cloned, so listeners survive. The item set is snapshotted at init: items added to the nav later need .destroy() and re-init to be managed.

HTML

The full anatomy (every slot is optional except the brand):

<header class="soma-navbar" data-soma-responsive>

  <div class="soma-navbar-before">…app switcher…</div>

  <a class="soma-navbar-brand" href="/">
    <img src="logo.svg" alt="" /> Nware <strong>Portal</strong>
  </a>

  <nav class="soma-navbar-nav" aria-label="Primary">
    <ul>
      <li><a href="…" aria-current="page">Dashboard</a></li>
      <li><a href="…">Deployments</a></li>
      <li><a class="soma-dropdown2-trigger" aria-controls="nav-projects" href="#">Projects</a></li>
    </ul>
  </nav>

  <div class="soma-navbar-secondary">
    <label class="soma-navbar-search">
      <span class="soma-icon soma-icon-search"></span>
      <input type="search" placeholder="Search…" aria-label="Search" />
    </label>
    <button class="soma-navbar-action" aria-label="Notifications">
      <span class="soma-icon soma-icon-bell"></span>
    </button>
  </div>

</header>

A nav item that opens a menu is a normal link with soma-dropdown2-trigger + aria-controls. It grows the chevron automatically, and auto-init wires it like any other dropdown. The menus themselves live outside the navbar markup.

The branded variant is the same anatomy with one extra class:

<header class="soma-navbar soma-navbar-primary">
  …same slots as the bare bar…
</header>

CSS classes

ClassEffect
.soma-navbar-primaryBranded variant: primary-colored 48px bar, theme-aware foreground, fg-tinted hover pills and focus rings (this docs site wears it). The bare navbar stays transparent.
.soma-navbarThe bar: transparent, full width, flex row. Put it in a <header> for the banner landmark.
.soma-navbar-beforeSlot ahead of the brand (app switcher).
.soma-navbar-brandProduct name; an optional child <img> renders as a logo mark.
.soma-navbar-navGlobal nav: ul > li > a, hover pills, aria-current="page" or .soma-active marks the current link. A .soma-dropdown2-trigger link grows a chevron and keeps its pill while open.
.soma-navbar-searchCompact search field (icon + input in a label).
.soma-navbar-secondaryEnd-aligned cluster; makes the spacer unnecessary.
.soma-navbar-actionRound icon button (notifications, help, account). Needs an aria-label.
.soma-navbar-spacerFlexible gap, the older alternative to -secondary; still valid.
[data-soma-responsive]Opts the navbar into overflow management (auto-init).

JavaScript

Constructor

FormNotes
Soma.responsiveNavbar(elOrSelector)Get or create the singleton for a .soma-navbar. Throws when nothing matches or the element lacks the class. A navbar without a .soma-navbar-nav > ul yields an inert instance (nothing to manage). No options.
<… data-soma-responsive>Auto-init binds every matching .soma-navbar on DOMContentLoaded. The attribute is all most pages need.

Instance methods

MethodEffect
.refresh()Re-measure and re-distribute. Container resizes are already covered by the ResizeObserver; call this after other width-affecting changes: renamed labels, a late-loading font.
.destroy()Disconnect the observer, move any overflowed items back into the navbar, remove the generated "More" trigger and menu, drop the singleton.

There are no options and no events — the component manages markup you already own. The generated menu is a standard dropdown2 appended to <body>, so overflowed items keep full menu keyboard support; list items are moved, not cloned, so their listeners survive the trip in and out. The available width honours everything after the nav in the bar (actions, version …), skips the flexible spacer, and is RTL-aware. Remember the snapshot rule — changing the item set means re-init:

// data-soma-responsive auto-inits; keep an instance only for the API.
const rn = Soma.responsiveNavbar(document.querySelector('.soma-navbar'));

rn.refresh();     // re-measure after a label change / font swap

// Changing the item set = re-init:
rn.destroy();
navUl.insertAdjacentHTML('beforeend', '<li><a href="/audit">Audit</a></li>');
Soma.responsiveNavbar(document.querySelector('.soma-navbar'));

The live examples above, exactly as this page wires them:

<!-- Dropdown menus for the all-slots demo (outside the navbar) -->
<div id="nb-apps" class="soma-dropdown2" aria-hidden="true">
  <ul>
    <li><a href="#">Nware Portal</a></li>
    <li><a href="#">Nware Cortex</a></li>
    <li><a href="#">Soma docs</a></li>
  </ul>
</div>
<div id="nb-projects" class="soma-dropdown2" aria-hidden="true">
  <ul>
    <li><a href="#">Project X</a></li>
    <li><a href="#">Project Y</a></li>
    <li><a href="#">All projects</a></li>
  </ul>
</div>
<div id="nb-user" class="soma-dropdown2" aria-hidden="true">
  <div class="soma-dropdown2-section">
    <strong class="soma-dropdown2-heading">an@nware.io</strong>
    <ul>
      <li><a href="#">Profile</a></li>
      <li><a href="#">Sign out</a></li>
    </ul>
  </div>
</div>
// The page itself needs no JS: the dropdown triggers and the
// data-soma-responsive navbar are all wired by auto-init.