Theming Guide¶
Qt Framework includes a powerful, flexible theming system based on design tokens. This guide covers how to use built-in themes, customize them, and create your own.
Overview¶
The theming system uses a token-based architecture with three layers:
Primitive Tokens - Raw color values (e.g.,
primary_500: #2196F3)Semantic Tokens - Meaning-based references (e.g.,
bg_primary,fg_primary)Component Tokens - Component-specific styles (e.g.,
button_primary_bg)
This architecture ensures consistency and makes it easy to create cohesive themes.
Using Built-in Themes¶
Qt Framework comes with three built-in themes:
Light Theme¶
from qtframework.core import Application
from qtframework.themes import ThemeManager
app = Application()
theme_manager = ThemeManager()
# Load and apply light theme
theme_manager.load_theme("light")
theme_manager.apply_theme(app)
Dark Theme¶
from qtframework.core import Application
from qtframework.themes import ThemeManager
app = Application()
theme_manager = ThemeManager()
# Load and apply dark theme
theme_manager.load_theme("dark")
theme_manager.apply_theme(app)
High Contrast Theme¶
For accessibility, use the high contrast theme:
theme_manager.load_theme("high_contrast")
theme_manager.apply_theme(app)
Accessing Theme Colors¶
From Theme Tokens¶
from qtframework.themes.builtin_themes import create_light_theme
theme = create_light_theme()
# Access primitive colors
primary_color = theme.tokens.primitive.primary_500 # "#2196F3"
# Access semantic colors
bg_color = theme.tokens.semantic.bg_primary # "#FFFFFF"
text_color = theme.tokens.semantic.fg_primary # "#212121"
# Access component colors
button_bg = theme.tokens.components.button_primary_bg
Creating Custom Themes¶
Method 1: Modify Existing Theme¶
from qtframework.themes.builtin_themes import create_light_theme
# Start with light theme
theme = create_light_theme()
# Customize colors
theme.tokens.primitive.primary_500 = "#FF5722" # Change primary color
theme.tokens.semantic.action_primary = "#FF5722"
# Apply the customized theme
theme_manager.apply_theme_object(theme)
Method 2: Create from Scratch¶
from qtframework.themes import Theme
from qtframework.themes.tokens import (
DesignTokens,
PrimitiveColors,
SemanticColors,
ComponentColors,
)
# Create custom tokens
tokens = DesignTokens()
# Set primitive colors
tokens.primitive = PrimitiveColors(
primary_500="#9C27B0", # Purple
primary_600="#7B1FA2",
# ... other colors
)
# Set semantic colors
tokens.semantic = SemanticColors(
bg_primary="#FFFFFF",
fg_primary="#212121",
action_primary=tokens.primitive.primary_500,
# ... other semantic tokens
)
# Set component colors
tokens.components = ComponentColors(
button_primary_bg=tokens.primitive.primary_500,
button_primary_fg="#FFFFFF",
# ... other component tokens
)
# Create theme
custom_theme = Theme(
name="purple",
display_name="Purple Theme",
description="A custom purple theme",
author="Your Name",
version="1.0.0",
tokens=tokens,
)
# Apply it
theme_manager.apply_theme_object(custom_theme)
Method 3: Load from YAML¶
Create a theme file my_theme.yaml:
name: ocean
display_name: Ocean Theme
description: A calming ocean-inspired theme
author: Your Name
version: 1.0.0
tokens:
primitive:
primary_500: "#006064"
primary_600: "#00838F"
# ... other colors
semantic:
bg_primary: "#FFFFFF"
fg_primary: "#212121"
action_primary: "{primitive.primary_500}"
components:
button_primary_bg: "{semantic.action_primary}"
button_primary_fg: "#FFFFFF"
Load and apply:
from qtframework.themes import Theme
theme = Theme.from_yaml("my_theme.yaml")
theme_manager.apply_theme_object(theme)
Hot Reload During Development¶
Enable theme hot-reload for rapid development:
# Enable hot reload (watches theme files for changes)
theme_manager.enable_hot_reload("path/to/themes")
# Disable when done
theme_manager.disable_hot_reload()
Theme Switching at Runtime¶
Allow users to switch themes:
from PySide6.QtWidgets import QComboBox
def create_theme_switcher():
combo = QComboBox()
combo.addItems(["light", "dark", "high_contrast"])
def on_theme_changed(theme_name):
theme_manager.load_theme(theme_name)
theme_manager.apply_theme(app)
combo.currentTextChanged.connect(on_theme_changed)
return combo
Design Token Reference¶
Color Scales¶
Each color has a scale from 50 (lightest) to 950 (darkest):
Gray Scale:
gray_50throughgray_950Primary:
primary_50throughprimary_950Secondary:
secondary_50throughsecondary_950Success:
success_50throughsuccess_950Warning:
warning_50throughwarning_950Error:
error_50througherror_950Info:
info_50throughinfo_950
Semantic Tokens¶
Backgrounds¶
bg_primary- Main backgroundbg_secondary- Secondary surfacesbg_tertiary- Tertiary surfacesbg_elevated- Cards, dialogsbg_overlay- Modal overlays
Foreground/Text¶
fg_primary- Primary textfg_secondary- Secondary textfg_tertiary- Disabled textfg_on_accent- Text on accent colorsfg_on_dark- Text on dark backgroundsfg_on_light- Text on light backgrounds
Actions¶
action_primary- Primary action coloraction_primary_hover- Primary hover stateaction_primary_active- Primary active stateaction_secondary- Secondary action coloraction_secondary_hover- Secondary hover stateaction_secondary_active- Secondary active state
Feedback¶
feedback_success- Success messagesfeedback_warning- Warning messagesfeedback_error- Error messagesfeedback_info- Info messages
Borders¶
border_default- Default bordersborder_subtle- Subtle bordersborder_strong- Strong bordersborder_focus- Focus indicators
Typography Tokens¶
# Font families
theme.tokens.typography.font_family_default
theme.tokens.typography.font_family_mono
theme.tokens.typography.font_family_code
# Font sizes
theme.tokens.typography.font_size_xs # 11pt
theme.tokens.typography.font_size_sm # 12pt
theme.tokens.typography.font_size_md # 14pt
theme.tokens.typography.font_size_lg # 16pt
theme.tokens.typography.font_size_xl # 18pt
# Font weights
theme.tokens.typography.font_weight_normal
theme.tokens.typography.font_weight_medium
theme.tokens.typography.font_weight_bold
Spacing Tokens¶
theme.tokens.spacing.space_2 # 4px
theme.tokens.spacing.space_4 # 8px
theme.tokens.spacing.space_6 # 12px
theme.tokens.spacing.space_8 # 16px
theme.tokens.spacing.space_12 # 24px
Font Scaling¶
The theme system supports dynamic font scaling to improve accessibility. You can scale all typography tokens proportionally using a percentage value:
# Initialize theme manager with font scaling
theme_manager = ThemeManager(font_scale=125) # 125% larger fonts
# Or update font scale dynamically
theme_manager.set_font_scale(150) # 150% larger fonts
# Regenerate stylesheet with new scale
app.setStyleSheet(theme_manager.get_stylesheet())
Font Scale Range: 50% to 200%
100= Normal size (default)125= 25% larger (recommended for accessibility)150= 50% larger75= 25% smaller
Font scaling maintains the visual hierarchy by scaling all typography tokens (xs, sm, md, lg, xl, etc.) proportionally.
Configuration Integration¶
You can integrate font scaling with your configuration system:
# config.yaml
ui:
font_scale: 125 # 125% font size
# Load and apply font scale from config
config_manager = ConfigManager()
font_scale = config_manager.get("ui.font_scale", 100)
theme_manager = ThemeManager(font_scale=font_scale)
Best Practices¶
Use Semantic Tokens - Prefer semantic tokens over primitive colors in your widgets
Consistent Scales - Stick to the predefined color scale increments
Test Both Themes - Always test your app in both light and dark themes
Accessibility - Ensure sufficient contrast ratios (WCAG AA: 4.5:1 for text)
Document Custom Tokens - If adding custom component tokens, document their purpose
Support Font Scaling - Allow users to adjust font scale for accessibility (50-200%)
Advanced: Custom Stylesheet Generation¶
The theme system generates Qt stylesheets automatically. For custom styling:
theme = create_light_theme()
# Add custom stylesheet rules
theme.custom_styles = {
"MyCustomWidget": """
background-color: {semantic.bg_primary};
color: {semantic.fg_primary};
border: 1px solid {semantic.border_default};
border-radius: {borders.radius_md}px;
"""
}
# Tokens in {curly braces} are automatically resolved
stylesheet = theme.generate_stylesheet()
app.setStyleSheet(stylesheet)
Troubleshooting¶
Theme not applying¶
Make sure you’re applying the theme after creating the application:
app = Application()
theme_manager.apply_theme(app) # Must be after app creation
Colors look wrong¶
Check that semantic tokens are properly resolved:
theme.tokens.resolve_semantic_colors()
Custom theme not loading¶
Verify your YAML syntax and ensure all required fields are present:
try:
theme = Theme.from_yaml("my_theme.yaml")
except Exception as e:
print(f"Theme loading error: {e}")