Complete guide to themes, custom styling, and professional appearance
The Bravura SDK provides a comprehensive theming system that makes it easy to create professional, consistent user interfaces. This guide covers everything from basic theme application to advanced custom theme creation.
---
---
from bravura.themes.theme_manager import ProfessionalThemeManager
# Initialize theme manager
theme_manager = ProfessionalThemeManager(app_instance)
# Apply a professional theme
theme_manager.apply_theme("wigley_site")
That's it! All your ttk widgets are now professionally styled.
✅ Automatic styling for all ttk widgets (Frame, Label, Button, Entry, etc.)
✅ Color coordination across your entire application
✅ Professional animations and hover effects
✅ Accessibility compliance (text contrast, focus indicators)
✅ Dark/light mode support
✅ Custom branding (Enterprise tier)
---
| Theme ID | Description | Best For |
|---|---|---|
| `"default"` | Clean light theme | General applications |
| `"dark"` | Professional dark theme | Night-time use |
| `"black"` | Deep black theme | Media applications |
| `"white"` | Pure white theme | Clean, minimal designs |
| `"blue_deep"` | Deep blue accent | Professional software |
| `"pink_professional"` | Pink accent theme | Creative applications |
| Theme ID | Description | Features |
|---|---|---|
| `"wigley_site"` | Premium teal/gold | Ambient backgrounds, enhanced animations |
| `"platinum"` | Enterprise platinum | Glass effects, premium components |
| `"ocean_blue"` | Ocean blue theme | Professional blue palette |
| Theme ID | Description | Unique Features |
|---|---|---|
| `"custom"` | Custom branding | Full color customization, company branding |
| `"executive"` | Executive theme | Premium gradients, advanced effects |
Run the theme showcase demo:
python demo_themes.py
---
class MyApp:
def __init__(self):
self.root = tk.Tk()
# Initialize ThemeManager FIRST
self.theme_manager = ProfessionalThemeManager(self)
# Apply theme BEFORE creating widgets
self.theme_manager.apply_theme("wigley_site")
# Now create your UI
self.create_ui()
def change_theme(self, theme_name):
# Apply new theme
self.theme_manager.apply_theme(theme_name)
# Recreate ambient backgrounds if using them
if hasattr(self, 'ambient_bg'):
self.ambient_bg.destroy()
self.ambient_bg = AmbientBackground(
self.root,
theme_tokens=self.theme_manager.get_current_theme_colors()
)
# Get current theme name
current_theme = theme_manager.get_current_theme_name()
# Get current theme colors
theme_colors = theme_manager.get_current_theme_colors()
# List all available themes
available_themes = theme_manager.get_available_themes()
---
from bravura.themes.theme_manager import PROFESSIONAL_THEMES
# Create custom theme
PROFESSIONAL_THEMES["my_theme"] = {
"name": "My Custom Theme",
"description": "Custom theme for my application",
# Color palette
"background_primary": "#1a1a1a",
"background_secondary": "#2a2a2a",
"text_main": "#ffffff",
"text_secondary": "#cccccc",
"accent_color_1": "#007acc",
"accent_color_2": "#00cc44",
# Component colors
"button_bg": "#007acc",
"button_fg": "#ffffff",
"border_color": "#007acc"
}
# Apply custom theme
theme_manager.apply_theme("my_theme")
PROFESSIONAL_THEMES["corporate"] = {
# Metadata
"name": "Corporate Theme",
"description": "Professional corporate branding",
"tier": "enterprise",
# Core colors
"background_primary": "#ffffff",
"background_secondary": "#f8f9fa",
"text_main": "#212529",
"text_secondary": "#6c757d",
"border_color": "#dee2e6",
# Accent colors
"accent_color_1": "#0066cc", # Primary brand color
"accent_color_2": "#28a745", # Secondary accent
# Button styles
"button_bg": "#0066cc",
"button_fg": "#ffffff",
"hover_bg": "#0052a3",
"active_bg": "#004080",
# Progress bars
"progress_bar_fg": "#0066cc",
"overall_progress_fg": "#28a745",
# Special effects
"glass_effect": "rgba(255, 255, 255, 0.1)",
"glow_color": "rgba(0, 102, 204, 0.3)"
}
def create_company_theme(primary_color="#FF6B00", secondary_color="#00C4FF"):
"""Create a theme branded for your company."""
PROFESSIONAL_THEMES["company_brand"] = {
"name": "Company Brand Theme",
"description": f"Custom theme with {primary_color} and {secondary_color}",
# Use company colors
"accent_color_1": primary_color,
"accent_color_2": secondary_color,
"button_bg": primary_color,
"border_color": primary_color,
"log_fg": secondary_color,
# Standard backgrounds
"background_primary": "#1a1a1a",
"background_secondary": "#2a2a2a",
"text_main": "#ffffff",
"text_secondary": "#cccccc"
}
return "company_brand"
# Use company colors
theme_name = create_company_theme("#8B5CF6", "#F59E0B") # Purple and amber
theme_manager.apply_theme(theme_name)
---
All themes provide these standard tokens:
theme_colors = theme_manager.get_current_theme_colors()
# Background colors
bg_primary = theme_colors["background_primary"] # Main window background
bg_secondary = theme_colors["background_secondary"] # Secondary surfaces
# Text colors
text_main = theme_colors["text_main"] # Primary text
text_secondary = theme_colors["text_secondary"] # Secondary text/labels
# Accent colors
accent_1 = theme_colors["accent_color_1"] # Primary accent
accent_2 = theme_colors["accent_color_2"] # Secondary accent
# Component colors
button_bg = theme_colors["button_bg"] # Button background
button_fg = theme_colors["button_fg"] # Button text
border = theme_colors["border_color"] # Borders and dividers
# Instead of hardcoded colors, use theme tokens
def create_custom_component(self, parent):
# Get current theme colors
colors = self.theme_manager.get_current_theme_colors()
# Use theme colors for consistency
frame = tk.Frame(
parent,
bg=colors["background_secondary"],
highlightbackground=colors["border_color"],
highlightthickness=1
)
label = tk.Label(
frame,
text="Custom Component",
bg=colors["background_secondary"],
fg=colors["text_main"]
)
Since tk widgets don't use ttk.Style, you must style them manually:
def style_text_widget(text_widget, theme_colors):
"""Apply theme colors to a tk.Text widget."""
text_widget.configure(
bg=theme_colors["background_secondary"],
fg=theme_colors["text_main"],
insertbackground=theme_colors["text_main"], # Cursor color
selectbackground=theme_colors["accent_color_1"], # Selection
selectforeground="#ffffff"
)
# Usage
text_area = tk.Text(parent)
style_text_widget(text_area, theme_manager.get_current_theme_colors())
---
from bravura.components import PremiumButton
# Button automatically uses theme colors
button = PremiumButton(
parent,
text="Save",
command=save_action,
style="primary", # Uses theme's button_bg color
theme_tokens=theme_manager.get_current_theme_colors()
)
# Progress bars automatically use theme colors
progress = ttk.Progressbar(
parent,
mode="determinate",
style="TProgressbar" # ThemeManager styles this automatically
)
from bravura.components import create_toast_center
# Toast notifications automatically use theme colors
theme_colors = theme_manager.get_current_theme_colors()
toast_center = create_toast_center(root, theme_colors)
# All toast types adapt to current theme
toast_center.show("Info message", kind="info") # Uses accent_color_1
toast_center.show("Success!", kind="success") # Uses success_color
toast_center.show("Warning", kind="warning") # Uses warning_color
toast_center.show("Error", kind="error") # Uses error_color
# Background, text, borders, and shadows all use theme colors
class ThemedFrame(ttk.Frame):
"""A frame that adapts to theme changes."""
def __init__(self, parent, theme_manager, **kwargs):
super().__init__(parent, **kwargs)
self.theme_manager = theme_manager
self.apply_theme()
def apply_theme(self):
"""Apply current theme colors."""
colors = self.theme_manager.get_current_theme_colors()
# Apply colors to custom styling
self.configure(style="Custom.TFrame")
---
class ThemeSwitchingApp:
def __init__(self):
self.root = tk.Tk()
self.theme_manager = ProfessionalThemeManager(self)
self.current_theme = "dark"
# Create theme switcher
self.create_theme_menu()
self.apply_current_theme()
def create_theme_menu(self):
"""Create menu for theme switching."""
menubar = tk.Menu(self.root)
theme_menu = tk.Menu(menubar, tearoff=0)
themes = ["default", "dark", "wigley_site", "platinum"]
for theme in themes:
theme_menu.add_command(
label=theme.replace("_", " ").title(),
command=lambda t=theme: self.switch_theme(t)
)
menubar.add_cascade(label="Themes", menu=theme_menu)
self.root.config(menu=menubar)
def switch_theme(self, theme_name):
"""Switch to a different theme."""
self.current_theme = theme_name
# Apply new theme
self.theme_manager.apply_theme(theme_name)
# Recreate ambient backgrounds if needed
self.recreate_ambient_backgrounds()
# Recreate toast center with new theme colors
if hasattr(self, 'toast_center'):
self.toast_center = create_toast_center(
self.root,
self.theme_manager.get_current_theme_colors()
)
# Refresh custom components
self.refresh_custom_components()
def recreate_ambient_backgrounds(self):
"""Recreate ambient backgrounds with new theme."""
if hasattr(self, 'ambient_bg'):
self.ambient_bg.destroy()
self.ambient_bg = AmbientBackground(
self.root,
theme_tokens=self.theme_manager.get_current_theme_colors()
)
def save_theme_preference(self, theme_name):
"""Save theme preference to config file."""
config = self.load_config()
config["theme"]["default_theme"] = theme_name
self.save_config(config)
def load_theme_preference(self):
"""Load saved theme preference."""
config = self.load_config()
return config.get("theme", {}).get("default_theme", "dark")
---
def create_variant_theme(base_theme, modifications):
"""Create a theme variant by modifying an existing theme."""
# Start with base theme
new_theme = PROFESSIONAL_THEMES[base_theme].copy()
# Apply modifications
new_theme.update(modifications)
# Update metadata
new_theme["name"] = f"{new_theme['name']} (Modified)"
new_theme["description"] = f"Modified version of {base_theme}"
return new_theme
# Create a high-contrast variant
high_contrast = create_variant_theme("dark", {
"text_main": "#ffffff",
"text_secondary": "#cccccc",
"background_primary": "#000000",
"background_secondary": "#1a1a1a"
})
def generate_theme_from_colors(primary, secondary, name="Generated"):
"""Generate a complete theme from two colors."""
return {
"name": name,
"description": f"Auto-generated theme with {primary} and {secondary}",
"tier": "enterprise",
# Use provided colors
"accent_color_1": primary,
"accent_color_2": secondary,
"button_bg": primary,
"border_color": primary,
# Standard colors
"background_primary": "#1a1a1a",
"background_secondary": "#2a2a2a",
"text_main": "#ffffff",
"text_secondary": "#cccccc"
}
# Generate theme from user input
user_theme = generate_theme_from_colors("#FF6B35", "#F7931E", "Sunset")
PROFESSIONAL_THEMES["sunset"] = user_theme
def validate_theme(theme_dict):
"""Validate that a theme has all required properties."""
required_keys = [
"name", "background_primary", "text_main",
"accent_color_1", "button_bg", "button_fg"
]
missing = [key for key in required_keys if key not in theme_dict]
if missing:
raise ValueError(f"Theme missing required keys: {missing}")
# Validate color formats
import re
color_pattern = r'^#[0-9A-Fa-f]{6}{{RenderedMarkdown}}#39;
for key, value in theme_dict.items():
if key.endswith("_color") or key in ["button_bg", "button_fg"]:
if not re.match(color_pattern, value):
raise ValueError(f"Invalid color format for {key}: {value}")
return True
---
# Good theme design
good_theme = {
"background_primary": "#1a1a1a", # Dark but not pure black
"background_secondary": "#2a2a2a", # Slightly lighter
"text_main": "#ffffff", # High contrast
"text_secondary": "#cccccc", # Readable but secondary
"accent_color_1": "#007acc", # Distinct accent
"accent_color_2": "#00cc44" # Complementary accent
}
# Avoid these patterns
bad_theme = {
"background_primary": "#000000", # Too dark, harsh
"text_main": "#808080", # Low contrast
"accent_color_1": "#ffff00", # Bright, hard to read
"accent_color_2": "#ff00ff" # Jarring combination
}
---
Problem: Widgets don't change appearance after theme switch
Solutions:
# 1. Apply theme BEFORE creating widgets
theme_manager = ProfessionalThemeManager(self)
theme_manager.apply_theme("dark")
self.create_ui() # Create widgets after theme application
# 2. Force refresh for existing widgets
theme_manager.apply_theme("new_theme")
self.root.update_idletasks() # Force UI refresh
Problem: Custom colors don't match expected appearance
Debug:
# Check current theme colors
colors = theme_manager.get_current_theme_colors()
print("Current theme colors:", colors)
# Verify theme was applied
print("Current theme:", theme_manager.get_current_theme_name())
Problem: Theme switching causes lag
Optimization:
# Cache theme colors
self._cached_colors = None
def get_colors(self):
if self._cached_colors is None:
self._cached_colors = self.theme_manager.get_current_theme_colors()
return self._cached_colors
# Clear cache when theme changes
def on_theme_change(self):
self._cached_colors = None
self.apply_theme_to_components()
Problem: Theme looks different on different platforms
Solutions:
---
---
Ready to create beautiful, professional themes with Bravura! 🎨
For more examples, see the theme showcase demo:
python demo_themes.py