Brand kit architecture: white-label PWA branding #95
Labels
No labels
app:activities
app:chat
app:events
app:forum
app:libra
app:market
app:restaurant
app:tasks
app:wallet
app:webapp
bug
enhancement
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
aiolabs/webapp#95
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Background
Today's branding state across the multi-app shell:
src/assets/logo.png, 1024×1024) — hardcoded reference inLogin.vue,LoginDemo.vue,AppSidebar.vue,MobileDrawer.vue.public/:favicon.ico,apple-touch-icon.png(180),mask-icon.svg,icon-{192,512}.png,icon-maskable-{192,512}.png. Hand-crafted, committed binaries.vite.<app>.config.tsregisters the same sevenpublic/filenames inVitePWA.includeAssets+manifest.icons. Same paths in every<app>.html.VITE_APP_NAME(commit5541d2b).So today: name varies per deployment, all icons are shared across every standalone and every deployment. A deployment branded "Sortir" still ships the generic aiolabs logo as the PWA install icon, the favicon, and the in-app
<img>.Public sector + cooperative deployers want their own logo. NixOS hosts in
deploy/server-deploy(cfaun, four84, syntro, atio, …) want per-host branding. Per-standalone overrides are the cherry on top.Goals
deploy/server-deploy/hosts/<host>/branding/and changing a logo is a server-deploy commit + redeploy, NOT a webapp release. Brand and code evolve on independent axes.@vite-pwa/assets-generator. No more hand-managing seven PNG files at seven sizes.Proposed architecture
Brand kit contract
Resolution order at build time:
<X>:branding/<dep>/icons/<X>/logo.{svg,png}if presentbranding/<dep>/logo.{svg,png}branding/README.mdSource format support
favicon.svg(modern browsers prefer this over.ico), and the in-app@brand/logocan be tinted via CSS.favicon.svgbenefit and the recolorable in-app logosrc/assets/logo.pngis our 1024 source). An SVG upgrade is a separate, non-blocking task.Build pipeline
@vite-pwa/assets-generatorreadsBRAND_DIRenv var (default:./branding/default).vite build, generating into a gitignoredpublic/icons/directory.vite.<app>.config.tsreferences generated paths inVitePWA.includeAssets+manifest.icons.name,theme_color,background_colorcome frombrand.json(replacing the currentVITE_APP_NAMEenv injection).<app>.html<link rel="icon">/apple-touch-icon/mask-icontags resolved via Vite HTML transforms.<img src="@/assets/logo.png">references switch to a@brand/logoVite alias resolved fromBRAND_DIR.Gitignored generated icons
public/icons/becomes a build artifact, not committed. Removes "did I forget to commit the regenerated 512 maskable" from the failure modes. Build script regenerates each time.NixOS deployment integration
The brand kit slots directly into the existing
deploy/server-deployfan-out:The webapp's
flake.nixexposes a parameterized builder instead of (or alongside) a fixedpackages.default:Each host calls it:
Why this is the right shape
1. Logo changes don't require a webapp release. This is the meaningful UX win. Today: webapp dev → bump
webapp-demolock → vet on aio-demo → fast-forward main → bumpwebapplock → deploy. With brand kit: one server-deploy commit + redeploy. Nix sees the brand dir hash change → rebuilds closure → deploys.2. CLAUDE.md's "production-bound changes need a flake.lock bump" still applies — but only to code. Brand assets live in server-deploy, change without flake input bumps. Brand and code become independent axes.
3. Each host's brand is co-located with its other host config. Same directory as nginx vhost, same dir as service module. Forking a host's branding is one cp.
4. Reproducibility for free. Brand assets become part of the nix derivation hash. Hosts sharing a brand share the build via the binary cache.
5. Composes with external white-labelers. A third party running webapp on their own NixOS infra calls
inputs.webapp.lib.mkWebapp { brandDir = ./their-branding; }. Their brand stays in their infra repo. Real white-label — not just multi-tenancy.Release flow becomes cleaner
The mental model: server-deploy stages branding decisions per-host; webapp stages code decisions per-channel. Two orthogonal axes.
Scope / phasing
Phase 1 — this issue: brand kit architecture in webapp.
branding/default/withbrand.json+ current logo aslogo.png@vite-pwa/assets-generator, sharp verification under nixpwa-assets.config.tsreadingBRAND_DIR<link>tags in<app>.html@brand/logoVite alias, migrate<img src="@/assets/logo.png">consumersbrand.jsondrives manifestname/theme_color/background_color(replacesVITE_APP_NAMEenv injection)public/icons/, remove the seven committed icon binarieslib.mkWebappfromflake.nixbranding/README.mdCLAUDE.md(webapp + workspace) under the standalone app patternPhase 2 — follow-up server-deploy PR: migrate hosts to
mkWebapp.branding/directories createdservices/webapp.nixswitches toinputs.webapp.lib.mkWebapp { brandDir = ./../branding; }VITE_APP_NAMEenv injection removed (now driven bybrand.json)aiolabs/server-deploy.Acceptance
BRAND_DIRproduces the aiolabs default (no regression for current behavior).BRAND_DIR=branding/test-fixture(with a different logo) produces a build whose favicon, apple-touch icon, PWA install icons, in-app logo, and manifest name/colors all reflect the test fixture.icons/events/logo.svgproduces an events standalone whose icons differ from the same fixture's main webapp.nix buildagainst the webapp flake'slib.mkWebappproduces a usable staticdist/derivation.branding/README.md+CLAUDE.md.Open questions
<img>can be tinted via CSS filters /currentColor. Do we want the in-app brand resolution to inline SVG as a Vue component (full color control) or just<img src>(simpler, no recoloring)? Probably<img>for v1.brand.jsonschema: minimum is{ name }. Should also includethemeColor,backgroundColor, maybesiteUrl, maybesupportEmail. Define + Zod-validate at build time.@vite-pwa/assets-generatoruses sharp (native libvips). Historically a nix pain point. Verify on first deploy underbuildNpmPackage; document the fix if non-trivial.Context
Discovered while answering "how does the logo work and how do I change it." Original answer was "swap the shared files; per-standalone icons aren't supported; per-deployment branding isn't supported either." This issue closes that gap properly — not as a per-standalone tweak but as the white-label brand kit the deployment model actually wants.
Per-standalone PWA icons + faviconto Brand kit architecture: white-label PWA brandingPhase 2 follow-up filed: aiolabs/server-deploy#8 — "Migrate webapp hosts to brand-kit (mkWebapp) deployment". Tracks the host-side migration once
lib.mkWebapplands here.