Skip to main content

branding

wootc is themeable so partners and enterprises can ship a branded migrator without forking the code. (For the built-in brand builds — TunaOS, Bluefin, Aurora, Bazzite — and how a build embeds a brand at compile time with real logo/typeface/theme assets, see branding-and-distribution.md; this page covers the runtime override, which also wins over an embedded brand.) Three override files, all read at runtime from C:\wootc\:

brand.json — look and copy​

Drop a brand.json next to the installer (or push it via MDM to C:\wootc\brand.json). Any field you omit falls back to the wootc default; you only specify what you want to change.

{
"name": "Acme Switch",
"tagline": "Move to Acme Linux in minutes.",
"logoEmoji": "🅰️",
"version": "2.0",
"accent": "#e6007a",
"accentText": "#ffffff",
"background": "#0d0b14",
"card": "#181320",
"text": "#f0e8f5",
"installVerb": "Migrate"
}
FieldEffect
nameTitle bar, window title
taglineLaunchpad subtitle, window title
logoEmojiTitle-bar logo
versionTitle-bar version tag
accent / accentTextPrimary buttons, selection, slider, focus ring (and its text color)
background / card / textCore palette (applied as CSS variables)
installVerbCTA + heading verb ("Install" → "Migrate", "Switch", …)
productNameThe installer's own name (title bar); name is the distribution being installed
logoDataUriReal logo (data URI) replacing the emoji everywhere a mark renders
fontFamily + fontDataUriBrand typeface, embedded as a data URI (woff2)
catalog / defaultImageRestrict + order the offered images by id; pre-select one
hideCustomImageRemove the custom-OCI field on every channel

The palette is applied as CSS custom properties at startup, so a single accent change re-skins every button, chip, and highlight consistently. See the branded screenshot in gui-walkthrough.md.

brand.css — deep restyle​

A C:\wootc\brand.css is injected after the app stylesheet (and after any embedded brand theme), so it can restyle components the tokens don't reach — button treatment, radii, gradients. The stylesheet is fully token-driven (--bg, --bg-card, --accent, --radius, …), so most re-skins need only brand.json; reach for brand.css when you want gradient buttons or pill shapes.

images.json — the variant catalog​

Replace the built-in TunaOS catalog with your own images. Same schema as the built-in list:

[
{
"id": "acme-desktop",
"name": "Acme Desktop",
"emoji": "🅰️",
"base": "Acme Linux 3",
"desktop": "gnome",
"desktopName": "GNOME",
"imageRef": "registry.acme.example/acme-desktop:latest",
"description": "Acme's supported desktop image."
}
]

imageRef is passed opaquely to the deployer — it is never inspected by the installer, so any OCI reference your provisioner understands works.

Locked-down single-image onramp​

A common case: a distro or product wants to offer their Linux as a one-click escape hatch from Windows — no variant picker, no choice, just "Leave Windows for FooOS." Ship an images.json with exactly one entry and the installer collapses to that single image:

[
{
"id": "fooos",
"name": "FooOS",
"emoji": "🦊",
"base": "FooOS 12",
"desktop": "gnome",
"desktopName": "",
"imageRef": "registry.foo.example/fooos:stable",
"description": "The whole of FooOS, installed from Windows."
}
]

With one image the variant grid still renders that single card pre-selected, so the user goes straight to credentials → Install. Pair it with a brand.json whose installVerb is "Switch" or "Install FooOS" and tagline names the destination, and the app reads end-to-end as a first-party FooOS onramp rather than a generic tool.

Recommended locked-down bundle for a single-project onramp:

FilePurpose
images.json (1 entry)pins the destination image; hides the choice
brand.jsonfirst-party name, logo, accent, "Switch to FooOS" verb
MDM/installer placementpush both to C:\wootc\ before first launch

Because imageRef is opaque and the boot chain is provisioner-agnostic (see architecture-boundary.md and non-bootc-adoption.md), a project shipping a non-bootc image can use the exact same locked-down onramp — only the provisioner stage differs.

What is NOT themeable (on purpose)​

The honesty rules are not configurable: the "nothing is deleted from Windows" reassurance, the per-app truthful session outcomes, and the BitLocker/data-safety warnings are part of the product's trust contract, not the skin. A branded migrator can look like anything; it cannot lie about what happens to the user's data.

Testing a brand​

Add a scenario to tests/gui/gui.spec.js with your brand object (see the branding test) — it renders your skin headlessly and screenshots it in ~1 second, no Windows needed.