Brand assets #
This directory is the build-time source of truth for Streamplace's visual
identity. Everything else — the Expo app icon and splash screen (and from
those, every iOS and Android icon format via expo prebuild), favicons, the
Web Push fallback icon, OG link banner, desktop ICO/ICNS icons, docs logos,
the downloadable SVGs on /brand, and the logo components rendered in the app itself — is generated
from these files by js/brand/generate.mjs and gitignored.
Run the generator with pnpm run brand from the repo root. It also runs
automatically on pnpm install and before app, docs, and desktop builds.
Web Push uses /notification-icon, which serves the node's runtime mainLogo
branding asset with its declared image MIME type. Production and other branded
nodes can therefore use the same binary; no custom build is required.
If no image logo is configured, the endpoint serves the bundled
/brand/notification-icon.png, a 512×512 PNG generated from the app-icon artwork
(icon.svg / icon.png, or the synthesized mark). The main notification image
never uses the favicon; the small notification badge is unchanged. Responses
are cached for five minutes, so runtime logo changes can take that long to appear.
Runtime browser favicons #
In Settings → Branding, upload three independent icons: Default favicon,
Light mode favicon, and Dark mode favicon. Each accepts SVG, PNG, or ICO
up to 100 KiB. Use an icon with a background for the default and transparent
light/dark overrides if desired; uploads are served unchanged. Override selection
follows the browser/OS prefers-color-scheme, not the app's theme setting, and
changes without reloading the page.
The branding keys are favicon (default), faviconLight, and faviconDark.
Clients without theme selection and schemes without an override use the default;
if no default is uploaded, they use the bundled mark.
The same keys travel with branding ZIP exports/imports and can be set through
the branding CLI or XRPC. No custom frontend build is needed for uploads.
The HTML declares a generic /favicon.png link before media-qualified
/favicon.png?scheme=light and /favicon.png?scheme=dark links, so selection
also works before JavaScript loads. /favicon.ico remains the generic fallback.
These routes serve uploads with their declared image MIME type and cache for
five minutes; the open admin tab reflects uploads and deletions immediately.
Hydrated fallback links use fresh URLs on branding changes so a deleted upload
cannot return from the browser's cache.
The brand generator owns the bundled PNG and ICO, rather than Expo injecting
an extra generic icon link that would override the themed links.
Favicons are web-only; native app icons and the Web Push icon are unchanged.
White-labeling #
To ship your own identity, point the generator at your own flat directory of files, either of:
brand/custom/— a gitignored sibling of this directory; if it contains abrand.jsonit takes precedence.SP_BRAND_DIR=/path/to/your/brand— explicit override, wins over both.
The directory in git holds the generic open-source identity.
File contract #
Only two files are required; everything else is synthesized from the mark
and brand.json colors when absent.
| File | Required | Purpose |
|---|---|---|
brand.json |
yes | Name, wordmark text, colors, monochrome flag, optional story for the /brand guidelines page. |
mark.svg |
yes | The logo mark. Must declare a viewBox. If monochrome is true, use fill="currentColor" so the UI can tint it. |
icon.svg / icon.png |
no | Full-bleed square app icon art. Default: mark at 62% on colors.iconBackground. |
icon-foreground.svg / .png |
no | Android adaptive icon foreground (keep art in the inner ~66% safe zone). Default: mark at 45% on transparent. |
splash.svg / splash.png |
no | Splash screen logo, shown on colors.splashBackground. Default: mark at 50% on transparent. |
wordmark.svg |
no | Wordmark lettering for downloads/lockup. Default: SVG <text> of the wordmark string. |
linkbanner.svg / .png |
no | 1200×630 OG/social card. Default: mark centered on colors.bannerBackground. |
brand.json #
{
"name": "Streamplace",
"wordmark": "stream.place",
"defaultSiteTitle": "My Streamplace Node",
"monochrome": false,
"colors": {
"ink": "#0A0A0B",
"paper": "#ffffff",
"iconBackground": "#ffffff",
"iconForeground": null,
"adaptiveIconBackground": "#111113",
"adaptiveIconForeground": null,
"splashBackground": "#ffffff",
"splashForeground": null,
"tileBackground": "#111113",
"tileForeground": "#ffffff",
"tileHairline": "rgba(255,255,255,0.10)",
"bannerBackground": null,
"bannerForeground": null
},
"story": null
}
All colors are optional; *Foreground colors default to ink and only
apply to monochrome marks (a multi-color mark renders as-is). wordmark is
the text rendered next to the mark in the app's lockup — a . in it gets
the accent treatment. defaultSiteTitle (default My <name> Node) is what
an unbranded node calls itself in the nav until its operator sets a runtime
siteTitle via the branding admin; a first-party brand points it at its own
wordmark so its nodes show the styled wordmark with no runtime config. story optionally carries the mark's design story for
the /brand guidelines screen (tagline, readings, geometry, specs,
usage — see BrandStory in the generated js/app/assets/generated/brand.ts);
sections without data are hidden.