Bravura SDK - Theming Guide

Back to Documentation Hub

Bravura SDK - Theming Guide

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.

---

Table of Contents

  1. Quick Start
  2. Available Themes
  3. Using ThemeManager
  4. Custom Theme Creation
  5. Theme Tokens & Colors
  6. Component-Specific Styling
  7. Runtime Theme Switching
  8. Advanced Customization
  9. Best Practices
  10. Troubleshooting

---

Quick Start

Basic Theme Application


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.

What ThemeManager Handles

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)

---

Available Themes

Standard Themes (All Tiers)

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

Professional+ Themes

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

Enterprise Themes

Theme ID Description Unique Features
`"custom"` Custom branding Full color customization, company branding
`"executive"` Executive theme Premium gradients, advanced effects

Preview Themes

Run the theme showcase demo:


python demo_themes.py

---

Using ThemeManager

Initialization


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()

Theme Switching


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()
        )

Current Theme Information


# 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()

---

Custom Theme Creation

Basic Custom Theme


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")

Complete Theme Definition


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)"
}

Company Branding Theme


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)

---

Theme Tokens & Colors

Standard Theme Tokens

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

Using Theme Tokens


# 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"]
    )

Manual tk Widget Styling

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())

---

Component-Specific Styling

Premium Buttons


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


# Progress bars automatically use theme colors
progress = ttk.Progressbar(
    parent,
    mode="determinate",
    style="TProgressbar"  # ThemeManager styles this automatically
)

Toast Notifications


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

Custom Components


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")

---

Runtime Theme Switching

Basic Theme Switching


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()
            )

Persistent Theme Selection


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")

---

Advanced Customization

Theme Inheritance


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"
})

Dynamic Theme Generation


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

Theme Validation


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

---

Best Practices

✅ Do's

  1. Initialize ThemeManager first - Always before creating widgets
  2. Apply theme before UI creation - Ensures consistent styling
  3. Use theme tokens - Instead of hardcoded colors
  4. Test themes on target platforms - Colors may render differently
  5. Provide theme switching - Let users choose their preference
  6. Document custom themes - Record color choices for maintenance
  7. Validate contrast ratios - Ensure accessibility compliance

❌ Don'ts

  1. Don't manually configure ttk.Style - ThemeManager handles this
  2. Don't hardcode colors - Use theme tokens instead
  3. Don't apply themes after widget creation - May not style existing widgets
  4. Don't mix tk and ttk styling - Choose one approach
  5. Don't create themes without testing - Verify on different displays
  6. Don't forget theme switching cleanup - Recreate backgrounds when switching

Theme Design Guidelines


# 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
}

---

Troubleshooting

Theme Not Applied

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

Colors Look Wrong

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())

Performance Issues

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()

Cross-Platform Issues

Problem: Theme looks different on different platforms

Solutions:

---

Related Documentation

---

Ready to create beautiful, professional themes with Bravura! 🎨

For more examples, see the theme showcase demo:


python demo_themes.py