Skip to content

Branding

Michael Dohmen edited this page Aug 13, 2026 · 2 revisions

Branding

Product name, logo and five colours are editable in Settings → Branding / Colours and travel with the file — open a file someone else branded, and you see their colours and logo, not the defaults.

The five colours

Setting Role
Accent Everything active: primary buttons, filters, links. Lighter and darker shades are derived from it automatically.
Header bar Top bar, table head, side panel. A live contrast ratio against white text is shown next to it — below 4.5:1 it's flagged as hard to read.
Attention Overdue items and the "waiting" status.
Done Completed items.
Unsaved The dot in the file bar while changes are pending.

Shades (lighter/darker variants, soft backgrounds) are computed from these five via simple white/black mixing (shade() in src/lib/color.js) rather than requiring you to pick eight colour fields and risk an inconsistent palette. A "reset colours" button restores the shipped defaults.

The default palette (DEFAULT_COLORS in src/app.jsx):

{ accent: '#0e7c86', band: '#16202b', flag: '#c2521b', ok: '#2e7d5b', pending: '#d19a0a' }

Product name and logo

  • Product name appears in the header, on the lock screen, and as the watermark text when no logo is uploaded. Without a logo, it's split visually at the first capital letter — "open" in a lighter weight, "Toolbox" bold — a reasonable default for CamelCase product names that needs no image at all.
  • Logo: upload an SVG. It's embedded directly into the saved HTML (so it travels with the file, no external image reference) and replaces the wordmark at four places at once: header, lock screen, settings-page footer, and the watermark.

The SVG sanitiser

Any uploaded logo goes through src/lib/svg.js before it's stored, since the output file gets passed around and a malicious SVG is a real vector for stored XSS in exactly this kind of "open someone else's file" workflow:

  • <script> tags are stripped entirely.
  • Event-handler attributes (onclick, onload, and every other on…) are removed.
  • <foreignObject> — which can embed arbitrary HTML inside an SVG — is stripped.
  • External references (xlink:href, href pointing off-document) are removed, keeping the file's no-network-at-runtime guarantee intact even for a logo someone uploaded later.

You're told exactly what was removed, not just that "something changed."

The project's own logo

docs/logo.svg — a document-with-padlock mark in the app's own teal palette — is the framework's own default (DEFAULT_BRAND.logo in src/app.jsx), shown in the header, lock screen, watermark and settings footer out of the box, like any starter template's placeholder branding. It's fully replaceable per tool via Settings → Branding, exactly like any other logo.

It's also:

  • the built file's favicon, embedded as a base64 data: URI in index.html — no external request, consistent with the single-file/no-network rule
  • the header image at the top of the project's README

Links in the shipped UI

Two small, intentional links live in the interface:

  • The watermark (bottom-right corner) links to the GitHub repository, opening in a new tab.
  • The copyright line in the Settings footer links "M. Dohmen" to the maintainer's LinkedIn profile.

Both are ordinary <a> links you're free to remove or change if you're shipping a rebranded fork — nothing about them is required for the app to function, and neither makes a network request on its own (they're just links; nothing loads until someone clicks).

Exporting and reusing a configuration

Settings → Configuration → Save writes everything on the settings page as JSON — colours, branding, AI endpoint configuration, interface language — without records and without the API key. The intent is carrying a working setup over to the next tool you build from this template, without dragging that tool's business data along. Load applies a saved configuration back, validating it field by field: anything not defined in the current settings shape is dropped and named in the resulting notice, and a value of the wrong type falls back to the default rather than being accepted as-is — so a hand-edited or slightly-stale config file degrades safely instead of corrupting settings.

Clone this wiki locally