feat(branding): per-app banner + per-brand default theme via brand.json #104

Merged
padreug merged 2 commits from feat/brand-banner-and-default-theme into dev 2026-06-15 20:15:49 +00:00

2 commits

Author SHA1 Message Date
d4d088fb50 feat(branding): per-brand default theme + palette via brand.json
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>
2026-06-15 22:12:13 +02:00
8f2c401e00 feat(branding): optional per-app banner replacing logo + name in header
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>
2026-06-15 22:11:01 +02:00