0
# Dynamic Theming
1
2
The ThemeService provides dynamic theming capabilities for ng2-charts, allowing applications to change chart colors and styling at runtime based on user preferences or application state.
3
4
## Capabilities
5
6
### ThemeService
7
8
Injectable service that manages dynamic theme options for all charts in the application.
9
10
```typescript { .api }
11
/**
12
* Service for managing dynamic chart themes
13
* Allows runtime updates to chart styling and colors
14
*/
15
@Injectable({
16
providedIn: 'root'
17
})
18
export class ThemeService {
19
/**
20
* Observable stream of current theme options
21
* Charts automatically update when this changes
22
*/
23
colorschemesOptions: BehaviorSubject<ChartOptions | undefined>;
24
}
25
```
26
27
### Theme Configuration
28
29
Set theme options that will be applied to all charts.
30
31
```typescript { .api }
32
/**
33
* Sets global theme options for all charts
34
* Options are merged with individual chart options
35
* @param options - Chart.js options object with theme overrides
36
*/
37
setColorschemesOptions(options: ChartConfiguration['options']): void;
38
39
/**
40
* Gets the current theme options
41
* @returns Current theme configuration or undefined
42
*/
43
getColorschemesOptions(): ChartConfiguration['options'];
44
```
45
46
**Usage Examples:**
47
48
```typescript
49
import { Component, OnInit } from '@angular/core';
50
import { BaseChartDirective, ThemeService } from 'ng2-charts';
51
import { ChartOptions, ChartData } from 'chart.js';
52
53
@Component({
54
selector: 'app-theme-demo',
55
template: `
56
<div class="theme-controls">
57
<button (click)="setLightTheme()">Light Theme</button>
58
<button (click)="setDarkTheme()">Dark Theme</button>
59
<button (click)="setCustomTheme()">Custom Theme</button>
60
</div>
61
62
<canvas baseChart [data]="chartData" [type]="'bar'"></canvas>
63
`,
64
standalone: true,
65
imports: [BaseChartDirective]
66
})
67
export class ThemeDemoComponent implements OnInit {
68
69
constructor(private themeService: ThemeService) {}
70
71
chartData: ChartData = {
72
labels: ['Red', 'Blue', 'Yellow', 'Green', 'Purple', 'Orange'],
73
datasets: [{
74
label: 'Sample Data',
75
data: [12, 19, 3, 5, 2, 3]
76
}]
77
};
78
79
setLightTheme() {
80
const lightTheme: ChartOptions = {
81
plugins: {
82
legend: {
83
labels: {
84
color: '#333333'
85
}
86
}
87
},
88
scales: {
89
x: {
90
ticks: {
91
color: '#333333'
92
},
93
grid: {
94
color: 'rgba(0, 0, 0, 0.1)'
95
}
96
},
97
y: {
98
ticks: {
99
color: '#333333'
100
},
101
grid: {
102
color: 'rgba(0, 0, 0, 0.1)'
103
}
104
}
105
}
106
};
107
108
this.themeService.setColorschemesOptions(lightTheme);
109
}
110
111
setDarkTheme() {
112
const darkTheme: ChartOptions = {
113
plugins: {
114
legend: {
115
labels: {
116
color: '#ffffff'
117
}
118
}
119
},
120
scales: {
121
x: {
122
ticks: {
123
color: '#ffffff'
124
},
125
grid: {
126
color: 'rgba(255, 255, 255, 0.1)'
127
}
128
},
129
y: {
130
ticks: {
131
color: '#ffffff'
132
},
133
grid: {
134
color: 'rgba(255, 255, 255, 0.1)'
135
}
136
}
137
}
138
};
139
140
this.themeService.setColorschemesOptions(darkTheme);
141
}
142
143
setCustomTheme() {
144
const customTheme: ChartOptions = {
145
plugins: {
146
legend: {
147
labels: {
148
color: '#ff6b6b',
149
font: {
150
size: 14,
151
weight: 'bold'
152
}
153
}
154
}
155
},
156
scales: {
157
x: {
158
ticks: {
159
color: '#4ecdc4'
160
},
161
grid: {
162
color: 'rgba(78, 205, 196, 0.3)'
163
}
164
},
165
y: {
166
ticks: {
167
color: '#4ecdc4'
168
},
169
grid: {
170
color: 'rgba(78, 205, 196, 0.3)'
171
}
172
}
173
}
174
};
175
176
this.themeService.setColorschemesOptions(customTheme);
177
}
178
}
179
```
180
181
## Theme Override Behavior
182
183
The ThemeService uses a special merging behavior for theme options:
184
185
- **Simple fields**: Direct replacement of matching fields in chart options
186
- **Arrays**: Single object in theme array acts as template for all elements in chart array
187
- **Objects**: Deep merge with existing chart options
188
189
### Array Override Example
190
191
```typescript
192
// Theme setting that affects ALL axes
193
const themeOptions: ChartOptions = {
194
scales: {
195
x: [{ // Single object template
196
ticks: { color: 'white' },
197
gridLines: { color: 'rgba(255,255,255,0.1)' }
198
}],
199
y: [{ // Single object template
200
ticks: { color: 'white' },
201
gridLines: { color: 'rgba(255,255,255,0.1)' }
202
}]
203
}
204
};
205
206
// This will apply the styling to ALL x and y axes in ALL charts
207
themeService.setColorschemesOptions(themeOptions);
208
```
209
210
## Advanced Theming Patterns
211
212
### Reactive Theme Switching
213
214
```typescript
215
import { Component } from '@angular/core';
216
import { BaseChartDirective, ThemeService } from 'ng2-charts';
217
import { ChartOptions } from 'chart.js';
218
import { BehaviorSubject } from 'rxjs';
219
220
type Theme = 'light' | 'dark' | 'blue';
221
222
@Component({
223
selector: 'app-reactive-theme',
224
template: `
225
<select (change)="onThemeChange($event)">
226
<option value="light">Light</option>
227
<option value="dark">Dark</option>
228
<option value="blue">Blue</option>
229
</select>
230
`,
231
standalone: true,
232
imports: [BaseChartDirective]
233
})
234
export class ReactiveThemeComponent {
235
private themeSubject = new BehaviorSubject<Theme>('light');
236
237
constructor(private themeService: ThemeService) {
238
this.themeSubject.subscribe(theme => {
239
this.applyTheme(theme);
240
});
241
}
242
243
onThemeChange(event: any) {
244
this.themeSubject.next(event.target.value as Theme);
245
}
246
247
private applyTheme(theme: Theme) {
248
const themes = {
249
light: {
250
plugins: {
251
legend: { labels: { color: '#333' } }
252
},
253
scales: {
254
x: { ticks: { color: '#333' } },
255
y: { ticks: { color: '#333' } }
256
}
257
},
258
dark: {
259
plugins: {
260
legend: { labels: { color: '#fff' } }
261
},
262
scales: {
263
x: { ticks: { color: '#fff' } },
264
y: { ticks: { color: '#fff' } }
265
}
266
},
267
blue: {
268
plugins: {
269
legend: { labels: { color: '#2196f3' } }
270
},
271
scales: {
272
x: { ticks: { color: '#2196f3' } },
273
y: { ticks: { color: '#2196f3' } }
274
}
275
}
276
};
277
278
this.themeService.setColorschemesOptions(themes[theme]);
279
}
280
}
281
```
282
283
### Theme Persistence
284
285
```typescript
286
import { Injectable } from '@angular/core';
287
import { ThemeService } from 'ng2-charts';
288
import { ChartOptions } from 'chart.js';
289
290
@Injectable({
291
providedIn: 'root'
292
})
293
export class PersistentThemeService {
294
private readonly THEME_KEY = 'chart-theme';
295
296
constructor(private themeService: ThemeService) {
297
this.loadSavedTheme();
298
}
299
300
setTheme(options: ChartOptions) {
301
this.themeService.setColorschemesOptions(options);
302
localStorage.setItem(this.THEME_KEY, JSON.stringify(options));
303
}
304
305
private loadSavedTheme() {
306
const saved = localStorage.getItem(this.THEME_KEY);
307
if (saved) {
308
try {
309
const theme = JSON.parse(saved);
310
this.themeService.setColorschemesOptions(theme);
311
} catch (e) {
312
console.warn('Failed to load saved theme:', e);
313
}
314
}
315
}
316
}
317
```
318
319
### CSS Variable Integration
320
321
```typescript
322
import { Component, OnInit } from '@angular/core';
323
import { BaseChartDirective, ThemeService } from 'ng2-charts';
324
import { ChartOptions, ChartData } from 'chart.js';
325
326
@Component({
327
selector: 'app-css-theme',
328
template: `<canvas baseChart [data]="chartData" [type]="'line'"></canvas>`,
329
styles: [`
330
:host {
331
--primary-color: #2196f3;
332
--text-color: #333333;
333
--grid-color: rgba(0, 0, 0, 0.1);
334
}
335
336
:host.dark-theme {
337
--primary-color: #90caf9;
338
--text-color: #ffffff;
339
--grid-color: rgba(255, 255, 255, 0.1);
340
}
341
`],
342
standalone: true,
343
imports: [BaseChartDirective]
344
})
345
export class CssThemeComponent implements OnInit {
346
347
chartData: ChartData = {
348
labels: ['January', 'February', 'March', 'April', 'May', 'June'],
349
datasets: [{
350
label: 'Sample Data',
351
data: [12, 19, 3, 5, 2, 3]
352
}]
353
};
354
355
constructor(private themeService: ThemeService) {}
356
357
ngOnInit() {
358
this.updateThemeFromCSS();
359
}
360
361
private updateThemeFromCSS() {
362
const computedStyle = getComputedStyle(document.documentElement);
363
const primaryColor = computedStyle.getPropertyValue('--primary-color').trim();
364
const textColor = computedStyle.getPropertyValue('--text-color').trim();
365
const gridColor = computedStyle.getPropertyValue('--grid-color').trim();
366
367
const theme: ChartOptions = {
368
plugins: {
369
legend: {
370
labels: {
371
color: textColor
372
}
373
}
374
},
375
scales: {
376
x: {
377
ticks: { color: textColor },
378
grid: { color: gridColor }
379
},
380
y: {
381
ticks: { color: textColor },
382
grid: { color: gridColor }
383
}
384
}
385
};
386
387
this.themeService.setColorschemesOptions(theme);
388
}
389
}
390
```
391
392
## Theme Option Types
393
394
```typescript { .api }
395
interface ThemeOptions extends ChartOptions {
396
plugins?: {
397
legend?: {
398
labels?: {
399
color?: string;
400
font?: {
401
size?: number;
402
weight?: string | number;
403
family?: string;
404
};
405
};
406
};
407
tooltip?: {
408
backgroundColor?: string;
409
titleColor?: string;
410
bodyColor?: string;
411
borderColor?: string;
412
};
413
};
414
scales?: {
415
[key: string]: {
416
ticks?: {
417
color?: string;
418
font?: {
419
size?: number;
420
family?: string;
421
};
422
};
423
grid?: {
424
color?: string;
425
borderColor?: string;
426
};
427
};
428
};
429
}
430
```