Theme Manager
Create and manage multiple color themes for your animations using the Theme Manager. Build light and dark modes, brand variations, and custom color schemes that can be switched at runtime without re-exporting.
What is Theme Manager?
Interface for creating and managing multiple color themes.
Theme Manager Overview
Theme Manager:
- •Centralized interface for theme creation
- •Define multiple themes (light, dark, brand variants)
- •Assign color values to slots per theme
- •Preview themes in real-time
- •Apply themes to animation
- •Export with multiple themes
Core Functions:
- •Create Themes: Add new theme variations
- •Edit Theme Colors: Define color slot values for each theme
- •Manage Themes: Rename, duplicate, delete themes
- •Apply Themes: Switch active theme in editor
- •Preview Changes: See updates in real-time
- •Export Themes: Package multiple themes in dotLottie
Benefits of Theme Manager
Advantages:
- •Visual Interface: Easy theme creation without code
- •Real-time Preview: See changes immediately on canvas
- •Multi-theme Support: Create unlimited theme variations
- •Organized Workflow: All themes managed in one place
- •Export Ready: Themes automatically included in dotLottie export
- •Professional Results: Consistent, well-structured themes
Use Cases:
- •Light and dark mode creation
- •Brand color variations
- •Seasonal theme variations
- •Client-specific themes
- •A/B testing color schemes
- •Platform-specific themes (iOS, Android, Web)
Accessing Theme Manager
Multiple ways to open Theme Manager.
Opening Theme Manager
Method 1 - Left Sidebar:
Look for "Theme Manager" or "Themes" in left sidebar
Click "Theme Manager" button
Panel opens showing theme list
View and manage themes
Method 2 - Menu Bar:
Click Window or View menu
Select "Theme Manager"
Panel opens
Start creating themes
Method 3 - Panel Switcher:
Look for panel tabs/switcher
Find "Themes" or "Theme Manager" tab
Click to switch to theme panel
Panel displays
Method 4 - Properties Panel:
Select color property linked to slot
Look for "Manage Themes" link or button
Click to open Theme Manager
Opens with relevant slot focused
Theme Manager Interface
Panel Sections:
- •Theme List: All created themes
- •Active Theme Indicator: Currently applied theme
- •Create Theme Button: Add new theme
- •Theme Editor: Edit selected theme
- •Color Slot List: All color slots in project
- •Slot Value Editor: Set colors per theme
- •Preview Area: See theme applied (or use canvas)
- •Apply/Export Options: Use theme, export settings
Visual Indicators:
- •Active theme highlighted or marked
- •Slot colors displayed as swatches
- •Undefined slots indicated (warnings)
- •Changes preview in real-time
Creating Themes
Add new theme variations to your animation.
How to Create a Theme
Creating a New Theme:
Open Theme Manager
Click "Create Theme" or "New Theme" button
Theme creation dialog opens
Enter theme name (e.g., "Light", "Dark", "Brand A")
Choose base theme (optional - copies existing theme colors)
Click "Create" or "Add"
New theme appears in theme list
Ready to define colors
Theme Naming:
- •Use descriptive names: "Light Mode", "Dark Mode", "Summer Theme"
- •Indicate purpose or context: "iOS Theme", "Android Theme"
- •Avoid generic names: "Theme 1", "New Theme"
- •Consistent naming convention
Common Theme Names:
- •Light - Light mode/theme
- •Dark - Dark mode/theme
- •Brand Primary - Main brand colors
- •Brand Secondary - Alternative brand colors
- •High Contrast - Accessibility theme
- •Seasonal - Holiday or seasonal variations
Default Theme
First Theme:
- •First theme created becomes default
- •Or existing theme marked as default
- •Default theme used when no theme specified
- •Can change default designation
Default Theme Purpose:
- •Fallback when theme not specified
- •Used in players without theme support
- •Preview default in editor
- •Export default for compatibility
Duplicating Themes
Copy Existing Theme:
Select theme to duplicate
Right-click or click "Duplicate" button
New theme created with same slot values
Rename duplicated theme
Modify colors as needed
Duplication Benefits:
- •Start from existing theme
- •Faster than defining from scratch
- •Maintain similar color relationships
- •Create variations efficiently
Use Cases:
- •Create "Dark" from "Light" theme
- •Create seasonal variations
- •Create subtle brand variants
- •Create A/B test variations
Editing Theme Colors
Define color values for each theme.
Theme Editor Interface
Editing a Theme:
Select theme from theme list
Theme editor displays
Shows all color slots
Current value for each slot in this theme
Color swatches clickable
What You See:
- •List of all color slots in project
- •Current color value for each slot in selected theme
- •Color swatches (visual preview)
- •Slot names
- •Undefined slots (if any)
Assigning Colors to Slots
Set Slot Color for Theme:
Select theme to edit
Find color slot in list
Click color swatch for that slot
Color picker opens
Choose color for this theme
Color updates immediately
Preview on canvas updates (if theme active)
Color Selection:
- •Use color picker (HEX, RGB, HSL)
- •Pick from canvas (eyedropper, if available)
- •Enter hex code directly
- •Use document colors
- •Copy/paste colors between slots
Defining All Slots
Complete Theme Definition:
- •All slots should be defined for each theme
- •Undefined slots may cause issues
- •Theme Manager warns about undefined slots
- •Define all slots before exporting
Workflow:
Create theme
Go through each slot
Assign appropriate color
Verify all slots defined
Preview theme
Adjust colors as needed
Undefined Slot Handling:
- •Warning indicator in Theme Manager
- •May fall back to default color
- •May cause visual inconsistency
- •Always define all slots per theme
Light and Dark Themes
Creating light and dark mode support.
Light Theme Setup
Light Theme Colors:
Create theme named "Light"
Define slot colors for light mode:
- •Background: White or light gray (#FFFFFF, #FAFAFA)
- •Surface: Light gray or white (#F5F5F5, #FFFFFF)
- •Primary: Vibrant brand color (#2196F3)
- •Text: Dark gray or black (#212121, #000000)
- •Text Secondary: Medium gray (#757575)
- •Border: Light gray (#E0E0E0)
Preview on canvas
Adjust for readability
Verify contrast ratios
Light Theme Characteristics:
- •Bright backgrounds
- •Dark text on light backgrounds
- •High contrast for readability
- •Vibrant accent colors
- •Subtle shadows and borders
Dark Theme Setup
Dark Theme Colors:
Create theme named "Dark"
Define slot colors for dark mode:
- •Background: Dark gray or black (#121212, #000000)
- •Surface: Slightly lighter gray (#1E1E1E, #2C2C2C)
- •Primary: Lighter, desaturated brand color (#64B5F6)
- •Text: White or light gray (#FFFFFF, #E0E0E0)
- •Text Secondary: Medium gray (#B0B0B0)
- •Border: Dark gray (#424242)
Preview on canvas
Adjust for readability
Verify contrast ratios
Dark Theme Characteristics:
- •Dark backgrounds (not pure black, typically #121212)
- •Light text on dark backgrounds
- •Slightly desaturated colors (less vibrant than light mode)
- •Elevated surfaces slightly lighter than background
- •Reduced contrast to avoid eye strain
Color Relationships
Maintaining Harmony:
- •Primary color should be adjusted between themes
- •Light theme: Vibrant colors
- •Dark theme: Desaturated, lighter colors
- •Text must meet WCAG contrast ratios (4.5:1 minimum)
- •Surface colors should have clear hierarchy
Example Color Mapping:
Slot: Primary
- Light Theme: #2196F3 (vivid blue)
- Dark Theme: #64B5F6 (lighter, desaturated blue)
Slot: Background
- Light Theme: #FFFFFF (white)
- Dark Theme: #121212 (dark gray)
Slot: Text
- Light Theme: #212121 (dark gray)
- Dark Theme: #FFFFFF (white)
Slot: Surface
- Light Theme: #F5F5F5 (light gray, darker than background)
- Dark Theme: #1E1E1E (lighter than background for elevation)Applying Themes
Switch between themes in the editor.
Applying a Theme
How to Apply:
Open Theme Manager
Select theme from theme list
Click "Apply" or "Set Active" button
Theme becomes active
Canvas updates with theme colors
All slots use values from active theme
Active Theme Indicator:
- •Active theme highlighted in list
- •Badge or checkmark indicator
- •Name displayed prominently
- •Canvas reflects active theme
Previewing Themes
Real-time Preview:
- •Changes to theme colors update immediately
- •Switch themes to see differences
- •Canvas updates in real-time
- •Compare themes side-by-side (if supported)
Preview Workflow:
Apply Light theme
Review animation appearance
Apply Dark theme
Compare differences
Adjust colors as needed
Switch back and forth
Refine until satisfied
Theme Switching
Switching Between Themes:
- •Click theme in Theme Manager to activate
- •Or use dropdown/selector (if available)
- •Canvas updates immediately
- •All color slots update to theme values
- •Animation plays with new colors
Use Cases:
- •Preview how animation looks in each theme
- •Compare color choices
- •Verify theme quality
- •Test contrast and readability
- •Ensure smooth transitions
Managing Multiple Themes
Organize and maintain theme variations.
Theme List Management
Theme Organization:
- •Themes listed in Theme Manager
- •Rename themes for clarity
- •Reorder themes (if supported)
- •Delete unused themes
- •Duplicate for variations
Renaming Themes:
Select theme
Right-click or click rename button
Enter new name
Press Enter to confirm
Updated name appears in list
Deleting Themes:
Select theme to delete
Click "Delete" button or press Delete key
Confirmation dialog appears (typically)
Confirm deletion
Theme removed from list
Cannot delete if only one theme remains (typically)
Theme Count Considerations
How Many Themes?
- •Typical: 2-3 themes (light, dark, and maybe brand variant)
- •Simple Projects: 1-2 themes
- •Complex Projects: 3-5 themes
- •Too Many: Hard to maintain, confusing for users
When to Add Theme:
- •Clear use case (light/dark mode)
- •Brand variations needed
- •Platform-specific requirements
- •Seasonal or event variations
- •User customization options
When Not to Add:
- •Minor color tweaks (use one theme)
- •Overly similar variations
- •No clear use case
- •Maintenance burden
Brand Variation Themes
Creating multiple brand color schemes.
Creating Brand Themes
Brand Variation Workflow:
Create color slots for brand colors
Create first brand theme (e.g., "Brand A")
Define brand A color values
Duplicate theme for variant
Rename to "Brand B"
Change primary brand colors
Adjust related colors
Preview both themes
Brand Theme Example:
Slots:
- Primary (main brand color)
- Secondary (accent color)
- Background
- Text
Brand A Theme:
- Primary: #2196F3 (blue)
- Secondary: #FF5722 (orange)
- Background: #FFFFFF
- Text: #212121
Brand B Theme:
- Primary: #9C27B0 (purple)
- Secondary: #4CAF50 (green)
- Background: #FFFFFF
- Text: #212121White-Label Themes
Multi-Client Support:
- •Create theme per client
- •Each theme has client's brand colors
- •Same animation, different branding
- •Export with all client themes
- •Select theme at runtime based on client
Use Cases:
- •SaaS products with multiple clients
- •White-label applications
- •Partner integrations
- •Reseller products
- •Multi-brand companies
Theme Workflows
Common patterns for theme creation.
Light/Dark Mode Workflow
Complete Light/Dark Setup:
Plan color slots:
- •Identify all themeable colors
- •Create slots: Primary, Background, Surface, Text, Text Secondary, Border, etc.
Link properties:
- •Select all color properties
- •Link to appropriate slots
- •Verify all themed properties linked
Create Light theme:
- •Create "Light" theme
- •Define all slot values for light mode
- •Bright backgrounds, dark text, vibrant colors
Create Dark theme:
- •Duplicate Light theme or create new
- •Rename to "Dark"
- •Define all slot values for dark mode
- •Dark backgrounds, light text, desaturated colors
Preview both themes:
- •Switch between Light and Dark
- •Verify readability and contrast
- •Adjust colors as needed
Export with themes:
- •Export as dotLottie
- •Both themes embedded
- •Ready for runtime switching
Brand Variation Workflow
Multiple Brand Themes:
Create brand color slots:
- •Primary, Secondary brand colors
- •Supporting colors (background, text)
Link brand elements:
- •Logo colors
- •Button colors
- •Accent elements
Create first brand theme:
- •"Brand A" or client name
- •Define all brand colors
Create additional brand themes:
- •Duplicate or create new
- •Define colors for each brand
- •Maintain consistency in non-brand colors
Preview each brand:
- •Switch between brands
- •Verify each looks professional
Export with all brands:
- •Export as dotLottie
- •All brand themes included
- •Select brand at runtime
A/B Testing Workflow
Test Color Variations:
Create base theme
Create variation theme
Change specific colors to test
Export with both themes
Use in A/B test
Select theme based on test group
Measure results
Keep winning theme
Advanced Theme Management
Advanced techniques for theme creation.
Theme Inheritance
Base Theme Concept:
- •Create base theme with most colors defined
- •Duplicate for variations
- •Only change specific colors
- •Maintains consistency across themes
Benefits:
- •Faster theme creation
- •Consistent color relationships
- •Easy maintenance
- •Clear relationships between themes
Seasonal Themes
Holiday and Seasonal Variations:
Create base year-round theme
Duplicate for seasonal variation
Adjust colors for season/holiday
Preview seasonal version
Export with both themes
Switch based on date/time
Seasonal Theme Examples:
- •Winter/Holiday: Cool colors, snow effects
- •Summer: Warm, bright colors
- •Spring: Pastels, fresh colors
- •Fall: Warm, earthy tones
- •Halloween: Orange and black
- •Christmas: Red and green
Dynamic Theme Selection
Runtime Theme Logic:
// Detect user's system theme
const prefersDark = window.matchMedia("(prefers-color-scheme: dark)").matches;
const theme = prefersDark ? "dark" : "light";
// Load animation with detected theme
player.loadAnimation({
path: "animation.lottie",
theme: theme,
});
// Listen for theme changes
window.matchMedia("(prefers-color-scheme: dark)").addEventListener("change", (e) => {
const newTheme = e.matches ? "dark" : "light";
player.setTheme(newTheme);
});Theme Selection Strategies:
- •System preference: Detect user's OS theme
- •User setting: Allow manual theme selection
- •Time-based: Auto-switch based on time of day
- •Context-based: Theme based on app state or page
- •Brand-based: Theme based on client/brand context
Under the Hood: Theme State Management
Deep dive into how Lottie Creator manages theme state and operations.
Theme State Machine Architecture
Theme management uses a state machine (XState) to handle theme operations and state transitions.
State Machine Overview:
Theme State Machine
├── States
│ ├── idle - No operation in progress
│ ├── creating - Creating new theme
│ ├── editing - Editing theme colors
│ ├── applying - Switching active theme
│ ├── deleting - Deleting theme
│ └── validating - Validating theme completeness
├── Events
│ ├── CREATE_THEME - Trigger theme creation
│ ├── EDIT_THEME - Enter edit mode
│ ├── APPLY_THEME - Switch active theme
│ ├── DELETE_THEME - Remove theme
│ ├── UPDATE_SLOT_COLOR - Change slot color in theme
│ └── VALIDATE - Check theme completeness
└── Context
├── themes - Array of all themes
├── activeThemeId - Currently active theme
├── editingThemeId - Theme being edited (if any)
└── validationErrors - Validation issuesState Machine Implementation:
import { createMachine, assign } from "xstate";
const themeManagerMachine = createMachine({
id: "themeManager",
initial: "idle",
context: {
themes: [],
activeThemeId: null,
editingThemeId: null,
validationErrors: [],
},
states: {
idle: {
on: {
CREATE_THEME: {
target: "creating",
actions: "prepareThemeCreation",
},
EDIT_THEME: {
target: "editing",
actions: "setEditingTheme",
},
APPLY_THEME: {
target: "applying",
actions: "prepareThemeApplication",
},
DELETE_THEME: {
target: "deleting",
actions: "prepareThemeDeletion",
},
},
},
creating: {
entry: "createNewTheme",
on: {
SUCCESS: {
target: "idle",
actions: "addThemeToList",
},
ERROR: {
target: "idle",
actions: "handleCreationError",
},
},
},
editing: {
on: {
UPDATE_SLOT_COLOR: {
actions: "updateSlotColorInTheme",
},
FINISH_EDITING: {
target: "idle",
actions: "finalizeEditing",
},
},
},
applying: {
entry: "applyThemeToScene",
on: {
SUCCESS: {
target: "idle",
actions: ["setActiveTheme", "updateCanvas"],
},
ERROR: {
target: "idle",
actions: "handleApplicationError",
},
},
},
deleting: {
entry: "deleteTheme",
on: {
SUCCESS: {
target: "idle",
actions: "removeThemeFromList",
},
ERROR: {
target: "idle",
actions: "handleDeletionError",
},
},
},
validating: {
entry: "validateThemes",
on: {
SUCCESS: {
target: "idle",
actions: "clearValidationErrors",
},
ERROR: {
target: "idle",
actions: "setValidationErrors",
},
},
},
},
});State Transitions:
// Create theme
themeManager.send({
type: "CREATE_THEME",
data: {
name: "Dark Mode",
basedOn: "light", // Optional: duplicate existing theme
},
});
// Edit theme color
themeManager.send({
type: "UPDATE_SLOT_COLOR",
data: {
themeId: "dark",
slotId: "primary-color",
color: { r: 100, g: 181, b: 246, a: 1 },
},
});
// Apply theme
themeManager.send({
type: "APPLY_THEME",
data: {
themeId: "dark",
},
});DEFAULT_THEME Concept
Every project has a DEFAULT_THEME for compatibility and fallback.
What is DEFAULT_THEME?
- •Special Theme ID: Reserved identifier "DEFAULT_THEME"
- •Always Exists: Created automatically with first color slot
- •Fallback Mechanism: Used when no theme specified
- •Compatibility: Ensures animations work in non-theme-aware players
- •Initial Values: Slots created with DEFAULT_THEME color
DEFAULT_THEME Structure:
const DEFAULT_THEME = {
id: "DEFAULT_THEME",
name: "Default",
isSystem: true, // System-generated, can't be deleted
isPrimary: false, // Not a user-created primary theme
colors: new Map([
["primary-color", { r: 33, g: 150, b: 243, a: 1 }],
["bg-color", { r: 255, g: 255, b: 255, a: 1 }],
// ... all slot default colors
]),
};DEFAULT_THEME Behavior:
class ThemeManager {
getActiveTheme(): Theme {
// If no theme explicitly set, use DEFAULT_THEME
if (!this.activeThemeId) {
return this.themes.get("DEFAULT_THEME");
}
return this.themes.get(this.activeThemeId);
}
getSlotColor(slotId: string): Color {
const activeTheme = this.getActiveTheme();
// Try to get color from active theme
if (activeTheme.hasColor(slotId)) {
return activeTheme.getColor(slotId);
}
// Fallback to DEFAULT_THEME
const defaultTheme = this.themes.get("DEFAULT_THEME");
return defaultTheme.getColor(slotId);
}
createColorSlot(slotId: string, initialColor: Color): void {
const slot = new ColorSlot(slotId, initialColor);
// Automatically add slot to DEFAULT_THEME
const defaultTheme = this.themes.get("DEFAULT_THEME");
defaultTheme.setColor(slotId, initialColor);
// Also add to all other themes (prompts user for colors)
for (const theme of this.themes.values()) {
if (theme.id !== "DEFAULT_THEME") {
theme.setColor(slotId, initialColor); // Use default as starting point
}
}
}
}Why DEFAULT_THEME Exists:
- •Color Slot Creation: New slots need initial color values
- •Fallback: When theme missing or incomplete
- •Editor Mode: Working without explicit theme selection
- •Backwards Compatibility: Projects from before theming
- •Export Safety: Ensures all slots have values
DEFAULT_THEME vs User Themes:
// DEFAULT_THEME (system)
{
id: "DEFAULT_THEME",
name: "Default",
isSystem: true,
canDelete: false,
canRename: false
}
// User Theme
{
id: "light",
name: "Light Mode",
isSystem: false,
canDelete: true,
canRename: true
}Theme Data Storage
Themes are stored in the .creator project file format.
.creator File Structure:
{
"version": "1.0",
"project": {
"name": "My Animation",
"width": 1920,
"height": 1080
},
"themes": {
"DEFAULT_THEME": {
"id": "DEFAULT_THEME",
"name": "Default",
"isSystem": true,
"colors": {
"primary-color": { "r": 33, "g": 150, "b": 243, "a": 1 },
"bg-color": { "r": 255, "g": 255, "b": 255, "a": 1 }
}
},
"light": {
"id": "light",
"name": "Light Mode",
"isSystem": false,
"colors": {
"primary-color": { "r": 33, "g": 150, "b": 243, "a": 1 },
"bg-color": { "r": 255, "g": 255, "b": 255, "a": 1 }
}
},
"dark": {
"id": "dark",
"name": "Dark Mode",
"isSystem": false,
"colors": {
"primary-color": { "r": 100, "g": 181, "b": 246, "a": 1 },
"bg-color": { "r": 18, "g": 18, "b": 18, "a": 1 }
}
}
},
"activeThemeId": "light",
"colorSlots": [
{
"assetId": "primary-color",
"slotType": 2,
"type": "SLOT"
},
{
"assetId": "bg-color",
"slotType": 2,
"type": "SLOT"
}
],
"layers": [],
"assets": []
}Theme Serialization:
class ThemeSerializer {
serialize(themes: Map<string, Theme>): any {
const serialized = {};
for (const [id, theme] of themes) {
serialized[id] = {
id: theme.id,
name: theme.name,
isSystem: theme.isSystem,
colors: {},
};
// Serialize colors map
for (const [slotId, color] of theme.colors) {
serialized[id].colors[slotId] = {
r: color.r,
g: color.g,
b: color.b,
a: color.a,
};
}
}
return serialized;
}
deserialize(data: any): Map<string, Theme> {
const themes = new Map();
for (const [id, themeData] of Object.entries(data)) {
const theme = new Theme(themeData.id, themeData.name, themeData.isSystem);
// Deserialize colors
for (const [slotId, colorData] of Object.entries(themeData.colors)) {
theme.setColor(slotId, colorData);
}
themes.set(id, theme);
}
return themes;
}
}File Save Process:
async function saveProject() {
// 1. Gather project data
const projectData = {
version: "1.0",
project: getProjectMetadata(),
themes: themeSerializer.serialize(themeManager.getAllThemes()),
activeThemeId: themeManager.getActiveThemeId(),
colorSlots: slotManager.serializeSlots(),
layers: sceneGraph.serializeLayers(),
assets: assetManager.serializeAssets(),
};
// 2. Convert to JSON
const json = JSON.stringify(projectData, null, 2);
// 3. Save to file
await fs.writeFile(projectPath, json, "utf-8");
// 4. Mark as saved
projectState.markSaved();
}File Load Process:
async function loadProject(filePath: string) {
// 1. Read file
const json = await fs.readFile(filePath, "utf-8");
const data = JSON.parse(json);
// 2. Load themes first
const themes = themeSerializer.deserialize(data.themes);
themeManager.setThemes(themes);
// 3. Set active theme
themeManager.setActiveTheme(data.activeThemeId);
// 4. Load color slots (they reference themes)
slotManager.deserializeSlots(data.colorSlots);
// 5. Load layers and assets
sceneGraph.deserializeLayers(data.layers);
assetManager.deserializeAssets(data.assets);
// 6. Render with active theme
canvas.render();
}Active Theme Management
How the editor manages the currently active theme.
Active Theme State:
class ThemeManager {
private activeThemeId: string = "DEFAULT_THEME";
private themes: Map<string, Theme> = new Map();
private listeners: Set<ThemeChangeListener> = new Set();
getActiveTheme(): Theme {
return this.themes.get(this.activeThemeId);
}
setActiveTheme(themeId: string): void {
// 1. Validate theme exists
if (!this.themes.has(themeId)) {
throw new Error(`Theme "${themeId}" not found`);
}
// 2. Store previous theme (for undo)
const previousThemeId = this.activeThemeId;
// 3. Update active theme
this.activeThemeId = themeId;
// 4. Update all slot values
this.updateSlotsWithTheme(themeId);
// 5. Trigger canvas re-render
canvas.render();
// 6. Notify listeners
this.notifyThemeChange(previousThemeId, themeId);
// 7. Add to undo stack
undoManager.add({
type: "THEME_CHANGE",
previousTheme: previousThemeId,
newTheme: themeId,
});
}
private updateSlotsWithTheme(themeId: string): void {
const theme = this.themes.get(themeId);
// Update each slot with theme's color
for (const slot of slotManager.getAllSlots()) {
const color = theme.getColor(slot.id);
slot.updateValue(color);
}
}
private notifyThemeChange(oldThemeId: string, newThemeId: string): void {
const event = {
type: "theme_changed",
oldTheme: oldThemeId,
newTheme: newThemeId,
timestamp: Date.now(),
};
for (const listener of this.listeners) {
listener.onThemeChange(event);
}
}
}Theme Change Propagation:
User Selects Theme "dark"
↓
ThemeManager.setActiveTheme("dark")
↓
Load "dark" theme colors
↓
Update all color slots with dark theme values
│
├─ Slot "primary-color": #2196F3 → #64B5F6
├─ Slot "bg-color": #FFFFFF → #121212
└─ Slot "text-color": #212121 → #FFFFFF
↓
Slots notify referenced properties
│
├─ Button fill updates to #64B5F6
├─ Background fill updates to #121212
└─ Text fill updates to #FFFFFF
↓
Canvas re-renders with new colors
↓
UI updates active theme indicator
↓
Undo operation added to historyPerformance Optimization:
class ThemeManager {
private renderScheduled = false;
setActiveTheme(themeId: string): void {
// ... theme change logic ...
// Batch render updates
this.scheduleRender();
}
updateSlotColor(themeId: string, slotId: string, color: Color): void {
const theme = this.themes.get(themeId);
theme.setColor(slotId, color);
// Only update if this is the active theme
if (themeId === this.activeThemeId) {
const slot = slotManager.getSlot(slotId);
slot.updateValue(color);
// Batch render
this.scheduleRender();
}
}
private scheduleRender(): void {
if (this.renderScheduled) return;
this.renderScheduled = true;
// Use requestAnimationFrame to batch multiple updates
requestAnimationFrame(() => {
canvas.render();
this.renderScheduled = false;
});
}
}Slot Distribution to Themes
When a new slot is created, it's automatically distributed to all themes.
Slot Distribution Process:
class SlotManager {
createSlot(slotId: string, initialColor: Color): ColorSlot {
// 1. Create slot
const slot = new ColorSlot(slotId, initialColor);
this.slots.set(slotId, slot);
// 2. Add to DEFAULT_THEME
const defaultTheme = themeManager.getTheme("DEFAULT_THEME");
defaultTheme.setColor(slotId, initialColor);
// 3. Distribute to all user themes
const userThemes = themeManager.getUserThemes();
for (const theme of userThemes) {
// Use initial color as starting point
theme.setColor(slotId, initialColor);
// Mark as needing user review
theme.markSlotAsNew(slotId);
}
// 4. Notify UI to prompt user for theme colors
this.notifySlotCreated(slotId, userThemes.length);
return slot;
}
private notifySlotCreated(slotId: string, themeCount: number): void {
// Show notification to user
notifications.show({
type: "info",
message: `Color slot "${slotId}" created. Define colors for ${themeCount} themes.`,
action: {
label: "Open Theme Manager",
callback: () => panels.open("theme-manager", { focusSlot: slotId }),
},
});
}
}New Slot in Existing Themes:
// Before: Project with 2 themes
themes = {
light: {
colors: {
primary: { r: 33, g: 150, b: 243, a: 1 },
bg: { r: 255, g: 255, b: 255, a: 1 },
},
},
dark: {
colors: {
primary: { r: 100, g: 181, b: 246, a: 1 },
bg: { r: 18, g: 18, b: 18, a: 1 },
},
},
};
// User creates new slot "accent" with color #FF5722
slotManager.createSlot("accent", { r: 255, g: 87, b: 34, a: 1 });
// After: Slot added to all themes
themes = {
light: {
colors: {
primary: { r: 33, g: 150, b: 243, a: 1 },
bg: { r: 255, g: 255, b: 255, a: 1 },
accent: { r: 255, g: 87, b: 34, a: 1 }, // New - needs review
},
},
dark: {
colors: {
primary: { r: 100, g: 181, b: 246, a: 1 },
bg: { r: 18, g: 18, b: 18, a: 1 },
accent: { r: 255, g: 87, b: 34, a: 1 }, // New - needs adjustment for dark
},
},
};
// UI prompts user: "Review new color slot 'accent' in Dark theme"Slot Validation:
class ThemeManager {
validateThemeCompleteness(themeId: string): ValidationResult {
const theme = this.themes.get(themeId);
const allSlots = slotManager.getAllSlots();
const errors: string[] = [];
const warnings: string[] = [];
for (const slot of allSlots) {
if (!theme.hasColor(slot.id)) {
errors.push(`Theme "${theme.name}" missing color for slot "${slot.id}"`);
}
}
// Check for slots with default colors (may need customization)
for (const [slotId, color] of theme.colors) {
const defaultColor = this.getTheme("DEFAULT_THEME").getColor(slotId);
if (colorsEqual(color, defaultColor)) {
warnings.push(`Slot "${slotId}" in theme "${theme.name}" uses default color (consider customizing)`);
}
}
return {
valid: errors.length === 0,
errors,
warnings,
};
}
}Undo/Redo Integration
Theme operations integrate with the undo/redo system.
Undoable Operations:
enum ThemeOperation {
CREATE_THEME = "CREATE_THEME",
DELETE_THEME = "DELETE_THEME",
RENAME_THEME = "RENAME_THEME",
CHANGE_ACTIVE_THEME = "CHANGE_ACTIVE_THEME",
UPDATE_SLOT_COLOR = "UPDATE_SLOT_COLOR",
DUPLICATE_THEME = "DUPLICATE_THEME",
}
interface ThemeUndoableAction {
type: ThemeOperation;
execute(): void;
undo(): void;
redo(): void;
}Update Slot Color Undo:
class UpdateSlotColorAction implements ThemeUndoableAction {
constructor(
private themeId: string,
private slotId: string,
private oldColor: Color,
private newColor: Color
) {}
execute(): void {
const theme = themeManager.getTheme(this.themeId);
theme.setColor(this.slotId, this.newColor);
// If this is active theme, update canvas
if (themeManager.getActiveThemeId() === this.themeId) {
const slot = slotManager.getSlot(this.slotId);
slot.updateValue(this.newColor);
canvas.render();
}
}
undo(): void {
const theme = themeManager.getTheme(this.themeId);
theme.setColor(this.slotId, this.oldColor);
if (themeManager.getActiveThemeId() === this.themeId) {
const slot = slotManager.getSlot(this.slotId);
slot.updateValue(this.oldColor);
canvas.render();
}
}
redo(): void {
this.execute();
}
}Change Active Theme Undo:
class ChangeActiveThemeAction implements ThemeUndoableAction {
constructor(
private previousThemeId: string,
private newThemeId: string
) {}
execute(): void {
themeManager.setActiveTheme(this.newThemeId);
}
undo(): void {
themeManager.setActiveTheme(this.previousThemeId);
}
redo(): void {
this.execute();
}
}Create Theme Undo:
class CreateThemeAction implements ThemeUndoableAction {
private createdTheme: Theme;
constructor(
private themeName: string,
private baseThemeId?: string
) {}
execute(): void {
// Create theme
this.createdTheme = themeManager.createTheme(this.themeName, this.baseThemeId);
}
undo(): void {
// Delete the created theme
themeManager.deleteTheme(this.createdTheme.id);
}
redo(): void {
// Re-add the theme
themeManager.addTheme(this.createdTheme);
}
}Undo Manager Integration:
class ThemeManager {
updateSlotColor(themeId: string, slotId: string, newColor: Color): void {
// Get current color for undo
const theme = this.themes.get(themeId);
const oldColor = theme.getColor(slotId);
// Create undoable action
const action = new UpdateSlotColorAction(themeId, slotId, oldColor, newColor);
// Execute and add to undo stack
action.execute();
undoManager.add(action);
}
setActiveTheme(themeId: string): void {
const previousThemeId = this.activeThemeId;
// Create undoable action
const action = new ChangeActiveThemeAction(previousThemeId, themeId);
// Execute and add to undo stack
action.execute();
undoManager.add(action);
}
}Theme Events and Lifecycle
Theme operations trigger events that other systems can listen to.
Theme Event Types:
interface ThemeEvent {
type: ThemeEventType;
timestamp: number;
data: any;
}
enum ThemeEventType {
THEME_CREATED = "theme_created",
THEME_DELETED = "theme_deleted",
THEME_RENAMED = "theme_renamed",
THEME_ACTIVATED = "theme_activated",
SLOT_COLOR_CHANGED = "slot_color_changed",
THEME_VALIDATED = "theme_validated",
}Event Emitter:
class ThemeManager extends EventEmitter {
createTheme(name: string, baseThemeId?: string): Theme {
// Create theme
const theme = new Theme(name, generateId());
// Copy colors from base theme if specified
if (baseThemeId) {
const baseTheme = this.themes.get(baseThemeId);
for (const [slotId, color] of baseTheme.colors) {
theme.setColor(slotId, color);
}
}
// Add to themes map
this.themes.set(theme.id, theme);
// Emit event
this.emit("theme_created", {
type: ThemeEventType.THEME_CREATED,
timestamp: Date.now(),
data: {
themeId: theme.id,
themeName: theme.name,
basedOn: baseThemeId,
},
});
return theme;
}
setActiveTheme(themeId: string): void {
const previousThemeId = this.activeThemeId;
this.activeThemeId = themeId;
// ... update slots and render ...
// Emit event
this.emit("theme_activated", {
type: ThemeEventType.THEME_ACTIVATED,
timestamp: Date.now(),
data: {
previousTheme: previousThemeId,
activeTheme: themeId,
},
});
}
updateSlotColor(themeId: string, slotId: string, color: Color): void {
const theme = this.themes.get(themeId);
const oldColor = theme.getColor(slotId);
theme.setColor(slotId, color);
// Emit event
this.emit("slot_color_changed", {
type: ThemeEventType.SLOT_COLOR_CHANGED,
timestamp: Date.now(),
data: {
themeId,
slotId,
oldColor,
newColor: color,
},
});
// Update canvas if active theme
if (themeId === this.activeThemeId) {
const slot = slotManager.getSlot(slotId);
slot.updateValue(color);
canvas.render();
}
}
}Event Listeners:
// UI updates
themeManager.on("theme_created", (event) => {
themeListUI.addTheme(event.data.themeId);
notifications.show(`Theme "${event.data.themeName}" created`);
});
themeManager.on("theme_activated", (event) => {
themeListUI.setActive(event.data.activeTheme);
canvas.render();
});
// Analytics tracking
themeManager.on("slot_color_changed", (event) => {
analytics.track("theme_color_updated", {
theme: event.data.themeId,
slot: event.data.slotId,
});
});
// Auto-save on theme changes
themeManager.on("theme_created", () => projectManager.markDirty());
themeManager.on("theme_deleted", () => projectManager.markDirty());
themeManager.on("slot_color_changed", () => projectManager.markDirty());Theme Import/Export Sync
When importing animations, themes are merged with conflict resolution.
Theme Import Process:
class ThemeImporter {
async importAnimation(lottieJSON: any): Promise<void> {
// 1. Parse imported animation's themes
const importedThemes = this.parseThemes(lottieJSON);
// 2. Detect conflicts
const conflicts = this.detectConflicts(importedThemes);
// 3. Resolve conflicts
if (conflicts.length > 0) {
const resolution = await this.promptUserForResolution(conflicts);
this.applyResolution(importedThemes, resolution);
}
// 4. Merge themes
for (const theme of importedThemes) {
themeManager.addTheme(theme);
}
// 5. Merge slots
const importedSlots = this.parseSlots(lottieJSON);
for (const slot of importedSlots) {
slotManager.mergeSlot(slot);
}
}
private detectConflicts(importedThemes: Theme[]): Conflict[] {
const conflicts: Conflict[] = [];
const existingThemes = themeManager.getAllThemes();
for (const importedTheme of importedThemes) {
const existing = existingThemes.get(importedTheme.id);
if (existing) {
conflicts.push({
type: "theme_id_conflict",
existingTheme: existing,
importedTheme: importedTheme,
});
}
}
return conflicts;
}
private async promptUserForResolution(conflicts: Conflict[]): Promise<ResolutionStrategy> {
// Show dialog to user
return await showConflictDialog({
conflicts,
options: [
{ value: "rename", label: "Rename imported themes" },
{ value: "merge", label: "Merge with existing themes" },
{ value: "skip", label: "Skip conflicting themes" },
{ value: "replace", label: "Replace existing themes" },
],
});
}
}Performance Characteristics
Theme operations are optimized for smooth user experience.
Operation Performance:
// Typical performance metrics
const performanceMetrics = {
createTheme: "< 1ms", // Nearly instant
deleteTheme: "< 1ms", // Nearly instant
renameTheme: "< 1ms", // Nearly instant
updateSlotColor: "< 5ms", // Color update + slot propagation
switchActiveTheme: "10-50ms", // Depends on slot count and complexity
validateTheme: "< 5ms", // Check all slots defined
exportThemes: "50-200ms", // Depends on theme count and file size
importThemes: "100-500ms", // Depends on file size and conflicts
};Optimization Techniques:
// 1. Batched Updates
class ThemeManager {
private updateBatch: Map<string, Color> = new Map();
private batchScheduled = false;
updateSlotColor(themeId: string, slotId: string, color: Color): void {
// Add to batch
this.updateBatch.set(`${themeId}:${slotId}`, color);
// Schedule batch processing
if (!this.batchScheduled) {
this.batchScheduled = true;
requestAnimationFrame(() => this.processBatch());
}
}
private processBatch(): void {
// Process all updates at once
for (const [key, color] of this.updateBatch) {
const [themeId, slotId] = key.split(":");
this.applyColorUpdate(themeId, slotId, color);
}
// Clear batch
this.updateBatch.clear();
this.batchScheduled = false;
// Single render
canvas.render();
}
}
// 2. Lazy Validation
class ThemeManager {
private validationCache: Map<string, ValidationResult> = new Map();
validateTheme(themeId: string): ValidationResult {
// Check cache
if (this.validationCache.has(themeId)) {
return this.validationCache.get(themeId);
}
// Perform validation
const result = this.performValidation(themeId);
// Cache result
this.validationCache.set(themeId, result);
return result;
}
invalidateValidationCache(themeId?: string): void {
if (themeId) {
this.validationCache.delete(themeId);
} else {
this.validationCache.clear();
}
}
}Best Practices
Tips for effective theme management.
Define All Slots
Complete Themes:
- •Every theme should define all color slots
- •Undefined slots cause inconsistency
- •Theme Manager warns about undefined slots
- •Define all before exporting
Verification:
- •Review each theme
- •Check for undefined slot warnings
- •Test each theme thoroughly
- •Ensure no missing colors
Maintain Contrast
Accessibility:
- •Text must meet WCAG contrast ratios (4.5:1 minimum for body text)
- •Test contrast in both themes
- •Use contrast checker tools
- •Adjust colors for readability
Contrast Tips:
- •Light theme: Dark text on light background
- •Dark theme: Light text on dark background
- •Sufficient contrast for all text
- •Test with actual users if possible
Test Both Themes
Quality Assurance:
- •Preview animation in each theme
- •Verify all elements visible
- •Check readability
- •Test on actual devices
- •Ensure smooth theme switching
Testing Checklist:
- •✅ All colors defined
- •✅ Text readable in all themes
- •✅ Proper contrast ratios
- •✅ Visual hierarchy maintained
- •✅ Animation looks good in each theme
- •✅ No broken elements
- •✅ Theme switching smooth
Keep Themes Consistent
Consistency Rules:
- •Non-brand colors should be similar across themes
- •Maintain visual hierarchy
- •Similar color relationships
- •Predictable user experience
Example:
- •If Primary is more saturated than Secondary in Light theme
- •Primary should be more saturated in Dark theme too
- •Maintain color relationships
Document Themes
Theme Documentation:
- •Document purpose of each theme
- •Note color values and rationale
- •Guide for future updates
- •Handoff to developers/designers
Documentation Example:
Light Theme:
- Purpose: Daytime use, standard mode
- Background: #FFFFFF (pure white)
- Primary: #2196F3 (brand blue)
- Text: #212121 (near-black, WCAG AAA)
Dark Theme:
- Purpose: Low-light use, dark mode
- Background: #121212 (Material dark gray, not pure black)
- Primary: #64B5F6 (lighter blue, adjusted for dark bg)
- Text: #FFFFFF (white, WCAG AAA on background)Keyboard Shortcuts
Theme Management:
- •No standard theme-specific shortcuts
- •Use general navigation shortcuts
General:
- •Cmd/Ctrl + Z - Undo theme changes
- •Cmd/Ctrl + Y - Redo
- •Enter - Confirm theme name edit
- •Escape - Cancel edit or close panel
See Complete Keyboard Shortcuts for all shortcuts.
Common Issues
"Can't find Theme Manager"
- •Check left sidebar for "Themes" or "Theme Manager"
- •Look in Window or View menu
- •Feature may require specific Lottie Creator version
- •Check panel tabs/switcher
"Theme not updating canvas"
- •Verify theme is applied (active)
- •Check properties are linked to slots
- •Ensure slots defined in theme
- •Refresh editor if needed
- •Try switching to another theme and back
"Undefined slots warning"
- •Some slots not defined in current theme
- •Go through slot list
- •Assign color to each undefined slot
- •Warning should disappear
- •All themes should define all slots
"Theme exported but not working"
- •Verify using dotLottie format (themes require dotLottie)
- •Check player supports themes (Lottie-web 5.10+)
- •Ensure themes properly exported
- •Verify theme selection in player code
- •Test with modern player
"Can't delete theme"
- •Must have at least one theme
- •Cannot delete active theme (apply different theme first)
- •Check if theme is default (change default first)
- •Verify permissions
"Colors not changing when editing theme"
- •Ensure correct theme selected for editing
- •Verify editing active theme (changes show immediately)
- •If editing inactive theme, apply it to see changes
- •Check slot properly linked to properties
Tips for Theme Manager Success
Start with Two Themes
Begin Simple:
- •Start with Light and Dark themes
- •Most common use case
- •Easy to manage
- •Add more only if needed
- •Test thoroughly before adding complexity
Duplicate to Save Time
Use Duplication:
- •Create first theme completely
- •Duplicate for variations
- •Change only necessary colors
- •Faster than starting from scratch
- •Maintains consistency
Preview Frequently
Regular Preview:
- •Apply theme to see changes
- •Switch between themes often
- •Catch issues early
- •Verify quality continuously
- •Adjust as needed
Name Descriptively
Clear Names:
- •
"Light Mode", "Dark Mode" > "Theme 1", "Theme 2"
- •
"Brand A - Red", "Brand B - Blue" > "Theme A", "Theme B"
- •
Self-documenting
- •
Easier to manage
- •
Professional approach
Define Complete Themes
No Undefined Slots:
- •Define all slots in all themes
- •Avoid undefined slot warnings
- •Ensures consistency
- •Professional quality
- •Export-ready
Test on Target Platform
Platform Testing:
- •Export and test in actual player
- •Verify theme switching works
- •Check performance
- •Test on target devices
- •Ensure compatibility