The bar above is every app's only header

It wears the current app's name, icon and accent, carries that app's navigation, and adds the switcher into the other apps plus the shared account menu. One tag:

<script src="https://bar.cashabarba.com/appbar.js" data-app="train" defer></script>

Navigation comes from apps.json, so it renders before the app's own JavaScript does. data-app only matters when the hostname is not in that registry.

Driving it from the app

cbAppbar.configure({
  title: household.name,                       // overrides the app name
  nav: [{ label: 'Home', key: 'home' }],       // key = in-page section, href = link
  active: 'home',
  actions: [{ label: 'Reset week', onClick: resetWeek }],
  onNavigate: (key) => showSection(key),       // also fires cb-appbar:navigate
});
cbAppbar.setStatus('Saved', 'ok');             // small pill beside the nav
cbAppbar.profile().then((p) => p.displayName); // shared profile, no extra request

A second row

Sub-navigation and banners stay with the app, hung below the bar:

.subnav { position: sticky; top: var(--cb-appbar-h, 0px); }

The account

One profile for every app, at /account — display name, avatar, units, sex, birth year. It is stored as Supabase user_metadata, so it arrives with the identity check each app already makes.