GoJS v3.0+ introduces comprehensive theme management capabilities that allow for dynamic switching between light, dark, and custom themes. The theme system provides centralized control over colors, fonts, and other visual properties across all diagram elements.
The ThemeManager coordinates theme switching and maintains theme data for one or more diagrams.
class ThemeManager {
/**
* Creates a new ThemeManager with default light and dark themes.
* @param init - Optional initialization properties
*/
constructor(init?: Partial<ThemeManager>);
// Theme Collections
themeMap: Map<string, Theme>;
// Active Theme Configuration
currentTheme: string; // Current active theme name ('light', 'dark', 'system', or custom)
defaultTheme: string; // Fallback theme name (default: 'light')
preferredColorScheme: 'light' | 'dark'; // Browser's preferred color scheme (read-only)
// Integration Options
changesDivBackground: boolean; // Whether to update diagram div background color
// Diagram Management
addDiagram(diagram: Diagram): this;
removeDiagram(diagram: Diagram): this;
// Theme Operations
set(themeName: string, props: Partial<Theme>): this;
findValue(prop: string | string[] | number, source?: string | string[], tprop?: string): any;
getValue(theme: Theme, prop: string | string[] | number, source?: string | string[], tprop?: string): any;
findTheme(themeName: string): Theme | undefined;
}Usage Examples:
// Basic theme setup with automatic div background
const themeManager = new go.ThemeManager({
currentTheme: 'system', // Follow browser preference
changesDivBackground: true // Update div background color
});
// Add diagram to theme management
themeManager.addDiagram(myDiagram);
myDiagram.themeManager = themeManager;
// Switch themes programmatically
themeManager.currentTheme = 'dark';
// Custom theme creation
themeManager.set('custom', {
colors: {
text: '#333333',
group: '#e3f2fd',
selection: '#1976d2'
}
});
// Apply custom theme
themeManager.currentTheme = 'custom';Static class providing default light and dark theme definitions.
class Themes {
static readonly Light: Theme; // Default light theme
static readonly Dark: Theme; // Default dark theme
}Defines the structure for theme objects containing colors, fonts, and other visual properties.
interface Theme {
colors?: ThemeColors; // Color palette
fonts?: ThemeValues<string>; // Font definitions
numbers?: ThemeValues<number>; // Numeric values (stroke widths, etc.)
points?: ThemeValues<Point>; // Point coordinates
sizes?: ThemeValues<Size>; // Size definitions
rects?: ThemeValues<Rect>; // Rectangle definitions
margins?: ThemeValues<Margin>; // Margin definitions
spots?: ThemeValues<Spot>; // Spot definitions
arrowheads?: ThemeValues<string>; // Arrowhead styles
targetPropertyMap?: Map<string, string>; // Property mapping overrides
[index: string]: ThemeValues<any> | undefined; // Custom properties
}
interface ThemeColors {
text?: BrushLike; // Text color for nodes and labels
comment?: BrushLike; // Comment/annotation color
link?: BrushLike; // Link stroke/fill color
group?: BrushLike; // Group background color
outline?: BrushLike; // Group border color
selection?: BrushLike; // Selection highlight color
div?: string; // Diagram div background color
gridMinor?: BrushLike; // Minor grid line color
gridMajor?: BrushLike; // Major grid line color
overviewBox?: BrushLike; // Overview viewport box color
tempLink?: BrushLike; // Temporary link color during linking
tempPort?: BrushLike; // Temporary port highlight color
adornmentFill?: BrushLike; // Tool handle fill color
adornmentStroke?: BrushLike; // Tool handle border color
dragSelect?: BrushLike; // Drag selection box color
[index: string]: BrushLike | BrushLike[] | ThemeColors | undefined;
}
interface ThemeValues<T> {
[index: string]: T;
}Specialized binding class that automatically updates properties based on theme changes.
class ThemeBinding extends Binding {
/**
* Creates a new ThemeBinding.
* @param targetprop - Target property name on the GraphObject
* @param sourceprop - Source property path in the theme
* @param themeSource - Source object within theme (e.g., 'colors', 'fonts')
* @param conv - Converter function for the target property
* @param themeConverter - Converter function for the theme value
*/
constructor(
targetprop?: string,
sourceprop?: string,
themeSource?: string,
conv?: TargetConversion,
themeConverter?: TargetConversion
);
themeSource: string | null; // Theme source object path
themeConverter: TargetConversion; // Theme value converter function
ofData(): this; // Configure for data object source
}{
colors: {
text: '#0a0a0a', // Neutral 950 - dark text
comment: '#ca8a04', // Yellow 600 - amber comments
link: '#0a0a0a', // Neutral 950 - dark links
group: '#a3a3a344', // Neutral 400 - light gray groups (transparent)
outline: '#a3a3a3', // Neutral 400 - gray outlines
selection: '#0ea5e9', // Sky 500 - blue selection
div: '#fff', // White background
gridMinor: '#e5e5e5', // Neutral 200 - light grid
gridMajor: '#a3a3a3', // Neutral 400 - darker grid
overviewBox: '#c026d3', // Fuschia 600 - magenta overview
tempLink: '#2563eb', // Blue 600 - blue temporary links
tempPort: '#c026d3', // Fuschia 600 - magenta ports
adornmentFill: '#0ea5e9', // Sky 500 - blue handles
adornmentStroke: '#1e40af', // Blue 800 - dark blue borders
dragSelect: '#c026d3' // Fuschia 600 - magenta selection box
},
fonts: {
normal: '10pt sans-serif',
bold: 'bold 12pt sans-serif'
},
numbers: {
group: 1, // Group stroke width
selection: 3 // Selection stroke width
},
margins: {
group: new go.Margin(5) // Group padding
},
arrowheads: {
toArrow: 'Standard'
}
}{
colors: {
text: '#f5f5f5', // Neutral 100 - light text
comment: '#facc15', // Yellow 400 - bright amber comments
link: '#f5f5f5', // Neutral 100 - light links
group: '#a3a3a388', // Neutral 400 - gray groups (more transparent)
outline: '#a3a3a3', // Neutral 400 - gray outlines
selection: '#38bdf8', // Sky 400 - lighter blue selection
div: '#171717', // Neutral 900 - dark background
gridMinor: '#262626', // Neutral 800 - dark grid
gridMajor: '#404040', // Neutral 700 - lighter grid
overviewBox: '#e879f9', // Fuschia 400 - lighter magenta overview
tempLink: '#60a5fa', // Blue 400 - lighter blue temporary links
tempPort: '#e879f9', // Fuschia 400 - lighter magenta ports
adornmentFill: '#38bdf8', // Sky 400 - lighter blue handles
adornmentStroke: '#2563eb', // Blue 600 - blue borders
dragSelect: '#e879f9' // Fuschia 400 - lighter magenta selection box
},
fonts: {
normal: '10pt sans-serif',
bold: 'bold 12pt sans-serif'
},
numbers: {
group: 1, // Group stroke width
selection: 3 // Selection stroke width
},
margins: {
group: new go.Margin(5) // Group padding
},
arrowheads: {
toArrow: 'Standard'
}
}Theme bindings automatically update GraphObject properties when themes change.
All GraphObjects provide convenience methods for creating theme bindings:
// Standard theme binding
theme(targetprop: string, sourceprop?: string, themeSource?: string, conv?: TargetConversion, themeconv?: TargetConversion): this;
// Theme binding to data object
themeData(targetprop: string, sourceprop?: string, themeSource?: string, conv?: TargetConversion, themeconv?: TargetConversion): this;
// Theme binding to named object
themeObject(targetprop: string, sourceprop?: string, objectSrcname?: string, themeSource?: string, conv?: TargetConversion, themeconv?: TargetConversion): this;
// Theme binding to model
themeModel(targetprop: string, sourceprop?: string, themeSource?: string, conv?: TargetConversion, themeconv?: TargetConversion): this;// Theme-aware node template
myDiagram.nodeTemplate =
new go.Node('Auto')
.add(
new go.Shape('RoundedRectangle', {
fill: 'white',
strokeWidth: 1
})
.theme('fill', 'group', 'colors') // Use theme's group color
.theme('stroke', 'outline', 'colors'), // Use theme's outline color
new go.TextBlock({
margin: 8,
font: '12pt sans-serif'
})
.theme('stroke', 'text', 'colors') // Use theme's text color
.theme('font', 'normal', 'fonts') // Use theme's normal font
.bind('text', 'name')
);
// Theme-aware link template
myDiagram.linkTemplate =
new go.Link({ routing: go.Routing.Orthogonal })
.add(
new go.Shape({ strokeWidth: 2 })
.theme('stroke', 'link', 'colors'), // Use theme's link color
new go.Shape({ toArrow: 'Standard' })
.theme('stroke', 'link', 'colors') // Use theme's link color
.theme('toArrow', 'toArrow', 'arrowheads') // Use theme's arrow style
);
// Complex theme binding with converter
new go.Shape()
.theme('fill', 'selection', 'colors', null, (color) => {
return go.Brush.lighten(color, 0.3); // Lighten selection color by 30%
});// Create a custom business theme
themeManager.set('business', {
colors: {
text: '#2c3e50',
group: '#ecf0f1',
outline: '#34495e',
selection: '#3498db',
link: '#34495e',
div: '#ffffff'
},
fonts: {
normal: '11pt "Segoe UI", Arial, sans-serif',
bold: 'bold 13pt "Segoe UI", Arial, sans-serif'
},
numbers: {
group: 2,
selection: 4
}
});
// High contrast theme for accessibility
themeManager.set('high-contrast', {
colors: {
text: '#000000',
group: '#ffffff',
outline: '#000000',
selection: '#ffff00',
link: '#000000',
div: '#ffffff'
},
numbers: {
group: 3,
selection: 5
}
});// Automatic theme switching based on system preference
const themeManager = new go.ThemeManager({
currentTheme: 'system',
changesDivBackground: true
});
// Listen for system theme changes
if (window.matchMedia) {
const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)');
mediaQuery.addListener(() => {
// ThemeManager automatically handles system preference changes
myDiagram.updateAllThemeBindings();
});
}
// Manual theme switching UI
function switchTheme(themeName) {
themeManager.currentTheme = themeName;
// All theme bindings update automatically
}// Validate theme completeness
function validateTheme(theme) {
const requiredColors = ['text', 'group', 'outline', 'selection', 'link'];
const missing = requiredColors.filter(color => !theme.colors?.[color]);
if (missing.length > 0) {
console.warn('Theme missing required colors:', missing);
}
return missing.length === 0;
}
// Apply theme with validation
if (validateTheme(myCustomTheme)) {
themeManager.set('custom', myCustomTheme);
themeManager.currentTheme = 'custom';
}The GoJS v3.0+ theme system provides powerful, centralized control over diagram appearance with seamless switching between predefined and custom themes, automatic system integration, and comprehensive binding capabilities.