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>
<link rel="stylesheet" href="https://bar.cashabarba.com/tokens.css">

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. tokens.css is the single source of truth for colour — apps alias its --cb-* custom properties onto their local names.

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.