0
# Theming
1
2
Comprehensive theming system for customizing colors, typography, spacing, and visual design across all Vuetify components with Material Design integration.
3
4
## Capabilities
5
6
### ThemeDefinition Interface
7
8
Core theme configuration interface that defines the structure for custom themes.
9
10
```typescript { .api }
11
/**
12
* Theme definition interface for creating custom themes
13
*/
14
interface ThemeDefinition {
15
/** Whether the theme is dark mode */
16
dark: boolean;
17
/** Color palette configuration */
18
colors: ThemeColors;
19
/** CSS custom properties and variables */
20
variables: ThemeVariables;
21
}
22
23
interface ThemeColors {
24
/** Primary brand color */
25
primary?: string;
26
/** Primary color variant */
27
'primary-darken-1'?: string;
28
/** Secondary brand color */
29
secondary?: string;
30
/** Secondary color variant */
31
'secondary-darken-1'?: string;
32
/** Accent color for highlights */
33
accent?: string;
34
/** Error state color */
35
error?: string;
36
/** Warning state color */
37
warning?: string;
38
/** Info state color */
39
info?: string;
40
/** Success state color */
41
success?: string;
42
/** Background color */
43
background?: string;
44
/** Surface color for cards, sheets */
45
surface?: string;
46
/** Surface variant color */
47
'surface-variant'?: string;
48
/** Text color on background */
49
'on-background'?: string;
50
/** Text color on surface */
51
'on-surface'?: string;
52
/** Text color on surface variant */
53
'on-surface-variant'?: string;
54
/** Text color on primary */
55
'on-primary'?: string;
56
/** Text color on secondary */
57
'on-secondary'?: string;
58
/** Text color on error */
59
'on-error'?: string;
60
/** Text color on warning */
61
'on-warning'?: string;
62
/** Text color on info */
63
'on-info'?: string;
64
/** Text color on success */
65
'on-success'?: string;
66
/** Outline color for borders */
67
outline?: string;
68
/** Outline variant color */
69
'outline-variant'?: string;
70
/** Inverse surface color */
71
'inverse-surface'?: string;
72
/** Text color on inverse surface */
73
'on-inverse-surface'?: string;
74
/** Inverse primary color */
75
'inverse-primary'?: string;
76
/** Shadow color */
77
shadow?: string;
78
/** Scrim overlay color */
79
scrim?: string;
80
}
81
82
interface ThemeVariables {
83
/** Border radius values */
84
'border-radius-root'?: string;
85
'border-radius-sm'?: string;
86
'border-radius-md'?: string;
87
'border-radius-lg'?: string;
88
'border-radius-xl'?: string;
89
90
/** Elevation shadows */
91
'shadow-key-umbra-opacity'?: number;
92
'shadow-key-penumbra-opacity'?: number;
93
'shadow-key-ambient-opacity'?: number;
94
95
/** Typography */
96
'font-weight-thin'?: number;
97
'font-weight-light'?: number;
98
'font-weight-regular'?: number;
99
'font-weight-medium'?: number;
100
'font-weight-bold'?: number;
101
'font-weight-black'?: number;
102
103
/** Letter spacing */
104
'letter-spacing-body'?: string;
105
'letter-spacing-button'?: string;
106
'letter-spacing-caption'?: string;
107
'letter-spacing-subtitle'?: string;
108
109
/** Line heights */
110
'line-height-sm'?: number;
111
'line-height-md'?: number;
112
'line-height-lg'?: number;
113
114
/** Spacing */
115
'spacing-xs'?: string;
116
'spacing-sm'?: string;
117
'spacing-md'?: string;
118
'spacing-lg'?: string;
119
'spacing-xl'?: string;
120
}
121
```
122
123
**Usage Examples:**
124
125
```vue
126
<template>
127
<div>
128
<!-- Theme selector -->
129
<v-select
130
v-model="selectedTheme"
131
:items="themeOptions"
132
label="Select Theme"
133
@update:model-value="applyTheme"
134
/>
135
136
<!-- Color palette preview -->
137
<v-card class="mb-4">
138
<v-card-title>Current Theme Colors</v-card-title>
139
<v-card-text>
140
<div class="color-grid">
141
<div
142
v-for="(color, name) in currentColors"
143
:key="name"
144
class="color-item"
145
>
146
<div
147
class="color-swatch"
148
:style="{
149
backgroundColor: color,
150
color: getContrastColor(name)
151
}"
152
>
153
<div class="color-name">{{ name }}</div>
154
<div class="color-value">{{ color }}</div>
155
</div>
156
</div>
157
</div>
158
</v-card-text>
159
</v-card>
160
161
<!-- Component examples with current theme -->
162
<v-row>
163
<v-col cols="12" md="6">
164
<v-card>
165
<v-card-title color="primary">Primary Components</v-card-title>
166
<v-card-text>
167
<v-btn color="primary" class="mb-2">Primary Button</v-btn><br>
168
<v-chip color="primary">Primary Chip</v-chip>
169
<v-text-field label="Primary Input" color="primary" />
170
</v-card-text>
171
</v-card>
172
</v-col>
173
174
<v-col cols="12" md="6">
175
<v-card>
176
<v-card-title color="secondary">Secondary Components</v-card-title>
177
<v-card-text>
178
<v-btn color="secondary" class="mb-2">Secondary Button</v-btn><br>
179
<v-chip color="secondary">Secondary Chip</v-chip>
180
<v-text-field label="Secondary Input" color="secondary" />
181
</v-card-text>
182
</v-card>
183
</v-col>
184
</v-row>
185
</div>
186
</template>
187
188
<script setup>
189
import { useTheme } from 'vuetify';
190
191
const theme = useTheme();
192
193
const selectedTheme = ref('light');
194
const themeOptions = [
195
{ title: 'Light Theme', value: 'light' },
196
{ title: 'Dark Theme', value: 'dark' },
197
{ title: 'Custom Blue', value: 'customBlue' },
198
{ title: 'Custom Green', value: 'customGreen' },
199
];
200
201
const customThemes = {
202
customBlue: {
203
dark: false,
204
colors: {
205
primary: '#1976D2',
206
'primary-darken-1': '#1565C0',
207
secondary: '#424242',
208
'secondary-darken-1': '#1B5E20',
209
accent: '#82B1FF',
210
error: '#FF5252',
211
info: '#2196F3',
212
success: '#4CAF50',
213
warning: '#FFC107',
214
background: '#F5F5F5',
215
surface: '#FFFFFF',
216
'on-surface': '#1D1B20'
217
},
218
variables: {
219
'border-radius-root': '8px',
220
'border-radius-sm': '4px',
221
'border-radius-lg': '16px'
222
}
223
},
224
customGreen: {
225
dark: false,
226
colors: {
227
primary: '#4CAF50',
228
'primary-darken-1': '#388E3C',
229
secondary: '#FF9800',
230
'secondary-darken-1': '#F57C00',
231
accent: '#00BCD4',
232
error: '#F44336',
233
info: '#2196F3',
234
success: '#8BC34A',
235
warning: '#FF9800',
236
background: '#E8F5E8',
237
surface: '#FFFFFF',
238
'on-surface': '#1B5E20'
239
},
240
variables: {
241
'border-radius-root': '12px',
242
'font-weight-regular': 400,
243
'font-weight-medium': 500
244
}
245
}
246
};
247
248
const currentColors = computed(() => theme.current.value.colors);
249
250
const applyTheme = (themeName) => {
251
if (customThemes[themeName]) {
252
theme.themes.value[themeName] = customThemes[themeName];
253
}
254
theme.name.value = themeName;
255
};
256
257
const getContrastColor = (colorName) => {
258
const onColorMap = {
259
'primary': 'on-primary',
260
'secondary': 'on-secondary',
261
'surface': 'on-surface',
262
'background': 'on-background',
263
'error': 'on-error',
264
'warning': 'on-warning',
265
'info': 'on-info',
266
'success': 'on-success'
267
};
268
269
return currentColors.value[onColorMap[colorName]] || '#FFFFFF';
270
};
271
272
onMounted(() => {
273
// Register custom themes
274
Object.entries(customThemes).forEach(([name, themeConfig]) => {
275
theme.themes.value[name] = themeConfig;
276
});
277
});
278
</script>
279
280
<style>
281
.color-grid {
282
display: grid;
283
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
284
gap: 16px;
285
}
286
287
.color-item {
288
border-radius: 8px;
289
overflow: hidden;
290
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
291
}
292
293
.color-swatch {
294
padding: 16px;
295
min-height: 80px;
296
display: flex;
297
flex-direction: column;
298
justify-content: center;
299
}
300
301
.color-name {
302
font-weight: 500;
303
text-transform: capitalize;
304
margin-bottom: 4px;
305
}
306
307
.color-value {
308
font-family: monospace;
309
font-size: 0.875em;
310
opacity: 0.8;
311
}
312
</style>
313
```
314
315
### Theme Options
316
317
Configuration interface for setting up the theming system during Vuetify initialization.
318
319
```typescript { .api }
320
/**
321
* Theme configuration options for Vuetify setup
322
*/
323
interface ThemeOptions {
324
/** Default theme name to use */
325
defaultTheme?: string;
326
/** Available theme definitions */
327
themes?: Record<string, ThemeDefinition>;
328
/** Color variations configuration */
329
variations?: ThemeVariations;
330
/** CSS custom properties prefix */
331
cspNonce?: string;
332
}
333
334
interface ThemeVariations {
335
/** Colors to generate variations for */
336
colors: string[];
337
/** Lighten amount for variants */
338
lighten: number;
339
/** Darken amount for variants */
340
darken: number;
341
}
342
343
/**
344
* Theme instance returned by useTheme composable
345
*/
346
interface ThemeInstance {
347
/** Current active theme */
348
current: Ref<ThemeDefinition>;
349
/** Available themes */
350
themes: Ref<Record<string, ThemeDefinition>>;
351
/** Whether theming is disabled */
352
isDisabled: Ref<boolean>;
353
/** Current theme name */
354
name: Ref<string>;
355
/** Computed theme styles */
356
computedThemes: ComputedRef<Record<string, ThemeDefinition>>;
357
/** Theme CSS classes */
358
themeClasses: Ref<Record<string, boolean>>;
359
/** Theme styles as CSS string */
360
styles: ComputedRef<string>;
361
}
362
```
363
364
**Usage Examples:**
365
366
```javascript
367
// vuetify.js configuration
368
import { createVuetify } from 'vuetify';
369
370
const vuetify = createVuetify({
371
theme: {
372
defaultTheme: 'myCustomTheme',
373
variations: {
374
colors: ['primary', 'secondary'],
375
lighten: 5,
376
darken: 5
377
},
378
themes: {
379
myCustomTheme: {
380
dark: false,
381
colors: {
382
primary: '#1976D2',
383
secondary: '#424242',
384
accent: '#82B1FF',
385
error: '#FF5252',
386
info: '#2196F3',
387
success: '#4CAF50',
388
warning: '#FFC107',
389
},
390
variables: {
391
'border-radius-root': '6px',
392
'font-weight-regular': 400,
393
}
394
},
395
dark: {
396
dark: true,
397
colors: {
398
primary: '#BB86FC',
399
secondary: '#03DAC6',
400
background: '#121212',
401
surface: '#1E1E1E',
402
'on-surface': '#FFFFFF'
403
}
404
}
405
}
406
}
407
});
408
```
409
410
### Color System
411
412
Material Design color system implementation with semantic color tokens and automatic contrast calculation.
413
414
```typescript { .api }
415
/**
416
* Color utilities for theme color manipulation
417
*/
418
interface ColorUtils {
419
/** Parse color string to RGB object */
420
parseColor: (color: string) => { r: number; g: number; b: number; a?: number };
421
/** Convert color to hex format */
422
colorToHex: (color: string) => string;
423
/** Convert color to RGB format */
424
colorToRgb: (color: string) => { r: number; g: number; b: number };
425
/** Convert color to HSL format */
426
colorToHsl: (color: string) => { h: number; s: number; l: number };
427
/** Lighten color by percentage */
428
lighten: (color: string, amount: number) => string;
429
/** Darken color by percentage */
430
darken: (color: string, amount: number) => string;
431
/** Calculate color contrast ratio */
432
getContrast: (color1: string, color2: string) => number;
433
/** Get accessible text color for background */
434
getTextColor: (backgroundColor: string) => string;
435
}
436
437
/**
438
* Material Design 3 color tokens
439
*/
440
interface MaterialDesignColors {
441
/** Primary color family */
442
primary: string;
443
'primary-container': string;
444
'on-primary': string;
445
'on-primary-container': string;
446
447
/** Secondary color family */
448
secondary: string;
449
'secondary-container': string;
450
'on-secondary': string;
451
'on-secondary-container': string;
452
453
/** Tertiary color family */
454
tertiary: string;
455
'tertiary-container': string;
456
'on-tertiary': string;
457
'on-tertiary-container': string;
458
459
/** Error color family */
460
error: string;
461
'error-container': string;
462
'on-error': string;
463
'on-error-container': string;
464
465
/** Surface color family */
466
surface: string;
467
'surface-dim': string;
468
'surface-bright': string;
469
'surface-container-lowest': string;
470
'surface-container-low': string;
471
'surface-container': string;
472
'surface-container-high': string;
473
'surface-container-highest': string;
474
'on-surface': string;
475
'on-surface-variant': string;
476
477
/** Outline colors */
478
outline: string;
479
'outline-variant': string;
480
481
/** Inverse colors */
482
'inverse-surface': string;
483
'inverse-on-surface': string;
484
'inverse-primary': string;
485
486
/** Additional semantic colors */
487
background: string;
488
'on-background': string;
489
shadow: string;
490
scrim: string;
491
}
492
```
493
494
**Usage Examples:**
495
496
```vue
497
<template>
498
<div>
499
<!-- Color picker for custom colors -->
500
<v-card class="mb-4">
501
<v-card-title>Color Customization</v-card-title>
502
<v-card-text>
503
<v-row>
504
<v-col cols="12" sm="6">
505
<v-color-picker
506
v-model="customPrimary"
507
show-swatches
508
@update:model-value="updatePrimaryColor"
509
/>
510
</v-col>
511
<v-col cols="12" sm="6">
512
<div class="color-preview">
513
<div class="color-info mb-4">
514
<h4>Selected Color: {{ customPrimary }}</h4>
515
<p>Hex: {{ colorToHex(customPrimary) }}</p>
516
<p>RGB: {{ formatRgb(colorToRgb(customPrimary)) }}</p>
517
<p>HSL: {{ formatHsl(colorToHsl(customPrimary)) }}</p>
518
</div>
519
520
<!-- Color variations -->
521
<div class="color-variations">
522
<div class="variation-item">
523
<div
524
class="color-block"
525
:style="{ backgroundColor: lighten(customPrimary, 0.2) }"
526
>
527
Lighten 20%
528
</div>
529
</div>
530
<div class="variation-item">
531
<div
532
class="color-block"
533
:style="{ backgroundColor: customPrimary }"
534
>
535
Original
536
</div>
537
</div>
538
<div class="variation-item">
539
<div
540
class="color-block"
541
:style="{ backgroundColor: darken(customPrimary, 0.2) }"
542
>
543
Darken 20%
544
</div>
545
</div>
546
</div>
547
</div>
548
</v-col>
549
</v-row>
550
</v-card-text>
551
</v-card>
552
553
<!-- Material Design color tokens demo -->
554
<v-card class="mb-4">
555
<v-card-title>Material Design Color System</v-card-title>
556
<v-card-text>
557
<div class="md-colors-grid">
558
<div v-for="colorFamily in colorFamilies" :key="colorFamily.name" class="color-family">
559
<h4>{{ colorFamily.name }}</h4>
560
<div class="color-tokens">
561
<div
562
v-for="token in colorFamily.tokens"
563
:key="token.name"
564
class="color-token"
565
:style="{
566
backgroundColor: getThemeColor(token.name),
567
color: getThemeColor(token.onColor || 'on-surface')
568
}"
569
>
570
<span class="token-name">{{ token.name }}</span>
571
<span class="token-value">{{ getThemeColor(token.name) }}</span>
572
</div>
573
</div>
574
</div>
575
</div>
576
</v-card-text>
577
</v-card>
578
579
<!-- Accessibility contrast checker -->
580
<v-card>
581
<v-card-title>Contrast Checker</v-card-title>
582
<v-card-text>
583
<v-row>
584
<v-col cols="12" sm="6">
585
<v-text-field
586
v-model="foregroundColor"
587
label="Foreground Color"
588
placeholder="#000000"
589
/>
590
<v-color-picker
591
v-model="foregroundColor"
592
mode="hex"
593
hide-inputs
594
/>
595
</v-col>
596
<v-col cols="12" sm="6">
597
<v-text-field
598
v-model="backgroundColor"
599
label="Background Color"
600
placeholder="#FFFFFF"
601
/>
602
<v-color-picker
603
v-model="backgroundColor"
604
mode="hex"
605
hide-inputs
606
/>
607
</v-col>
608
</v-row>
609
610
<div class="contrast-results mt-4">
611
<div
612
class="contrast-preview"
613
:style="{
614
backgroundColor: backgroundColor,
615
color: foregroundColor,
616
padding: '16px',
617
borderRadius: '8px',
618
textAlign: 'center'
619
}"
620
>
621
Sample Text Preview
622
</div>
623
624
<div class="contrast-info mt-4">
625
<v-chip
626
:color="contrastRatio >= 4.5 ? 'success' : contrastRatio >= 3 ? 'warning' : 'error'"
627
>
628
Contrast Ratio: {{ contrastRatio.toFixed(2) }}:1
629
</v-chip>
630
631
<div class="accessibility-scores mt-2">
632
<v-icon
633
:color="contrastRatio >= 4.5 ? 'success' : 'error'"
634
class="mr-1"
635
>
636
{{ contrastRatio >= 4.5 ? 'mdi-check' : 'mdi-close' }}
637
</v-icon>
638
<span>AA Normal (4.5:1)</span>
639
640
<v-icon
641
:color="contrastRatio >= 7 ? 'success' : 'error'"
642
class="ml-4 mr-1"
643
>
644
{{ contrastRatio >= 7 ? 'mdi-check' : 'mdi-close' }}
645
</v-icon>
646
<span>AAA Normal (7:1)</span>
647
</div>
648
</div>
649
</div>
650
</v-card-text>
651
</v-card>
652
</div>
653
</template>
654
655
<script setup>
656
import { useTheme } from 'vuetify';
657
658
const theme = useTheme();
659
660
const customPrimary = ref('#1976D2');
661
const foregroundColor = ref('#000000');
662
const backgroundColor = ref('#FFFFFF');
663
664
// Color utility functions (these would be imported from Vuetify)
665
const colorToHex = (color) => {
666
// Implementation would use Vuetify's color utilities
667
return color.startsWith('#') ? color : `#${color}`;
668
};
669
670
const colorToRgb = (color) => {
671
// Implementation would use Vuetify's color utilities
672
const hex = color.replace('#', '');
673
return {
674
r: parseInt(hex.substr(0, 2), 16),
675
g: parseInt(hex.substr(2, 2), 16),
676
b: parseInt(hex.substr(4, 2), 16)
677
};
678
};
679
680
const colorToHsl = (color) => {
681
// Implementation would use Vuetify's color utilities
682
const { r, g, b } = colorToRgb(color);
683
const max = Math.max(r, g, b) / 255;
684
const min = Math.min(r, g, b) / 255;
685
const delta = max - min;
686
687
let h = 0;
688
if (delta !== 0) {
689
if (max === r / 255) h = ((g - b) / 255 / delta) % 6;
690
else if (max === g / 255) h = (b - r) / 255 / delta + 2;
691
else h = (r - g) / 255 / delta + 4;
692
}
693
h = Math.round(h * 60);
694
if (h < 0) h += 360;
695
696
const l = (max + min) / 2;
697
const s = delta === 0 ? 0 : delta / (1 - Math.abs(2 * l - 1));
698
699
return {
700
h: h,
701
s: Math.round(s * 100),
702
l: Math.round(l * 100)
703
};
704
};
705
706
const lighten = (color, amount) => {
707
// Implementation would use Vuetify's color utilities
708
const { r, g, b } = colorToRgb(color);
709
const lightenAmount = 255 * amount;
710
711
const newR = Math.min(255, Math.round(r + lightenAmount));
712
const newG = Math.min(255, Math.round(g + lightenAmount));
713
const newB = Math.min(255, Math.round(b + lightenAmount));
714
715
return `rgb(${newR}, ${newG}, ${newB})`;
716
};
717
718
const darken = (color, amount) => {
719
// Implementation would use Vuetify's color utilities
720
const { r, g, b } = colorToRgb(color);
721
const darkenAmount = 255 * amount;
722
723
const newR = Math.max(0, Math.round(r - darkenAmount));
724
const newG = Math.max(0, Math.round(g - darkenAmount));
725
const newB = Math.max(0, Math.round(b - darkenAmount));
726
727
return `rgb(${newR}, ${newG}, ${newB})`;
728
};
729
730
const getContrast = (color1, color2) => {
731
// Implementation would use Vuetify's color utilities
732
const getLuminance = (color) => {
733
const { r, g, b } = colorToRgb(color);
734
const [rs, gs, bs] = [r, g, b].map(c => {
735
c = c / 255;
736
return c <= 0.03928 ? c / 12.92 : Math.pow((c + 0.055) / 1.055, 2.4);
737
});
738
return 0.2126 * rs + 0.7152 * gs + 0.0722 * bs;
739
};
740
741
const lum1 = getLuminance(color1);
742
const lum2 = getLuminance(color2);
743
const brightest = Math.max(lum1, lum2);
744
const darkest = Math.min(lum1, lum2);
745
746
return (brightest + 0.05) / (darkest + 0.05);
747
};
748
749
const colorFamilies = [
750
{
751
name: 'Primary',
752
tokens: [
753
{ name: 'primary', onColor: 'on-primary' },
754
{ name: 'primary-container', onColor: 'on-primary-container' },
755
{ name: 'on-primary' },
756
{ name: 'on-primary-container' }
757
]
758
},
759
{
760
name: 'Surface',
761
tokens: [
762
{ name: 'surface', onColor: 'on-surface' },
763
{ name: 'surface-container', onColor: 'on-surface' },
764
{ name: 'surface-variant', onColor: 'on-surface-variant' },
765
{ name: 'on-surface' },
766
{ name: 'on-surface-variant' }
767
]
768
}
769
];
770
771
const contrastRatio = computed(() => {
772
return getContrast(foregroundColor.value, backgroundColor.value);
773
});
774
775
const getThemeColor = (colorName) => {
776
return theme.current.value.colors[colorName] || '#000000';
777
};
778
779
const formatRgb = (rgb) => {
780
return `rgb(${rgb.r}, ${rgb.g}, ${rgb.b})`;
781
};
782
783
const formatHsl = (hsl) => {
784
return `hsl(${hsl.h}, ${hsl.s}%, ${hsl.l}%)`;
785
};
786
787
const updatePrimaryColor = (color) => {
788
// Update theme with new primary color
789
const newTheme = {
790
...theme.current.value,
791
colors: {
792
...theme.current.value.colors,
793
primary: color
794
}
795
};
796
797
theme.themes.value[theme.name.value] = newTheme;
798
};
799
</script>
800
801
<style>
802
.color-variations {
803
display: flex;
804
gap: 8px;
805
margin-top: 16px;
806
}
807
808
.variation-item {
809
flex: 1;
810
}
811
812
.color-block {
813
padding: 16px 8px;
814
text-align: center;
815
border-radius: 4px;
816
font-size: 0.875em;
817
font-weight: 500;
818
color: white;
819
text-shadow: 1px 1px 2px rgba(0,0,0,0.5);
820
}
821
822
.md-colors-grid {
823
display: grid;
824
gap: 24px;
825
}
826
827
.color-family {
828
border: 1px solid rgba(0,0,0,0.1);
829
border-radius: 8px;
830
padding: 16px;
831
}
832
833
.color-tokens {
834
display: grid;
835
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
836
gap: 8px;
837
margin-top: 12px;
838
}
839
840
.color-token {
841
padding: 12px;
842
border-radius: 6px;
843
display: flex;
844
flex-direction: column;
845
gap: 4px;
846
font-size: 0.875em;
847
}
848
849
.token-name {
850
font-weight: 500;
851
}
852
853
.token-value {
854
font-family: monospace;
855
opacity: 0.8;
856
font-size: 0.8em;
857
}
858
859
.contrast-preview {
860
font-size: 18px;
861
font-weight: 500;
862
}
863
864
.accessibility-scores {
865
display: flex;
866
align-items: center;
867
gap: 16px;
868
font-size: 0.875em;
869
}
870
</style>
871
```
872
873
### Material Design Integration
874
875
Integration with Google's Material Design specification and design tokens.
876
877
```typescript { .api }
878
/**
879
* Material Design blueprint configurations
880
*/
881
interface MaterialDesignBlueprints {
882
/** Material Design 1 specification */
883
md1: Blueprint;
884
/** Material Design 2 specification */
885
md2: Blueprint;
886
/** Material Design 3 specification */
887
md3: Blueprint;
888
}
889
890
/**
891
* Material Design elevation system
892
*/
893
interface ElevationSystem {
894
/** Elevation levels 0-24 */
895
0: string;
896
1: string;
897
2: string;
898
3: string;
899
4: string;
900
6: string;
901
8: string;
902
12: string;
903
16: string;
904
24: string;
905
}
906
907
/**
908
* Material Design motion and easing
909
*/
910
interface MaterialMotion {
911
/** Standard easing curves */
912
easing: {
913
standard: string;
914
decelerated: string;
915
accelerated: string;
916
};
917
/** Duration tokens */
918
duration: {
919
short1: number;
920
short2: number;
921
medium1: number;
922
medium2: number;
923
long1: number;
924
long2: number;
925
};
926
}
927
928
/**
929
* Typography scale from Material Design
930
*/
931
interface MaterialTypography {
932
h1: TypographyDefinition;
933
h2: TypographyDefinition;
934
h3: TypographyDefinition;
935
h4: TypographyDefinition;
936
h5: TypographyDefinition;
937
h6: TypographyDefinition;
938
subtitle1: TypographyDefinition;
939
subtitle2: TypographyDefinition;
940
body1: TypographyDefinition;
941
body2: TypographyDefinition;
942
button: TypographyDefinition;
943
caption: TypographyDefinition;
944
overline: TypographyDefinition;
945
}
946
947
interface TypographyDefinition {
948
fontFamily?: string;
949
fontSize?: string;
950
fontWeight?: number;
951
lineHeight?: string;
952
letterSpacing?: string;
953
}
954
```
955
956
**Usage Examples:**
957
958
```javascript
959
// Using Material Design blueprints
960
import { createVuetify } from 'vuetify';
961
import { md3 } from 'vuetify/blueprints';
962
963
const vuetify = createVuetify({
964
blueprint: md3,
965
theme: {
966
themes: {
967
light: {
968
colors: {
969
// Material Design 3 color tokens
970
primary: '#6750A4',
971
'primary-container': '#EADDFF',
972
'on-primary': '#FFFFFF',
973
'on-primary-container': '#21005D',
974
secondary: '#625B71',
975
'secondary-container': '#E8DEF8',
976
'on-secondary': '#FFFFFF',
977
'on-secondary-container': '#1D192B',
978
tertiary: '#7D5260',
979
'tertiary-container': '#FFD8E4',
980
'on-tertiary': '#FFFFFF',
981
'on-tertiary-container': '#31111D',
982
error: '#BA1A1A',
983
'error-container': '#FFDAD6',
984
'on-error': '#FFFFFF',
985
'on-error-container': '#410002',
986
background: '#FFFBFE',
987
'on-background': '#1C1B1F',
988
surface: '#FFFBFE',
989
'on-surface': '#1C1B1F',
990
'surface-variant': '#E7E0EC',
991
'on-surface-variant': '#49454F',
992
outline: '#79747E',
993
'outline-variant': '#CAC4D0',
994
shadow: '#000000',
995
scrim: '#000000'
996
}
997
}
998
}
999
}
1000
});
1001
1002
// Custom Material Design theme
1003
const materialTheme = {
1004
dark: false,
1005
colors: {
1006
// Semantic color roles
1007
primary: '#1976D2',
1008
secondary: '#424242',
1009
tertiary: '#FF9800',
1010
1011
// Surface colors
1012
surface: '#FFFFFF',
1013
'surface-dim': '#DDD8CE',
1014
'surface-bright': '#FFF8F2',
1015
'surface-container-lowest': '#FFFFFF',
1016
'surface-container-low': '#F7F2EA',
1017
'surface-container': '#F1EBE3',
1018
'surface-container-high': '#ECE6DD',
1019
'surface-container-highest': '#E6E0D7',
1020
1021
// State colors
1022
error: '#BA1A1A',
1023
warning: '#F57C00',
1024
info: '#1976D2',
1025
success: '#388E3C'
1026
},
1027
variables: {
1028
// Material Design spacing
1029
'spacing-xs': '4px',
1030
'spacing-sm': '8px',
1031
'spacing-md': '16px',
1032
'spacing-lg': '24px',
1033
'spacing-xl': '32px',
1034
1035
// Border radius tokens
1036
'border-radius-none': '0px',
1037
'border-radius-sm': '4px',
1038
'border-radius-md': '8px',
1039
'border-radius-lg': '12px',
1040
'border-radius-xl': '16px',
1041
'border-radius-pill': '9999px',
1042
1043
// Elevation shadows
1044
'shadow-key-umbra-opacity': 0.2,
1045
'shadow-key-penumbra-opacity': 0.14,
1046
'shadow-key-ambient-opacity': 0.12
1047
}
1048
};
1049
```
1050
1051
## Types
1052
1053
```typescript { .api }
1054
// Core theme types
1055
type ThemeName = string;
1056
type ColorName = string;
1057
type ColorValue = string;
1058
1059
// Theme configuration
1060
interface ThemeConfiguration {
1061
defaultTheme: ThemeName;
1062
themes: Record<ThemeName, ThemeDefinition>;
1063
variations?: ThemeVariations;
1064
cspNonce?: string;
1065
}
1066
1067
// Color manipulation
1068
type ColorManipulation = 'lighten' | 'darken' | 'saturate' | 'desaturate';
1069
type ColorFormat = 'hex' | 'rgb' | 'hsl' | 'hsv';
1070
1071
// Material Design tokens
1072
type MaterialColorRole =
1073
| 'primary' | 'secondary' | 'tertiary' | 'error'
1074
| 'surface' | 'background' | 'outline';
1075
1076
type MaterialColorVariant =
1077
| 'container' | 'on-container' | 'variant'
1078
| 'dim' | 'bright' | 'lowest' | 'low' | 'high' | 'highest';
1079
1080
// Accessibility
1081
interface AccessibilityRequirement {
1082
level: 'AA' | 'AAA';
1083
size: 'normal' | 'large';
1084
ratio: number;
1085
}
1086
1087
// CSS Custom Properties
1088
type CSSCustomProperty = `--v-${string}`;
1089
type CSSValue = string | number;
1090
1091
// Theme utilities
1092
type ThemeUtility = {
1093
parseColor: (color: string) => { r: number; g: number; b: number; a?: number };
1094
formatColor: (color: any, format: ColorFormat) => string;
1095
getContrast: (foreground: string, background: string) => number;
1096
isAccessible: (foreground: string, background: string, requirement?: AccessibilityRequirement) => boolean;
1097
};
1098
```