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.