0
# Theme System
1
2
Theme system providing Snow and Bubble themes with customizable UI components and toolbar layouts. Themes control the visual presentation and user interface elements of the Quill editor, including toolbar positioning, styling, and interactive behaviors.
3
4
## Capabilities
5
6
### Base Theme Class
7
8
Abstract base class that all Quill themes extend, providing common UI management functionality.
9
10
```typescript { .api }
11
/**
12
* Base theme class for UI management
13
*/
14
abstract class Theme {
15
/** Default theme configuration options */
16
static DEFAULTS: Record<string, unknown>;
17
18
/** Theme registry for available themes */
19
static themes: Record<string, ThemeConstructor>;
20
21
/** Quill instance this theme belongs to */
22
quill: Quill;
23
24
/** Theme configuration options */
25
options: Record<string, unknown>;
26
27
/** Registered modules for this theme */
28
modules: Record<string, Module>;
29
30
/**
31
* Create theme instance
32
* @param quill - Quill editor instance
33
* @param options - Theme configuration options
34
*/
35
constructor(quill: Quill, options: Record<string, unknown>);
36
37
/**
38
* Initialize theme after construction
39
*/
40
init(): void;
41
42
/**
43
* Add and initialize module
44
* @param name - Module name
45
* @returns Initialized module instance
46
*/
47
addModule(name: string): Module;
48
}
49
50
interface ThemeOptions {
51
[key: string]: unknown;
52
}
53
54
interface ThemeConstructor {
55
new (quill: Quill, options: ThemeOptions): Theme;
56
DEFAULTS: ThemeOptions;
57
}
58
```
59
60
**Usage Examples:**
61
62
```typescript
63
// Define custom theme
64
class CustomTheme extends Theme {
65
static DEFAULTS = {
66
modules: {
67
toolbar: [
68
['bold', 'italic'],
69
['link']
70
]
71
}
72
};
73
74
constructor(quill, options) {
75
super(quill, options);
76
this.buildCustomUI();
77
}
78
79
init() {
80
super.init();
81
this.setupCustomBehaviors();
82
}
83
84
buildCustomUI() {
85
// Custom UI setup
86
}
87
}
88
89
// Register custom theme
90
Quill.register('themes/custom', CustomTheme);
91
92
// Use custom theme
93
const quill = new Quill('#editor', {
94
theme: 'custom'
95
});
96
```
97
98
### Snow Theme
99
100
Professional theme with toolbar positioned above the editor, suitable for document editing interfaces.
101
102
```typescript { .api }
103
class SnowTheme extends BaseTheme {
104
static DEFAULTS: {
105
modules: {
106
toolbar: ToolbarConfig;
107
};
108
};
109
110
/**
111
* Extend toolbar with Snow-specific features
112
* @param toolbar - Toolbar module instance
113
*/
114
extendToolbar(toolbar: Toolbar): void;
115
116
/**
117
* Build Snow theme UI
118
*/
119
buildTheme(): void;
120
}
121
122
class SnowTooltip extends BaseTooltip {
123
/** Root element for tooltip */
124
root: HTMLElement;
125
126
/**
127
* Listen for tooltip events
128
*/
129
listen(): void;
130
131
/**
132
* Show tooltip at position
133
*/
134
show(): void;
135
136
/**
137
* Hide tooltip
138
*/
139
hide(): void;
140
141
/**
142
* Position tooltip relative to reference
143
* @param reference - Reference element or bounds
144
*/
145
position(reference: HTMLElement | Bounds): void;
146
}
147
```
148
149
**Usage Examples:**
150
151
```typescript
152
// Use Snow theme (default)
153
const quill = new Quill('#editor', {
154
theme: 'snow',
155
modules: {
156
toolbar: [
157
[{ 'header': [1, 2, 3, false] }],
158
['bold', 'italic', 'underline'],
159
[{ 'color': [] }, { 'background': [] }],
160
[{ 'list': 'ordered'}, { 'list': 'bullet' }],
161
[{ 'align': [] }],
162
['link', 'image', 'video'],
163
['clean']
164
]
165
}
166
});
167
168
// Snow theme with custom toolbar container
169
const quill2 = new Quill('#editor2', {
170
theme: 'snow',
171
modules: {
172
toolbar: {
173
container: '#toolbar-container'
174
}
175
}
176
});
177
178
// Access Snow-specific features
179
const toolbar = quill.getModule('toolbar');
180
// Snow theme adds link editing functionality
181
```
182
183
### Bubble Theme
184
185
Contextual theme with floating toolbar that appears near selected text, suitable for inline editing scenarios.
186
187
```typescript { .api }
188
class BubbleTheme extends BaseTheme {
189
static DEFAULTS: {
190
modules: {
191
toolbar: ToolbarConfig;
192
};
193
};
194
195
/**
196
* Extend toolbar with Bubble-specific features
197
* @param toolbar - Toolbar module instance
198
*/
199
extendToolbar(toolbar: Toolbar): void;
200
201
/**
202
* Build Bubble theme UI
203
*/
204
buildTheme(): void;
205
}
206
207
class BubbleTooltip extends BaseTooltip {
208
/**
209
* Show tooltip at current selection
210
*/
211
show(): void;
212
213
/**
214
* Hide tooltip
215
*/
216
hide(): void;
217
218
/**
219
* Position tooltip near selection
220
*/
221
position(): void;
222
}
223
```
224
225
**Usage Examples:**
226
227
```typescript
228
// Use Bubble theme
229
const quill = new Quill('#editor', {
230
theme: 'bubble',
231
modules: {
232
toolbar: [
233
['bold', 'italic'],
234
['link'],
235
[{ 'header': 1 }, { 'header': 2 }]
236
]
237
}
238
});
239
240
// Bubble theme shows toolbar on text selection
241
// No permanent toolbar is visible
242
243
// Custom bubble configuration
244
const quill2 = new Quill('#editor2', {
245
theme: 'bubble',
246
bounds: '#editor-container', // Constrain bubble to container
247
modules: {
248
toolbar: [
249
['bold', 'italic', 'underline'],
250
['link']
251
]
252
}
253
});
254
```
255
256
### Base Theme Components
257
258
Common UI components used across themes.
259
260
```typescript { .api }
261
/**
262
* Base theme with common UI functionality
263
*/
264
class BaseTheme extends Theme {
265
/** Array of picker instances */
266
pickers: Picker[];
267
268
/** Tooltip instance */
269
tooltip: BaseTooltip;
270
271
/**
272
* Build toolbar buttons
273
* @param buttons - Button elements
274
* @param icons - Icon definitions
275
*/
276
buildButtons(buttons: HTMLElement[], icons: Record<string, string>): void;
277
278
/**
279
* Build picker controls
280
* @param selects - Select elements
281
* @param icons - Icon definitions
282
*/
283
buildPickers(selects: HTMLElement[], icons: Record<string, string>): void;
284
}
285
286
/**
287
* Base tooltip functionality
288
*/
289
class BaseTooltip extends Tooltip {
290
/** Text input element */
291
textbox: HTMLInputElement;
292
293
/** Current link range being edited */
294
linkRange: Range | null;
295
296
/**
297
* Enter edit mode
298
* @param mode - Edit mode ('link', 'video', 'formula')
299
* @param preview - Preview element
300
*/
301
edit(mode?: string, preview?: HTMLElement): void;
302
303
/**
304
* Cancel editing
305
*/
306
cancel(): void;
307
308
/**
309
* Save changes
310
*/
311
save(): void;
312
313
/**
314
* Set up event listeners
315
*/
316
listen(): void;
317
318
/**
319
* Restore focus to editor
320
*/
321
restoreFocus(): void;
322
}
323
```
324
325
**Usage Examples:**
326
327
```typescript
328
// Access theme components
329
const quill = new Quill('#editor', { theme: 'snow' });
330
331
// Get tooltip for custom functionality
332
const theme = quill.theme;
333
if (theme.tooltip) {
334
// Custom tooltip behavior
335
theme.tooltip.edit('link');
336
}
337
338
// Access pickers
339
if (theme.pickers) {
340
theme.pickers.forEach(picker => {
341
// Custom picker behavior
342
picker.update();
343
});
344
}
345
```
346
347
### UI Components
348
349
Reusable UI components used by themes.
350
351
```typescript { .api }
352
/**
353
* Dropdown picker component
354
*/
355
class Picker {
356
/** Original select element */
357
select: HTMLSelectElement;
358
359
/** Picker container element */
360
container: HTMLElement;
361
362
/** Picker label element */
363
label: HTMLElement;
364
365
/** Picker options container */
366
options: HTMLElement;
367
368
/**
369
* Build picker UI from select element
370
*/
371
buildPicker(): void;
372
373
/**
374
* Close picker dropdown
375
*/
376
close(): void;
377
378
/**
379
* Escape picker (close without selection)
380
*/
381
escape(): void;
382
383
/**
384
* Select item
385
* @param item - Item to select
386
* @param trigger - Whether to trigger change event
387
*/
388
selectItem(item: HTMLElement, trigger?: boolean): void;
389
390
/**
391
* Toggle picker visibility
392
*/
393
togglePicker(): void;
394
395
/**
396
* Update picker state
397
*/
398
update(): void;
399
}
400
401
/**
402
* Color picker component
403
*/
404
class ColorPicker extends Picker {
405
/**
406
* Build color picker with color swatches
407
*/
408
buildItem(option: HTMLOptionElement): HTMLElement;
409
}
410
411
/**
412
* Icon picker component
413
*/
414
class IconPicker extends Picker {
415
/**
416
* Build icon picker with SVG icons
417
*/
418
buildItem(option: HTMLOptionElement): HTMLElement;
419
}
420
421
/**
422
* Tooltip component
423
*/
424
class Tooltip {
425
/** Root tooltip element */
426
root: HTMLElement;
427
428
/** Quill instance */
429
quill: Quill;
430
431
/** Bounds for positioning */
432
boundsContainer: HTMLElement;
433
434
/**
435
* Hide tooltip
436
*/
437
hide(): void;
438
439
/**
440
* Position tooltip relative to reference
441
* @param reference - Reference element or bounds
442
*/
443
position(reference: HTMLElement | Bounds): void;
444
445
/**
446
* Show tooltip
447
*/
448
show(): void;
449
}
450
```
451
452
**Usage Examples:**
453
454
```typescript
455
// Access UI components
456
const quill = new Quill('#editor', {
457
theme: 'snow',
458
modules: {
459
toolbar: [
460
[{ 'color': [] }],
461
[{ 'background': [] }]
462
]
463
}
464
});
465
466
const toolbar = quill.getModule('toolbar');
467
const theme = quill.theme;
468
469
// Find color pickers
470
const colorPickers = theme.pickers.filter(picker => {
471
return picker.select.classList.contains('ql-color');
472
});
473
474
// Custom picker behavior
475
colorPickers.forEach(picker => {
476
picker.container.addEventListener('click', (e) => {
477
console.log('Color picker clicked');
478
});
479
});
480
```
481
482
### Theme Customization
483
484
Customize existing themes or create new ones.
485
486
```typescript { .api }
487
// Extend Snow theme
488
class CustomSnowTheme extends SnowTheme {
489
constructor(quill, options) {
490
super(quill, options);
491
this.addCustomStyles();
492
}
493
494
addCustomStyles() {
495
// Add custom CSS classes
496
this.quill.container.classList.add('custom-snow');
497
}
498
499
extendToolbar(toolbar) {
500
super.extendToolbar(toolbar);
501
502
// Add custom toolbar functionality
503
toolbar.addHandler('custom', (value) => {
504
// Custom handler
505
});
506
}
507
}
508
509
// Register custom theme
510
Quill.register('themes/custom-snow', CustomSnowTheme);
511
```
512
513
**Usage Examples:**
514
515
```typescript
516
// Theme with custom CSS
517
const quill = new Quill('#editor', {
518
theme: 'snow'
519
});
520
521
// Add custom CSS classes
522
quill.container.classList.add('dark-theme');
523
524
// Custom theme configuration
525
const quill2 = new Quill('#editor2', {
526
theme: 'snow',
527
modules: {
528
toolbar: {
529
container: [
530
[{ 'header': [1, 2, 3, false] }],
531
['bold', 'italic'],
532
['custom-button'] // Custom button
533
],
534
handlers: {
535
'custom-button': () => {
536
console.log('Custom button clicked');
537
}
538
}
539
}
540
}
541
});
542
543
// No theme (base editor only)
544
const quill3 = new Quill('#editor3'); // No theme specified
545
546
// Theme switching
547
function switchTheme(themeName) {
548
// Destroy current editor
549
const content = quill.getContents();
550
quill.container.innerHTML = '';
551
552
// Create new editor with different theme
553
const newQuill = new Quill(quill.container, {
554
theme: themeName,
555
modules: quill.options.modules
556
});
557
558
// Restore content
559
newQuill.setContents(content);
560
561
return newQuill;
562
}
563
564
// Switch to bubble theme
565
const bubbleQuill = switchTheme('bubble');
566
```
567
568
### Theme Events and Integration
569
570
Themes can listen to editor events and provide custom behaviors.
571
572
```typescript { .api }
573
class EventfulTheme extends Theme {
574
constructor(quill, options) {
575
super(quill, options);
576
this.setupEventListeners();
577
}
578
579
setupEventListeners() {
580
this.quill.on('selection-change', this.onSelectionChange.bind(this));
581
this.quill.on('text-change', this.onTextChange.bind(this));
582
}
583
584
onSelectionChange(range, oldRange, source) {
585
// Theme-specific selection handling
586
if (range) {
587
this.showContextualUI(range);
588
} else {
589
this.hideContextualUI();
590
}
591
}
592
593
onTextChange(delta, oldDelta, source) {
594
// Theme-specific text change handling
595
this.updateUI();
596
}
597
}
598
```
599
600
**Usage Examples:**
601
602
```typescript
603
// Theme with custom event handling
604
class InteractiveTheme extends SnowTheme {
605
constructor(quill, options) {
606
super(quill, options);
607
608
// Add selection highlighting
609
this.quill.on('selection-change', (range) => {
610
if (range && range.length > 0) {
611
this.highlightSelection(range);
612
} else {
613
this.clearHighlight();
614
}
615
});
616
}
617
618
highlightSelection(range) {
619
// Add visual selection indicator
620
const bounds = this.quill.getBounds(range);
621
if (bounds) {
622
this.showSelectionIndicator(bounds);
623
}
624
}
625
626
clearHighlight() {
627
this.hideSelectionIndicator();
628
}
629
}
630
631
Quill.register('themes/interactive', InteractiveTheme);
632
633
const quill = new Quill('#editor', {
634
theme: 'interactive'
635
});
636
```