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
| Slot | Use 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
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
| Class | Effect |
|---|---|
.soma-navbar-primary | Branded 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-navbar | The bar: transparent, full width, flex row. Put it in a <header> for the banner landmark. |
.soma-navbar-before | Slot ahead of the brand (app switcher). |
.soma-navbar-brand | Product name; an optional child <img> renders as a logo mark. |
.soma-navbar-nav | Global 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-search | Compact search field (icon + input in a label). |
.soma-navbar-secondary | End-aligned cluster; makes the spacer unnecessary. |
.soma-navbar-action | Round icon button (notifications, help, account). Needs an aria-label. |
.soma-navbar-spacer | Flexible gap, the older alternative to -secondary; still valid. |
[data-soma-responsive] | Opts the navbar into overflow management (auto-init). |
JavaScript
Constructor
| Form | Notes |
|---|---|
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
| Method | Effect |
|---|---|
.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.