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 |
|---|---|---|
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
- Sign in as an admin and open Branding in the sidebar (or visit
/branding). - 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
settingsmodule's store (SYSTEM scope) — there is no branding database table. They hydrate intoapp.state.branding.settingsat boot and hot-swap on save. - Images. Logo and favicon uploads are stored through the
file_storagemodule (referenced by UUID). Branding serves them back from its own anonymous routes,GET /api/branding/logoandGET /api/branding/favicon—file_storage's download endpoint requiresfile-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 behindbranding.manage(the exemption is GET-only). - Caching. The published URL carries
?v=<file id>; a replaced image is a newfile_storageid, so the URL is content-addressed. Versioned requests are servedpublic, max-age=31536000, immutable; a request without a usable version getspublic, max-age=3600so 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, andBrandingPresetrejects 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: nulland the frontend falls back tologoUrl, 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.pngis 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
brandingblock 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-primaryCSS 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
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
2568f51f646aa0744d4cd76df2d09277b2caa7134af52c6dda5f619f5af2580c
|
|
| MD5 |
ac83a43c9dc1d10907104cbaf06175d9
|
|
| BLAKE2b-256 |
257bc9f1a9341efd5685f361029b1e9a56314d99e1053eb63c93f1f011ec82a5
|
Provenance
The following attestation bundles were made for simple_module_branding-0.0.33.tar.gz:
Publisher:
release.yml on antosubash/simple_module_python
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
simple_module_branding-0.0.33.tar.gz -
Subject digest:
2568f51f646aa0744d4cd76df2d09277b2caa7134af52c6dda5f619f5af2580c - Sigstore transparency entry: 2709006573
- Sigstore integration time:
-
Permalink:
antosubash/simple_module_python@f7740e40ce8f3568e742385e6a64b688b59c0b99 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/antosubash
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@f7740e40ce8f3568e742385e6a64b688b59c0b99 -
Trigger Event:
workflow_dispatch
-
Statement type:
File details
Details for the file simple_module_branding-0.0.33-py3-none-any.whl.
File metadata
- Download URL: simple_module_branding-0.0.33-py3-none-any.whl
- Upload date:
- Size: 44.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
30cdb1cd689e029151804e1c47e4ba12b05d32feeb5dbe14c3838fe371ebdb4a
|
|
| MD5 |
f1b9baa3443667a24150bcde93a4d3f6
|
|
| BLAKE2b-256 |
e04810c859278b3f9ebd747e76e72acde4054ef9c0054ae0b200a4c7d21e141d
|
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
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
simple_module_branding-0.0.33-py3-none-any.whl -
Subject digest:
30cdb1cd689e029151804e1c47e4ba12b05d32feeb5dbe14c3838fe371ebdb4a - Sigstore transparency entry: 2709007070
- Sigstore integration time:
-
Permalink:
antosubash/simple_module_python@f7740e40ce8f3568e742385e6a64b688b59c0b99 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/antosubash
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@f7740e40ce8f3568e742385e6a64b688b59c0b99 -
Trigger Event:
workflow_dispatch
-
Statement type: