feat(branding): brand kit architecture (Phase 1) #96
2 changed files with 162 additions and 0 deletions
docs(branding): brand kit contract + CLAUDE.md section
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>
commit
3dfed23b43
52
CLAUDE.md
52
CLAUDE.md
|
|
@ -712,8 +712,60 @@ VITE_ADMIN_PUBKEYS='["pubkey1","pubkey2"]'
|
|||
|
||||
# Optional: Disable WebSocket if needed
|
||||
VITE_WEBSOCKET_ENABLED=true
|
||||
|
||||
# Brand kit override (defaults to ./branding/default)
|
||||
BRAND_DIR=branding/cfaun
|
||||
|
||||
# Per-standalone brand override (set by build pipeline, not directly)
|
||||
BRAND_APP=events
|
||||
```
|
||||
|
||||
## Brand kit (white-label PWA branding)
|
||||
|
||||
The webapp ships a brand kit architecture so the hub + every standalone
|
||||
(events, wallet, chat, market, …) can be rebranded per deployment without
|
||||
forking the codebase. See `branding/README.md` for the deployer contract.
|
||||
|
||||
**Single source of truth:** `branding/<dep>/` holds `logo.{svg,png}` +
|
||||
`brand.json`. `vite-branding.ts` reads brand.json and exposes a `@brand`
|
||||
import alias. `pwa-assets.config.ts` + `@vite-pwa/assets-generator` derive
|
||||
the full PWA icon set from the single logo source.
|
||||
|
||||
**brand.json schema:** `{ name, shortName?, themeColor?, backgroundColor? }`
|
||||
— `name` drives the manifest. `themeColor`/`backgroundColor` are optional
|
||||
chrome overrides; when unset, each standalone's per-app accent applies.
|
||||
|
||||
**In-app logo:** components reference `@brand/logo.png`. Active consumers:
|
||||
`Login.vue`, `LoginDemo.vue`, `AppSidebar.vue`, `MobileDrawer.vue`. The
|
||||
Vite alias resolves to the active brand dir at build time.
|
||||
|
||||
**Generated icons:** `public/icons/` is gitignored. `brandAssetsPlugin()`
|
||||
(registered first in every `vite.*.config.ts`'s plugins[]) runs the
|
||||
generator once per build/dev start via `buildStart`. Outputs match the
|
||||
existing filename convention (`icon-192.png`, `icon-maskable-512.png`,
|
||||
…) so HTML `<link>` hrefs and VitePWA `manifest.icons` reference
|
||||
`/icons/<name>` consistently across all 9 configs.
|
||||
|
||||
**Per-standalone override:** `branding/<dep>/icons/<app>/logo.{svg,png}`
|
||||
is checked before the brand's primary logo. The standalone build sets
|
||||
`BRAND_APP`; deployers just put files in the right place.
|
||||
|
||||
**Switching brands:**
|
||||
```bash
|
||||
BRAND_DIR=branding/cfaun pnpm build:events
|
||||
```
|
||||
|
||||
**Adding a new in-app logo consumer:** use `<img src="@brand/logo.png">`
|
||||
instead of `@/assets/logo.png`. The latter still works for non-brand
|
||||
assets (`@/assets/bitcoin.svg`, etc.) — it's only the logo that moved.
|
||||
|
||||
**NixOS deployment:** brand directories will eventually live in
|
||||
`deploy/server-deploy/hosts/<host>/branding/`, with each host's
|
||||
`services/webapp.nix` calling `inputs.webapp.lib.mkWebapp { brandDir =
|
||||
./../branding; }`. `flake.nix` exposing `lib.mkWebapp` is a follow-up
|
||||
to this PR. Until it lands, server-deploy continues to build webapp
|
||||
through its existing path. See aiolabs/server-deploy#8.
|
||||
|
||||
## Payment Rails Pattern
|
||||
|
||||
Shared primitives for modules that mix Lightning + fiat (and, future,
|
||||
|
|
|
|||
110
branding/README.md
Normal file
110
branding/README.md
Normal file
|
|
@ -0,0 +1,110 @@
|
|||
# Brand kit
|
||||
|
||||
This directory holds the **white-label brand kit** that drives the PWA's icons, manifest name/colors, and in-app `<img>` logo across the hub and every standalone (events, wallet, chat, market, …).
|
||||
|
||||
The committed `default/` is aiolabs's brand and is what unparameterized builds use. Downstream deployers add a sibling directory (e.g. `branding/cfaun/`) and point `BRAND_DIR` at it to ship a fully rebranded build with no fork required.
|
||||
|
||||
## Directory layout
|
||||
|
||||
```
|
||||
branding/
|
||||
README.md
|
||||
default/ # aiolabs default, committed
|
||||
logo.svg # preferred source (sharp at every size)
|
||||
logo.png # fallback source (≥ 1024×1024 if PNG-only)
|
||||
brand.json # { name, shortName?, themeColor?, backgroundColor? }
|
||||
icons/ # optional per-standalone overrides
|
||||
events/logo.svg
|
||||
wallet/logo.png
|
||||
cfaun/ # downstream deployer's brand (gitignored or in deploy repo)
|
||||
logo.svg
|
||||
brand.json
|
||||
```
|
||||
|
||||
aiolabs's `default/` currently ships PNG-only (1024×1024). Replace with `logo.svg` when a vector source becomes available — produces sharper icons at every size and unlocks `favicon.svg`.
|
||||
|
||||
## Source formats
|
||||
|
||||
**SVG strongly preferred:**
|
||||
|
||||
- Crisp at every output size (192 / 512 maskable / 180 apple / 48 favicon)
|
||||
- Enables sharp `favicon.svg` for modern browsers
|
||||
- The in-app `@brand/logo` reference can be tinted via CSS (`currentColor`, filters)
|
||||
|
||||
**PNG accepted with constraints:**
|
||||
|
||||
- **≥ 1024×1024** — smaller sources produce blurry icons on high-DPI Android install screens
|
||||
- **Square aspect ratio** — PWA icon canvas is square
|
||||
- **Transparent background** — the generator applies maskable/apple background colors itself
|
||||
- PNG-source deployments lose the `favicon.svg` benefit and the recolorable in-app logo
|
||||
|
||||
When both `logo.svg` and `logo.png` are present, SVG wins.
|
||||
|
||||
## brand.json schema
|
||||
|
||||
```jsonc
|
||||
{
|
||||
"name": "AIO", // required — drives PWA manifest name
|
||||
"shortName": "AIO", // optional — PWA home-screen label; defaults to `name`
|
||||
"themeColor": "#1f2937", // optional — PWA chrome color override (otherwise each standalone keeps its accent)
|
||||
"backgroundColor": "#fff" // optional — PWA splash background
|
||||
}
|
||||
```
|
||||
|
||||
`themeColor` and `backgroundColor` are *overrides*, not defaults. When unset, each standalone's own accent applies (wallet yellow `#eab308`, chat green `#16a34a`, …) — so the default brand kit preserves the per-app visual identity, and a deployer who wants unified chrome adds the override.
|
||||
|
||||
## Per-standalone overrides
|
||||
|
||||
Place a logo at `branding/<dep>/icons/<app>/logo.{svg,png}` to override the brand's primary logo for a single standalone build.
|
||||
|
||||
Resolution at build time:
|
||||
|
||||
1. `branding/<dep>/icons/<app>/logo.svg`
|
||||
2. `branding/<dep>/icons/<app>/logo.png`
|
||||
3. `branding/<dep>/logo.svg`
|
||||
4. `branding/<dep>/logo.png`
|
||||
5. Build fails with a clear error pointing here.
|
||||
|
||||
`<app>` is set via `BRAND_APP` env var (the standalone build script sets this; deployers don't touch it directly).
|
||||
|
||||
## How to use
|
||||
|
||||
**Building with the default brand:**
|
||||
|
||||
```bash
|
||||
pnpm build # main shell
|
||||
pnpm build:events # events standalone
|
||||
# … one per standalone
|
||||
```
|
||||
|
||||
**Building with a deployer's brand:**
|
||||
|
||||
```bash
|
||||
BRAND_DIR=branding/cfaun pnpm build:events
|
||||
```
|
||||
|
||||
`BRAND_DIR` accepts relative paths (resolved from the webapp repo root) or absolute paths (used by the NixOS builder, which mounts the brand directory into the sandbox at a `/nix/store/...-branding` path).
|
||||
|
||||
**Regenerating icons explicitly:**
|
||||
|
||||
The Vite plugin auto-runs the generator on every build/dev start. To run it standalone:
|
||||
|
||||
```bash
|
||||
pnpm generate-pwa-assets
|
||||
```
|
||||
|
||||
Outputs land in `public/icons/` (gitignored).
|
||||
|
||||
## Build pipeline
|
||||
|
||||
1. `BRAND_DIR` is resolved (defaults to `./branding/default`).
|
||||
2. `vite-branding.ts` reads `brand.json` and exposes `@brand/<file>` alias.
|
||||
3. `brandAssetsPlugin()` (registered in every `vite.*.config.ts`) runs `scripts/generate-pwa-assets.mjs` once per build via `buildStart`.
|
||||
4. The script stages the source logo into `public/icons/.brand-source.{svg,png}`, runs `pwa-assets-generator`, then deletes the staged source.
|
||||
5. Vite copies `public/icons/` into `dist/icons/`. Manifest references `icons/<name>.png`. HTML `<link>` tags reference `/icons/<name>.{ico,png}`.
|
||||
|
||||
## Integration with NixOS deployment
|
||||
|
||||
See [aiolabs/webapp#95](https://git.atitlan.io/aiolabs/webapp/issues/95) for the full architecture. In short: `deploy/server-deploy/hosts/<host>/branding/` will hold per-host brands, and each host's `services/webapp.nix` will call `inputs.webapp.lib.mkWebapp { brandDir = ./../branding; }` once `lib.mkWebapp` is exposed from `flake.nix` (separate follow-up issue).
|
||||
|
||||
The architectural payoff: brand and code become independent axes. Logo changes ship via server-deploy commits + redeploys — no webapp release, no `flake.lock` bump.
|
||||
Loading…
Add table
Add a link
Reference in a new issue