Custom Themes
Custom Themes
Section titled “Custom Themes”MiniFy supports fully customizable themes. This guide walks you through creating your own theme from scratch, understanding the theme system, and sharing themes with the community.
Theme System Overview
Section titled “Theme System Overview”┌───────────────────────────────────────────────────────────────────────────────┐│ Theme System Architecture │├───────────────────────────────────────────────────────────────────────────────┤│ ││ ┌─────────────────────────────────────────────────────────────────────────┐ ││ │ Theme JSON File (RawTheme) │ ││ │ │ ││ │ { │ ││ │ "name": "Custom Theme", │ ││ │ "panel": { "background": "#1a1b26", "borderRadius": 18 }, │ ││ │ "controls": { "iconColor": "#fff", "iconColorActive": "#7aa2f7" }, │ ││ │ "playbar": { "trackBg": "...", "trackFill": "..." }, │ ││ │ "typography": { "songTitle": {...}, "songArtist": {...} }, │ ││ │ "settings": { "panelBg": "...", "accent": "..." } │ ││ │ } │ ││ │ │ ││ └─────────────────────────────────────────────────────────────────────────┘ ││ │ ││ ▼ ││ ┌─────────────────────────────────────────────────────────────────────────┐ ││ │ Theme Loader (themeLoader.ts) │ ││ │ │ ││ │ transformTheme(RawTheme) → ThemeConfig → applyThemeConfig() │ ││ │ │ ││ └─────────────────────────────────────────────────────────────────────────┘ ││ │ ││ ▼ ││ ┌─────────────────────────────────────────────────────────────────────────┐ ││ │ CSS Custom Properties │ ││ │ │ ││ │ :root { │ ││ │ --player-panel-bg: #1a1b26; │ ││ │ --player-controls-color: #c0caf5; │ ││ │ --player-playbar-track-fill: linear-gradient(...); │ ││ │ --settings-accent: #7aa2f7; │ ││ │ } │ ││ │ │ ││ └─────────────────────────────────────────────────────────────────────────┘ ││ │ ││ ▼ ││ ┌─────────────────────────────────────────────────────────────────────────┐ ││ │ UI Components │ ││ │ │ ││ │ Components use CSS variables for all colors │ ││ │ Theme changes are instant (no rebuild needed) │ ││ │ │ ││ └─────────────────────────────────────────────────────────────────────────┘ ││ │└────────────────────────────────────────────────────────────────────────────────┘Theme File Locations
Section titled “Theme File Locations”Built-in Themes
Section titled “Built-in Themes”Built-in themes are bundled with the application:
apps/desktop/src/themes/├── dark.json├── light.json├── catppuccin.json├── dracula.json├── chatgpt.json├── youtube.json├── bmw.json└── milka.jsonCustom Theme Locations
Section titled “Custom Theme Locations”Custom themes should be placed in the user themes directory:
%APPDATA%\MiniFy\themes\Example: C:\Users\YourName\AppData\Roaming\MiniFy\themes\
~/Library/Application Support/MiniFy/themes/~/.config/MiniFy/themes/Creating a Custom Theme
Section titled “Creating a Custom Theme”-
Create the themes directory
Terminal window New-Item -ItemType Directory -Force "$env:APPDATA\MiniFy\themes"Terminal window mkdir -p ~/Library/Application\ Support/MiniFy/themesTerminal window mkdir -p ~/.config/MiniFy/themes -
Create your theme file
Create a new JSON file (e.g.,
my-theme.json):{"name": "My Custom Theme","panel": {"background": "#1e1e2e","borderRadius": 18,"shadow": "0 14px 40px rgba(0,0,0,0.55)"},"controls": {"iconColor": "#cdd6f4","iconColorActive": "#89b4fa"},"playbar": {"trackBg": "rgba(205,214,244,0.16)","trackFill": "linear-gradient(90deg, #89b4fa 0%, #f5c2e7 100%)","thumbColor": "#cdd6f4","timeTextColor": "#6c7086"},"typography": {"songTitle": { "color": "#cdd6f4" },"songArtist": { "color": "#a6adc8" }},"actions": {"iconColor": "#cdd6f4","iconBackground": "rgba(205,214,244,0.04)","iconBackgroundHover": "rgba(205,214,244,0.10)"},"cover": {"borderColor": "rgba(205,214,244,0.20)","borderRadius": 12},"settings": {"panelBg": "#313244","panelBorder": "#45475a","text": "#cdd6f4","textMuted": "#6c7086","accent": "#89b4fa"}} -
Restart MiniFy
Close and reopen MiniFy to load the new theme.
-
Select your theme
Right-click → Themes → Select “My Custom Theme”
Theme Schema Reference
Section titled “Theme Schema Reference”Complete Schema
Section titled “Complete Schema”The theme system uses a component-based structure matching RawTheme in themeLoader.ts:
{ "name": "Theme Name", "panel": { "background": "#0a0a0a", "borderRadius": 18, "shadow": "0 14px 40px rgba(0,0,0,0.55)" }, "controls": { "iconColor": "#ffffff", "iconColorActive": "#1DB954" }, "playbar": { "trackBg": "rgba(255,255,255,0.16)", "trackFill": "linear-gradient(90deg, #1DB954 0%, #1ed760 100%)", "thumbColor": "#ffffff", "timeTextColor": "#b0b0b0" }, "typography": { "songTitle": { "color": "#ffffff" }, "songArtist": { "color": "#b0b0b0" } }, "actions": { "iconColor": "#ffffff", "iconBackground": "rgba(255,255,255,0.04)", "iconBackgroundHover": "rgba(255,255,255,0.10)" }, "cover": { "borderColor": "rgba(255,255,255,0.20)", "borderRadius": 12 }, "settings": { "panelBg": "#0a0a0a", "panelBorder": "#ffffff1a", "text": "#ffffff", "textMuted": "#b0b0b0", "headerText": "#ffffff", "itemHover": "#ffffff14", "itemActive": "#ffffff24", "accent": "#1DB954" }}Required Fields
Section titled “Required Fields”| Field | Type | Description |
|---|---|---|
name | string | Display name in theme menu (required) |
Component Properties
Section titled “Component Properties”| Property | Type | CSS Variable | Description |
|---|---|---|---|
panel.background | string | --player-panel-bg | Main panel background |
panel.borderRadius | number | --player-panel-radius | Panel corner radius (px) |
panel.shadow | string | --player-panel-shadow | Panel box shadow |
Controls
Section titled “Controls”| Property | Type | CSS Variable | Description |
|---|---|---|---|
controls.iconColor | string | --player-controls-color | Control icons color |
controls.iconColorActive | string | --player-controls-color-active | Active control color |
Playbar
Section titled “Playbar”| Property | Type | CSS Variable | Description |
|---|---|---|---|
playbar.trackBg | string | --player-playbar-track-bg | Progress track background |
playbar.trackFill | string | --player-playbar-track-fill | Progress fill (supports gradients) |
playbar.thumbColor | string | --player-playbar-thumb-color | Scrubber thumb color |
playbar.timeTextColor | string | --player-playbar-time-color | Time display color |
Typography
Section titled “Typography”| Property | Type | CSS Variable | Description |
|---|---|---|---|
typography.songTitle.color | string | --player-song-title-color | Song title color |
typography.songArtist.color | string | --player-song-artist-color | Artist name color |
Actions
Section titled “Actions”| Property | Type | CSS Variable | Description |
|---|---|---|---|
actions.iconColor | string | --player-actions-color | Action icons color |
actions.iconBackground | string | --player-actions-bg | Action button background |
actions.iconBackgroundHover | string | --player-actions-bg-hover | Action button hover state |
| Property | Type | CSS Variable | Description |
|---|---|---|---|
cover.borderColor | string | --player-cover-border-color | Album art border |
cover.borderRadius | number | --player-cover-radius | Album art corner radius (px) |
Settings Panel
Section titled “Settings Panel”| Property | Type | CSS Variable | Description |
|---|---|---|---|
settings.panelBg | string | --settings-panel-bg | Settings panel background |
settings.panelBorder | string | --settings-panel-border | Settings panel border |
settings.text | string | --settings-text | Primary text color |
settings.textMuted | string | --settings-text-muted | Muted text color |
settings.headerText | string | --settings-header-text | Section header color |
settings.itemHover | string | --settings-item-hover | List item hover state |
settings.itemActive | string | --settings-item-active | List item active state |
settings.accent | string | --settings-accent | Accent color for toggles |
Color Formats
Section titled “Color Formats”Colors can be specified in multiple formats:
{ "panel": { "background": "#0a0a0a" }, "controls": { "iconColor": "rgb(255, 255, 255)", "iconColorActive": "hsl(142, 76%, 36%)" }, "playbar": { "trackFill": "linear-gradient(90deg, #1DB954 0%, #1ed760 100%)", "trackBg": "rgba(255, 255, 255, 0.16)" }}Example Themes
Section titled “Example Themes”A popular theme with cool, arctic colors:
{ "name": "Nord", "panel": { "background": "#2e3440", "borderRadius": 18, "shadow": "0 14px 40px rgba(0,0,0,0.55)" }, "controls": { "iconColor": "#eceff4", "iconColorActive": "#88c0d0" }, "playbar": { "trackBg": "rgba(236,239,244,0.16)", "trackFill": "linear-gradient(90deg, #88c0d0 0%, #81a1c1 100%)", "thumbColor": "#eceff4", "timeTextColor": "#4c566a" }, "typography": { "songTitle": { "color": "#eceff4" }, "songArtist": { "color": "#d8dee9" } }, "actions": { "iconColor": "#eceff4", "iconBackground": "rgba(236,239,244,0.04)", "iconBackgroundHover": "rgba(236,239,244,0.10)" }, "cover": { "borderColor": "rgba(236,239,244,0.20)", "borderRadius": 12 }, "settings": { "panelBg": "#3b4252", "panelBorder": "#434c5e", "text": "#eceff4", "textMuted": "#d8dee9", "accent": "#88c0d0" }}Tokyo Night
Section titled “Tokyo Night”A popular developer theme:
{ "name": "Tokyo Night", "panel": { "background": "#1a1b26", "borderRadius": 18, "shadow": "0 14px 40px rgba(0,0,0,0.65)" }, "controls": { "iconColor": "#c0caf5", "iconColorActive": "#7aa2f7" }, "playbar": { "trackBg": "rgba(192,202,245,0.12)", "trackFill": "linear-gradient(90deg, #7aa2f7 0%, #bb9af7 100%)", "thumbColor": "#c0caf5", "timeTextColor": "#565f89" }, "typography": { "songTitle": { "color": "#c0caf5" }, "songArtist": { "color": "#9aa5ce" } }, "actions": { "iconColor": "#c0caf5", "iconBackground": "rgba(192,202,245,0.04)", "iconBackgroundHover": "rgba(192,202,245,0.10)" }, "cover": { "borderColor": "rgba(192,202,245,0.20)", "borderRadius": 12 }, "settings": { "panelBg": "#24283b", "panelBorder": "#414868", "text": "#c0caf5", "textMuted": "#565f89", "accent": "#7aa2f7" }}Gruvbox Dark
Section titled “Gruvbox Dark”A retro groove theme:
{ "name": "Gruvbox Dark", "panel": { "background": "#282828", "borderRadius": 18, "shadow": "0 14px 40px rgba(0,0,0,0.55)" }, "controls": { "iconColor": "#ebdbb2", "iconColorActive": "#b8bb26" }, "playbar": { "trackBg": "rgba(235,219,178,0.16)", "trackFill": "linear-gradient(90deg, #b8bb26 0%, #fabd2f 100%)", "thumbColor": "#ebdbb2", "timeTextColor": "#928374" }, "typography": { "songTitle": { "color": "#ebdbb2" }, "songArtist": { "color": "#d5c4a1" } }, "actions": { "iconColor": "#ebdbb2", "iconBackground": "rgba(235,219,178,0.04)", "iconBackgroundHover": "rgba(235,219,178,0.10)" }, "cover": { "borderColor": "rgba(235,219,178,0.20)", "borderRadius": 12 }, "settings": { "panelBg": "#3c3836", "panelBorder": "#504945", "text": "#ebdbb2", "textMuted": "#928374", "accent": "#b8bb26" }}Solarized Dark
Section titled “Solarized Dark”The classic Solarized theme:
{ "name": "Solarized Dark", "panel": { "background": "#002b36", "borderRadius": 18, "shadow": "0 14px 40px rgba(0,0,0,0.55)" }, "controls": { "iconColor": "#839496", "iconColorActive": "#268bd2" }, "playbar": { "trackBg": "rgba(131,148,150,0.16)", "trackFill": "linear-gradient(90deg, #268bd2 0%, #2aa198 100%)", "thumbColor": "#839496", "timeTextColor": "#586e75" }, "typography": { "songTitle": { "color": "#93a1a1" }, "songArtist": { "color": "#839496" } }, "actions": { "iconColor": "#839496", "iconBackground": "rgba(131,148,150,0.04)", "iconBackgroundHover": "rgba(131,148,150,0.10)" }, "cover": { "borderColor": "rgba(131,148,150,0.20)", "borderRadius": 12 }, "settings": { "panelBg": "#073642", "panelBorder": "#073642", "text": "#839496", "textMuted": "#586e75", "accent": "#268bd2" }}Color Design Tips
Section titled “Color Design Tips”Contrast Guidelines
Section titled “Contrast Guidelines”┌───────────────────────────────────────────────────────────────────────────────┐│ Contrast Recommendations │├───────────────────────────────────────────────────────────────────────────────┤│ ││ Background vs Foreground: ││ ───────────────────────── ││ • Minimum contrast ratio: 4.5:1 (WCAG AA) ││ • Recommended: 7:1 or higher (WCAG AAA) ││ ││ Primary vs Background: ││ ────────────────────── ││ • Should be visually distinct ││ • Used for interactive elements ││ ││ Muted vs Background: ││ ──────────────────── ││ • Should be readable but subdued ││ • Minimum contrast ratio: 3:1 ││ │└────────────────────────────────────────────────────────────────────────────────┘Color Palette Tools
Section titled “Color Palette Tools”- Coolors - Color scheme generator
- ColorHunt - Curated color palettes
- Contrast Checker - WCAG contrast tool
- Paletton - Color wheel and harmonies
Theme Testing Checklist
Section titled “Theme Testing Checklist”Before sharing your theme, test these scenarios:
- Track title is readable
- Artist name is readable but subdued
- Progress bar is clearly visible
- Buttons have clear hover states
- Active/inactive states are distinguishable
- Works in all three layouts
- Settings page is usable
- AI DJ chat is readable
Managing Themes
Section titled “Managing Themes”In-App Theme Management
Section titled “In-App Theme Management”- Right-click → Settings → Themes
- View all themes (built-in + custom)
- Delete custom themes
- Export themes for sharing
Theme Validation
Section titled “Theme Validation”MiniFy validates themes on load. Invalid themes are skipped with a console warning.
Common validation errors:
- Missing
namefield - Missing
colorsobject - Invalid color format
- JSON syntax errors
Debugging Theme Issues
Section titled “Debugging Theme Issues”Open DevTools (F12) to see theme loading errors:
// Console output[Theme] Loading theme: my-theme.json[Theme] Error: Invalid color format for 'primary'Sharing Themes
Section titled “Sharing Themes”Contributing to MiniFy
Section titled “Contributing to MiniFy”-
Fork the repository
-
Add your theme
Add the JSON file to
apps/desktop/src/themes/ -
Add to website preview
Also add to
apps/www/public/themes/ -
Update theme index
Add your theme to
apps/www/public/themes/index.json -
Submit a Pull Request
Include screenshots showing your theme in use
Sharing Externally
Section titled “Sharing Externally”You can share theme JSON files directly:
- Export your theme from Settings
- Share the JSON file
- Recipients place it in their themes directory
Advanced Customization
Section titled “Advanced Customization”CSS Variable Reference
Section titled “CSS Variable Reference”All theme properties are mapped to CSS custom properties:
:root { /* Panel */ --player-panel-bg: #0a0a0a; --player-panel-radius: 18px; --player-panel-shadow: 0 14px 40px rgba(0,0,0,0.55);
/* Controls */ --player-controls-color: #ffffff; --player-controls-color-active: #1DB954;
/* Playbar */ --player-playbar-track-bg: rgba(255,255,255,0.16); --player-playbar-track-fill: linear-gradient(90deg, #1DB954 0%, #1ed760 100%); --player-playbar-thumb-color: #ffffff; --player-playbar-time-color: #b0b0b0;
/* Typography */ --player-song-title-color: #ffffff; --player-song-artist-color: #b0b0b0;
/* Actions */ --player-actions-color: #ffffff; --player-actions-bg: rgba(255,255,255,0.04); --player-actions-bg-hover: rgba(255,255,255,0.10);
/* Cover */ --player-cover-border-color: rgba(255,255,255,0.20); --player-cover-radius: 12px;
/* Settings Panel */ --settings-panel-bg: #0a0a0a; --settings-panel-border: #ffffff1a; --settings-text: #ffffff; --settings-text-muted: #b0b0b0; --settings-header-text: #ffffff; --settings-item-hover: #ffffff14; --settings-item-active: #ffffff24; --settings-accent: #1DB954;}Troubleshooting
Section titled “Troubleshooting”Theme not appearing in menu
Section titled “Theme not appearing in menu”- Check the file is valid JSON
- Verify it’s in the correct directory
- Ensure the file has a
.jsonextension - Restart MiniFy
Colors not applying correctly
Section titled “Colors not applying correctly”- Check for typos in color property names
- Verify color format is valid
- Check DevTools console for errors
Theme causes rendering issues
Section titled “Theme causes rendering issues”- Ensure adequate contrast between colors
- Test all UI elements
- Try adjusting problematic colors