Theming Melodee
Melodee includes built-in Light and Dark themes and can load custom CSS theme
packs from its Themes library. Administrators manage packs and the system
default under Administration > Themes. Users select an available theme in
Account > Profile; the preference is stored on the user and applied through
the melodee_ui_theme cookie.
If no system/user selection can be resolved, Melodee falls back to Dark.
Themes Library
A new 2.2.0 database seeds the Themes library at /app/themes/. The published
Compose file mounts the melodee_themes volume there. If an installation uses a
different library path, the database library record is authoritative.
The two built-in themes do not need this directory. Custom themes are discovered one directory below it:
/app/themes/
└── my-theme/
├── theme.json
├── theme.css
└── optional-assets...
The administration page can create a missing Themes library, but its suggested
path may not match a container’s mounts. Use /app/themes/ for the repository’s
Compose deployment unless you intentionally added another mount.
Import and Administration
Administration > Themes can rescan, import, export, and delete custom
themes, show validation warnings, and set system.defaultTheme. Built-in Light
and Dark cannot be deleted.
For UI import, the ZIP root must directly contain theme.json and theme.css:
my-theme.zip
├── theme.json
├── theme.css
└── preview.png
A ZIP whose only root entry is a my-theme/ directory does not match the 2.2.0
importer’s expected layout. Either repackage that directory’s contents at the
ZIP root or extract its top-level theme directory directly beneath the Themes
library and rescan. The ready-made archives in the repository use a top-level
directory and are therefore suited to direct extraction unless repackaged.
Pre-built packs are in the repository themes directory. The published set includes Melodee Light, Melodee Dark, Forest, Midnight Galaxy, Ocean Breeze, Sunset Vibes, and Synthwave.
The service defaults to a 50 MiB maximum archive size when
theme.maxUploadSizeMb is absent or nonpositive. The browser accepts at most
100 MiB, but the smaller service limit still wins. Create the database setting
to choose a lower operational limit.
theme.json
Only id and name are required by the metadata parser; theme.css is also a
required file. A complete example is:
{
"id": "my-theme",
"name": "My Theme",
"description": "A custom dark theme.",
"author": "Example Admin",
"version": "1.0.0",
"baseTheme": "dark",
"previewImage": "preview.png",
"branding": {
"logoImage": "logo.png",
"favicon": "favicon.ico"
},
"fonts": {
"base": "Inter, sans-serif",
"heading": "Inter, sans-serif",
"mono": "monospace"
},
"navMenu": {
"hidden": ["jukebox", "party"]
}
}
baseTheme selects Radzen’s dark base only when its value is exactly dark;
all other values use the default/light base. Relative preview, logo, favicon,
font, CSS, and image files are served through the theme API.
Navigation IDs currently rendered by the main menu include dashboard,
stats, artists, albums, charts, libraries, nowplaying, jukebox,
party, playlists, podcasts, radiostations, requests, songs, shares,
users, admin, themes, and about.
Hiding navigation is visual customization only. It does not revoke a role, protect a route, or replace server-side authorization.
theme.css
Custom CSS loads after the chosen Radzen base. A valid imported pack must define these Melodee tokens:
:root {
--md-surface-0: #121212;
--md-surface-1: #1e1e1e;
--md-surface-2: #2c2c2c;
--md-text-1: #ffffff;
--md-text-2: #c7c7c7;
--md-text-inverse: #000000;
--md-muted: #8a8a8a;
--md-border: #3a3a3a;
--md-divider: #303030;
--md-primary: #9c6ade;
--md-primary-contrast: #ffffff;
--md-accent: #42c7b9;
--md-accent-contrast: #000000;
--md-focus: #ffffff;
--md-success: #43a047;
--md-warning: #f9a825;
--md-error: #e53935;
--md-info: #1e88e5;
}
Table, chip, and font-family --md-* tokens are optional in 2.2.0. Radzen
--rz-* variables and component selectors can be overridden after the required
tokens to customize parts of the UI that do not yet consume Melodee tokens.
Validation and Contrast
Import rejects a missing/invalid theme.json, missing theme.css, missing
required tokens, a duplicate theme ID, an oversized archive, or ZIP path
traversal. Theme discovery records warnings for packs placed directly on disk.
Melodee checks a 4.5:1 contrast ratio for:
--md-text-1on each of--md-surface-0and--md-surface-1;--md-text-inverseon--md-primary.
With theme.enforceContrastValidation=true, an insufficient tested ratio makes
an import invalid. Otherwise it is a warning. Contrast parsing understands
hexadecimal and rgb(r,g,b) colors; variables, named colors, and more complex
CSS values may not validate as intended.
Security and Recovery
Import only trusted packs. Custom CSS and publicly served SVG/font/image assets can alter what users see, make remote requests, obscure controls, and imitate application UI. Review every file before import. Restrict filesystem write access to administrators, and do not place credentials or private URLs in CSS or metadata.
Theme assets are customization, not executable JavaScript: .js and arbitrary
file types are not served by the theme file endpoint. A theme cannot grant
permissions by revealing a hidden menu item.
Back up the melodee_themes volume. If a theme breaks the UI, remove the
melodee_ui_theme cookie or select dark, set system.defaultTheme to dark,
and then remove or correct the custom theme directory. See Backup and
Restore for volume backup procedures.
Feedback
Was this page helpful?
Glad to hear it! Please tell us how we can improve.
Sorry to hear that. Please tell us how we can improve.