Lets a deployer set the in-app color scheme a fresh visitor sees (e.g.
cfaun → darkmatter light) without forking. Two optional brand.json
fields, `theme` (light|dark|system) and `palette` (one of PALETTES),
distinct from `themeColor` which is PWA chrome only.
- vite-branding.ts surfaces them as VITE_BRAND_THEME / VITE_BRAND_PALETTE
at module load, so the default applies app-wide (hub + all standalones)
with no per-config wiring.
- theme-provider reads them as the INITIAL value of theme/palette; a
user's stored choice in localStorage still wins and persists.
- Splits the catppuccin = bare `:root` invariant (now BASE_PALETTE, used
by applyPalette to drop data-theme) from the configurable default.
Without this, a non-catppuccin brand default would strip the
data-theme attribute and silently render catppuccin instead.
Unset → the app's built-ins (dark + catppuccin), unchanged.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
A brand may ship a wide banner (logo + wordmark in one image) that
replaces the brand-kit logo + app-name pair in a standalone's header.
Events is the first consumer.
Banners are optional and resolve at build time, mirroring the existing
@brand-app-logo chain:
- resolveAppBanner(app?) checks per-standalone override first
(branding/<dep>/icons/<app>/banner.{svg,png}) then the brand's primary
banner (branding/<dep>/banner.{svg,png}); returns null when absent
instead of throwing, so brands without a banner keep logo + name.
- brandAppBannerAliasEntry() always registers the @brand-app-banner
alias (falling back to the logo) so the static import resolves; whether
it renders is gated by the VITE_APP_BANNER build flag.
- EventsPage renders the banner when the flag is set, else logo + name.
Deployers override per-standalone without touching the component. SVG
banners must have their text outlined to paths (browsers lack designer
fonts) — documented in branding/README.md.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
branding/README's "Integration with NixOS deployment" section now
describes the actual lib.mkWebapp API + the per-host call site, with
a ready-to-paste server-deploy snippet. Also documents the pnpm_10
pin, sharp/autoPatchelfHook handling, and CI=true bypass — anchors
that surface in error logs and benefit from being grep-able.
CLAUDE.md's NixOS deployment paragraph stops calling lib.mkWebapp a
future TODO and points at the API directly.
Adds a `nix build` recipe (default + impure brand override) for local
sanity-checking.
Part of aiolabs/webapp#97.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
branding/README.md is the deployer contract:
- Directory layout, source format constraints (SVG > PNG ≥ 1024),
brand.json schema, per-standalone override resolution order
- BRAND_DIR / BRAND_APP usage, generator pipeline walkthrough
- Pointer to issue #95 + the NixOS Phase 2 integration
webapp CLAUDE.md gains a Brand Kit section describing the moving
parts (vite-branding.ts, @brand alias, brandAssetsPlugin,
public/icons/ gitignore, per-app override path) so future sessions
on this repo know the convention without grepping.
Adds BRAND_DIR / BRAND_APP to the Environment Variables example.
Workspace ~/dev/CLAUDE.md note about "brand changes don't need
flake.lock bump" deferred to Phase 2 (server-deploy migration) —
that's when the workflow becomes reality.
Part of aiolabs/webapp#95.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>