Skip to main content

simple_module_branding

Customisable application branding for simple_module_python apps.

An administrator can set the application name, logo (plus an optional dark-background variant), favicon and primary brand colour from the admin UI (/branding), and those values are applied everywhere the framework would otherwise show the default identity — the sidebar/header logo and name, the browser tab title, the favicon, and the primary accent colour.

Screenshots

The admin page at /branding, and the same app rebranded as "Acme Analytics" (custom logo + name + primary colour) across the sidebar and dashboard:

Admin page Branding applied Across the app
Branding admin Branding applied Branded dashboard

Install

The module ships with the default app. To add it to a custom host, declare it as a dependency and let entry-point discovery pick it up:

# host/pyproject.toml
dependencies = ["simple_module_branding"]

[tool.uv.sources]
simple_module_branding = { workspace = true }

Then uv sync --all-packages. It requires the Settings and FileStorage modules to be installed too.

Usage

  1. Sign in as an admin and open Branding in the sidebar (or visit /branding).
  2. Set the application name, pick a primary colour, and upload a logo and/or favicon. Changes apply immediately across the app.

Programmatically, the current branding is available on every page through the branding Inertia shared prop (appName, primaryColor, designPack, logoUrl, logoDarkUrl, faviconUrl, banner). banner is null when unconfigured, which makes the frontend render nothing. Footer content is owned by the framework site layouts rather than branding. For a dark surface use darkSurfaceLogo(branding) from @simple-module-py/ui/lib/brand, which applies the logoDarkUrl → logoUrl fallback in one place.

How it works

  • Storage. The four values are persisted via the settings module's store (SYSTEM scope) — there is no branding database table. They hydrate into app.state.branding.settings at boot and hot-swap on save.
  • Images. Logo and favicon uploads are stored through the file_storage module (referenced by UUID). Branding serves them back from its own anonymous routes, GET /api/branding/logo and GET /api/branding/faviconfile_storage's download endpoint requires file-storage.download, which no logged-out visitor has, and the sign-in page is exactly where the logo must appear. Only the two ids currently held in branding settings are served, so this is not a way to read arbitrary files. Uploading and clearing on those same paths stay behind branding.manage (the exemption is GET-only).
  • Caching. The published URL carries ?v=<file id>; a replaced image is a new file_storage id, so the URL is content-addressed. Versioned requests are served public, max-age=31536000, immutable; a request without a usable version gets public, max-age=3600 so it self-corrects, and a 404 is never cached.
  • Announcement banner. A message plus a severity (info / warning / danger) rendered above every shell — app, public and auth — because an outage notice is most useful to people who cannot sign in. An empty message hides it. Severity colours are semantic, not brand-tinted: a warning wearing the deployment's accent colour stops reading as a warning.
  • Presets. One-click looks (POST /api/branding/presets/{key}), applied through the ordinary update path so every validator still runs. A preset only ever sets appearance (PRESET_FIELDS — primary colour, design pack); it can never overwrite the app name, an uploaded logo or a live banner, and BrandingPreset rejects any other field at construction.
  • Dark-background logo. The sidebar and mobile bar sit on a near-black surface in every theme, while the sign-in card and public page are light — so a single logo cannot read on both. Uploading a Logo (dark backgrounds) variant swaps it in on those surfaces only. It is optional: with none set the shared prop reports logoDarkUrl: null and the frontend falls back to logoUrl, so single-logo deployments look exactly as they did.
  • Lifecycle. Replacing or clearing an image deletes the file it stopped referencing, so repeated logo tweaks don't leave orphans in file_storage. Cleanup is best effort: the setting change has already been persisted, so a storage fault is logged rather than failing an otherwise-successful rebrand.
  • Upload validation. PNG, JPEG, WEBP, GIF and ICO up to 2 MB. The declared content-type is caller-controlled, so the first bytes are also checked against each format's magic number — a payload renamed logo.png is rejected. SVG is excluded on purpose: it is an XML document that can carry <script>, so serving one from the app's origin would be stored XSS.
  • Delivery. A registered Inertia shared-props provider injects a branding block into every page's shared props (authenticated and guest), which the frontend reads for the name, logo, favicon and colour.

Permissions

  • branding.view — view the branding admin page.
  • branding.manage — change branding (name, colour, logo, favicon).

The logo and favicon GET routes are anonymous by design (registered through the register_public_routes hook); everything else requires a permission.

Dependencies

Depends on the Settings and FileStorage modules.

Notes

  • Branding is currently SYSTEM-scoped (one identity per deployment). The settings store already supports tenant/user scope, leaving room for per-tenant branding later.
  • The primary colour overrides the --primary / --sidebar-primary CSS variables from a single hex; the full OKLCH colour scale is not regenerated.

License: MIT

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

simple_module_branding-0.0.33.tar.gz (46.4 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

simple_module_branding-0.0.33-py3-none-any.whl (44.3 kB view details)

Uploaded Python 3

File details

Details for the file simple_module_branding-0.0.33.tar.gz.

File metadata

  • Download URL: simple_module_branding-0.0.33.tar.gz
  • Upload date:
  • Size: 46.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for simple_module_branding-0.0.33.tar.gz
Algorithm Hash digest
SHA256 2568f51f646aa0744d4cd76df2d09277b2caa7134af52c6dda5f619f5af2580c
MD5 ac83a43c9dc1d10907104cbaf06175d9
BLAKE2b-256 257bc9f1a9341efd5685f361029b1e9a56314d99e1053eb63c93f1f011ec82a5

See more details on using hashes here.

Provenance

The following attestation bundles were made for simple_module_branding-0.0.33.tar.gz:

Publisher: release.yml on antosubash/simple_module_python

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file simple_module_branding-0.0.33-py3-none-any.whl.

File metadata

File hashes

Hashes for simple_module_branding-0.0.33-py3-none-any.whl
Algorithm Hash digest
SHA256 30cdb1cd689e029151804e1c47e4ba12b05d32feeb5dbe14c3838fe371ebdb4a
MD5 f1b9baa3443667a24150bcde93a4d3f6
BLAKE2b-256 e04810c859278b3f9ebd747e76e72acde4054ef9c0054ae0b200a4c7d21e141d

See more details on using hashes here.

Provenance

The following attestation bundles were made for simple_module_branding-0.0.33-py3-none-any.whl:

Publisher: release.yml on antosubash/simple_module_python

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Release history Release notifications | RSS feed

This release

0.0.33 This release

2 files

0.0.32

2 files

0.0.31

2 files

0.0.30

2 files

0.0.29

2 files

0.0.28

2 files

0.0.27

2 files

0.0.26

2 files

0.0.25

2 files

0.0.24

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page