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
Metadata
Release files for simple-module-branding 0.0.36
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| simple_module_branding-0.0.36.tar.gz | 67.9 kB | With provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| simple_module_branding-0.0.36-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 126.0 kB
Release files / simple_module_branding-0.0.36.tar.gz
| Download URL | simple_module_branding-0.0.36.tar.gz |
|---|---|
| Size | 67.9 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
dc9ed21ae942ee8956afbe04b9e56da8bfcef9ed30846355901699a6ad4accb4
|
|
BLAKE2b-256 checksum How to use checksums |
79f81516af16a8de91d75bc114bbefc488b4393e4dd8a2fe4045e41e7554f853
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 13, 2026.
Transparency logRelease files / simple_module_branding-0.0.36-py3-none-any.whl
| Download URL | simple_module_branding-0.0.36-py3-none-any.whl |
|---|---|
| Size | 58.1 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
c9e9bcba3fa3b9270876cbed37f4ac0b59c31ee3d7e2c05f632f95cd0b0c4f47
|
|
BLAKE2b-256 checksum How to use checksums |
1a47b5eb958ad97316def9161b1a622c0f60eddba33c68976f162999f5737b89
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 13, 2026.
Transparency logSecurity for simple-module-branding 0.0.36
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
Full provenance
All 2 files in this release have been uploaded with provenance metadata.
Attestation for simple_module_branding-0.0.36.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 13, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.