Skip to main content

header — application chrome

UiHeader is the top bar of an application: a brand on the left, an optional current-user widget on the right, and room for a few extra widgets in between. It is deliberately small and opinionated — a reusable chrome primitive, not a generic top-bar builder.

Its intended place is the very first child of a page's top-level stack, above the section that holds the content. The typical layout is header → menu/tabs → body.

Live — one header node: logo plus brand text (a link), two action nodes in extras, and the user widget with its CSS-drawn initials avatar. Click any of them.

Fields

FieldTypeMeaning
idStringNode id — also the DOM id on the <header> (omitted when null).
brandStringBrand text on the left. Plain string; HTML is escaped.
brandHrefStringLink target for the brand. Set = anchor, null = inert text.
brandLogoStringLogo image URL rendered before the brand text, inside the same anchor/span.
userUiHeader.UserCurrent-user widget on the right. null hides it entirely.
menuToggleStringId of a menu to control. Set = a hamburger button at the far left.
extrasList<UiNode>Extra widgets between brand and user. Any nodes; the renderer recurses.
extrasOverflowWRAP · MENUWhat happens when the extras don't fit. WRAP (default) grows a second line; MENU keeps one row and collapses the rest into a dropdown.
titleStringInherited from UiNode; unused by this renderer.
cssClassStringExtra CSS class on the <header>.

UiHeader.User is a small nested type:

FieldTypeMeaning
nameStringDisplay name shown next to the avatar (also the title tooltip).
initialsString1–2 characters drawn inside the avatar circle. Not truncated — pass exactly what you want.
profileHrefStringNavigation target when the widget is clicked.

Building one

UiHeader.of("Shop Admin")
.brandHref("/")
.brandLogo("/img/logo.svg")
.user(UiHeader.User.of("Ada Lovelace", "AL", "/profile"))
.extra(UiAction.primary("new-order", "New order").icon("add")
.onClick(UiTrigger.go("/orders/new")));

// Move the hamburger out of the sidebar and into the top bar:
UiHeader.of("Shop Admin").menuToggle("main-menu");

When the room runs out

A header has three greedy parts — brand, extras, user widget — and a phone gives it about 380 pixels. Rather than squeezing all three, the stylesheet gives up the least informative pixels first: the user's name disappears below 768px (the avatar still identifies them and stays tappable), gaps and padding tighten, and a long brand name truncates with an ellipsis.

That leaves the extras, and there you have a choice — the same one section offers for tabs:

Live — two headers with identical extras. Narrow your window: the first grows a second line, the second keeps one row and moves the rest behind .

BehaviourNeeds JS
WRAP (default)The bar grows taller; extras wrap onto another line.no
MENUOne row, overflow collapses into a dropdown.yes
UiHeader.of("Acme Admin")
.extrasOverflow(UiHeader.ExtrasOverflow.MENU);

MENU is a progressive enhancement and needs no wiring: the event bus runs the shared overflow behaviour on every mount. Without JavaScript the extras simply wrap — nothing is ever unreachable.

Notes

It is chrome, not content. The header belongs once per page, at the top of the page-level stack. Nothing about it is layout-generic — if you need a second bar, a toolbar or a breadcrumb strip, compose plain stacks instead of stretching this node.

extras is the extension point. Each entry goes back through the renderer, so a theme picker, a language switcher built from a form with a submitOnChange select, or a node type of your own all drop straight in. They are laid out side-by-side in a flex container, before the user widget.

menuToggle pairs the header with a drawer menu. Setting it to a menu node's id renders a hamburger at the far left that cycles that menu's state — the same data-menu-toggle hook the menu's own button uses. This is the usual arrangement for an admin shell with an overlay menu.

The avatar needs no asset. It is a CSS circle with initials inside — no image request, no JavaScript. brandLogo, on the other hand, is a real <img>; its alt is the brand text.

Everything is escaped. brand, initials and name are plain strings that survive escaping unchanged — accents and symbols are fine, raw HTML is not. Use extras when you need markup.

See also

  • menu — the sidebar/drawer the menuToggle hamburger controls.
  • action — the usual passenger in extras.
  • section — the content that sits below the header.
  • Triggers & actions — how the brand and user links navigate.