Vue Material Component Framework implementing Google's Material Design specification with comprehensive UI components, theming system, and accessibility features.
—
Comprehensive theming system for customizing colors, typography, spacing, and visual design across all Vuetify components with Material Design integration.
Core theme configuration interface that defines the structure for custom themes.
/**
* Theme definition interface for creating custom themes
*/
interface ThemeDefinition {
/** Whether the theme is dark mode */
dark: boolean;
/** Color palette configuration */
colors: ThemeColors;
/** CSS custom properties and variables */
variables: ThemeVariables;
}
interface ThemeColors {
/** Primary brand color */
primary?: string;
/** Primary color variant */
'primary-darken-1'?: string;
/** Secondary brand color */
secondary?: string;
/** Secondary color variant */
'secondary-darken-1'?: string;
/** Accent color for highlights */
accent?: string;
/** Error state color */
error?: string;
/** Warning state color */
warning?: string;
/** Info state color */
info?: string;
/** Success state color */
success?: string;
/** Background color */
background?: string;
/** Surface color for cards, sheets */
surface?: string;
/** Surface variant color */
'surface-variant'?: string;
/** Text color on background */
'on-background'?: string;
/** Text color on surface */
'on-surface'?: string;
/** Text color on surface variant */
'on-surface-variant'?: string;
/** Text color on primary */
'on-primary'?: string;
/** Text color on secondary */
'on-secondary'?: string;
/** Text color on error */
'on-error'?: string;
/** Text color on warning */
'on-warning'?: string;
/** Text color on info */
'on-info'?: string;
/** Text color on success */
'on-success'?: string;
/** Outline color for borders */
outline?: string;
/** Outline variant color */
'outline-variant'?: string;
/** Inverse surface color */
'inverse-surface'?: string;
/** Text color on inverse surface */
'on-inverse-surface'?: string;
/** Inverse primary color */
'inverse-primary'?: string;
/** Shadow color */
shadow?: string;
/** Scrim overlay color */
scrim?: string;
}
interface ThemeVariables {
/** Border radius values */
'border-radius-root'?: string;
'border-radius-sm'?: string;
'border-radius-md'?: string;
'border-radius-lg'?: string;
'border-radius-xl'?: string;
/** Elevation shadows */
'shadow-key-umbra-opacity'?: number;
'shadow-key-penumbra-opacity'?: number;
'shadow-key-ambient-opacity'?: number;
/** Typography */
'font-weight-thin'?: number;
'font-weight-light'?: number;
'font-weight-regular'?: number;
'font-weight-medium'?: number;
'font-weight-bold'?: number;
'font-weight-black'?: number;
/** Letter spacing */
'letter-spacing-body'?: string;
'letter-spacing-button'?: string;
'letter-spacing-caption'?: string;
'letter-spacing-subtitle'?: string;
/** Line heights */
'line-height-sm'?: number;
'line-height-md'?: number;
'line-height-lg'?: number;
/** Spacing */
'spacing-xs'?: string;
'spacing-sm'?: string;
'spacing-md'?: string;
'spacing-lg'?: string;
'spacing-xl'?: string;
}Usage Examples:
<template>
<div>
<!-- Theme selector -->
<v-select
v-model="selectedTheme"
:items="themeOptions"
label="Select Theme"
@update:model-value="applyTheme"
/>
<!-- Color palette preview -->
<v-card class="mb-4">
<v-card-title>Current Theme Colors</v-card-title>
<v-card-text>
<div class="color-grid">
<div
v-for="(color, name) in currentColors"
:key="name"
class="color-item"
>
<div
class="color-swatch"
:style="{
backgroundColor: color,
color: getContrastColor(name)
}"
>
<div class="color-name">{{ name }}</div>
<div class="color-value">{{ color }}</div>
</div>
</div>
</div>
</v-card-text>
</v-card>
<!-- Component examples with current theme -->
<v-row>
<v-col cols="12" md="6">
<v-card>
<v-card-title color="primary">Primary Components</v-card-title>
<v-card-text>
<v-btn color="primary" class="mb-2">Primary Button</v-btn><br>
<v-chip color="primary">Primary Chip</v-chip>
<v-text-field label="Primary Input" color="primary" />
</v-card-text>
</v-card>
</v-col>
<v-col cols="12" md="6">
<v-card>
<v-card-title color="secondary">Secondary Components</v-card-title>
<v-card-text>
<v-btn color="secondary" class="mb-2">Secondary Button</v-btn><br>
<v-chip color="secondary">Secondary Chip</v-chip>
<v-text-field label="Secondary Input" color="secondary" />
</v-card-text>
</v-card>
</v-col>
</v-row>
</div>
</template>
<script setup>
import { useTheme } from 'vuetify';
const theme = useTheme();
const selectedTheme = ref('light');
const themeOptions = [
{ title: 'Light Theme', value: 'light' },
{ title: 'Dark Theme', value: 'dark' },
{ title: 'Custom Blue', value: 'customBlue' },
{ title: 'Custom Green', value: 'customGreen' },
];
const customThemes = {
customBlue: {
dark: false,
colors: {
primary: '#1976D2',
'primary-darken-1': '#1565C0',
secondary: '#424242',
'secondary-darken-1': '#1B5E20',
accent: '#82B1FF',
error: '#FF5252',
info: '#2196F3',
success: '#4CAF50',
warning: '#FFC107',
background: '#F5F5F5',
surface: '#FFFFFF',
'on-surface': '#1D1B20'
},
variables: {
'border-radius-root': '8px',
'border-radius-sm': '4px',
'border-radius-lg': '16px'
}
},
customGreen: {
dark: false,
colors: {
primary: '#4CAF50',
'primary-darken-1': '#388E3C',
secondary: '#FF9800',
'secondary-darken-1': '#F57C00',
accent: '#00BCD4',
error: '#F44336',
info: '#2196F3',
success: '#8BC34A',
warning: '#FF9800',
background: '#E8F5E8',
surface: '#FFFFFF',
'on-surface': '#1B5E20'
},
variables: {
'border-radius-root': '12px',
'font-weight-regular': 400,
'font-weight-medium': 500
}
}
};
const currentColors = computed(() => theme.current.value.colors);
const applyTheme = (themeName) => {
if (customThemes[themeName]) {
theme.themes.value[themeName] = customThemes[themeName];
}
theme.name.value = themeName;
};
const getContrastColor = (colorName) => {
const onColorMap = {
'primary': 'on-primary',
'secondary': 'on-secondary',
'surface': 'on-surface',
'background': 'on-background',
'error': 'on-error',
'warning': 'on-warning',
'info': 'on-info',
'success': 'on-success'
};
return currentColors.value[onColorMap[colorName]] || '#FFFFFF';
};
onMounted(() => {
// Register custom themes
Object.entries(customThemes).forEach(([name, themeConfig]) => {
theme.themes.value[name] = themeConfig;
});
});
</script>
<style>
.color-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 16px;
}
.color-item {
border-radius: 8px;
overflow: hidden;
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
}
.color-swatch {
padding: 16px;
min-height: 80px;
display: flex;
flex-direction: column;
justify-content: center;
}
.color-name {
font-weight: 500;
text-transform: capitalize;
margin-bottom: 4px;
}
.color-value {
font-family: monospace;
font-size: 0.875em;
opacity: 0.8;
}
</style>Configuration interface for setting up the theming system during Vuetify initialization.
/**
* Theme configuration options for Vuetify setup
*/
interface ThemeOptions {
/** Default theme name to use */
defaultTheme?: string;
/** Available theme definitions */
themes?: Record<string, ThemeDefinition>;
/** Color variations configuration */
variations?: ThemeVariations;
/** CSS custom properties prefix */
cspNonce?: string;
}
interface ThemeVariations {
/** Colors to generate variations for */
colors: string[];
/** Lighten amount for variants */
lighten: number;
/** Darken amount for variants */
darken: number;
}
/**
* Theme instance returned by useTheme composable
*/
interface ThemeInstance {
/** Current active theme */
current: Ref<ThemeDefinition>;
/** Available themes */
themes: Ref<Record<string, ThemeDefinition>>;
/** Whether theming is disabled */
isDisabled: Ref<boolean>;
/** Current theme name */
name: Ref<string>;
/** Computed theme styles */
computedThemes: ComputedRef<Record<string, ThemeDefinition>>;
/** Theme CSS classes */
themeClasses: Ref<Record<string, boolean>>;
/** Theme styles as CSS string */
styles: ComputedRef<string>;
}Usage Examples:
// vuetify.js configuration
import { createVuetify } from 'vuetify';
const vuetify = createVuetify({
theme: {
defaultTheme: 'myCustomTheme',
variations: {
colors: ['primary', 'secondary'],
lighten: 5,
darken: 5
},
themes: {
myCustomTheme: {
dark: false,
colors: {
primary: '#1976D2',
secondary: '#424242',
accent: '#82B1FF',
error: '#FF5252',
info: '#2196F3',
success: '#4CAF50',
warning: '#FFC107',
},
variables: {
'border-radius-root': '6px',
'font-weight-regular': 400,
}
},
dark: {
dark: true,
colors: {
primary: '#BB86FC',
secondary: '#03DAC6',
background: '#121212',
surface: '#1E1E1E',
'on-surface': '#FFFFFF'
}
}
}
}
});Material Design color system implementation with semantic color tokens and automatic contrast calculation.
/**
* Color utilities for theme color manipulation
*/
interface ColorUtils {
/** Parse color string to RGB object */
parseColor: (color: string) => { r: number; g: number; b: number; a?: number };
/** Convert color to hex format */
colorToHex: (color: string) => string;
/** Convert color to RGB format */
colorToRgb: (color: string) => { r: number; g: number; b: number };
/** Convert color to HSL format */
colorToHsl: (color: string) => { h: number; s: number; l: number };
/** Lighten color by percentage */
lighten: (color: string, amount: number) => string;
/** Darken color by percentage */
darken: (color: string, amount: number) => string;
/** Calculate color contrast ratio */
getContrast: (color1: string, color2: string) => number;
/** Get accessible text color for background */
getTextColor: (backgroundColor: string) => string;
}
/**
* Material Design 3 color tokens
*/
interface MaterialDesignColors {
/** Primary color family */
primary: string;
'primary-container': string;
'on-primary': string;
'on-primary-container': string;
/** Secondary color family */
secondary: string;
'secondary-container': string;
'on-secondary': string;
'on-secondary-container': string;
/** Tertiary color family */
tertiary: string;
'tertiary-container': string;
'on-tertiary': string;
'on-tertiary-container': string;
/** Error color family */
error: string;
'error-container': string;
'on-error': string;
'on-error-container': string;
/** Surface color family */
surface: string;
'surface-dim': string;
'surface-bright': string;
'surface-container-lowest': string;
'surface-container-low': string;
'surface-container': string;
'surface-container-high': string;
'surface-container-highest': string;
'on-surface': string;
'on-surface-variant': string;
/** Outline colors */
outline: string;
'outline-variant': string;
/** Inverse colors */
'inverse-surface': string;
'inverse-on-surface': string;
'inverse-primary': string;
/** Additional semantic colors */
background: string;
'on-background': string;
shadow: string;
scrim: string;
}Usage Examples:
<template>
<div>
<!-- Color picker for custom colors -->
<v-card class="mb-4">
<v-card-title>Color Customization</v-card-title>
<v-card-text>
<v-row>
<v-col cols="12" sm="6">
<v-color-picker
v-model="customPrimary"
show-swatches
@update:model-value="updatePrimaryColor"
/>
</v-col>
<v-col cols="12" sm="6">
<div class="color-preview">
<div class="color-info mb-4">
<h4>Selected Color: {{ customPrimary }}</h4>
<p>Hex: {{ colorToHex(customPrimary) }}</p>
<p>RGB: {{ formatRgb(colorToRgb(customPrimary)) }}</p>
<p>HSL: {{ formatHsl(colorToHsl(customPrimary)) }}</p>
</div>
<!-- Color variations -->
<div class="color-variations">
<div class="variation-item">
<div
class="color-block"
:style="{ backgroundColor: lighten(customPrimary, 0.2) }"
>
Lighten 20%
</div>
</div>
<div class="variation-item">
<div
class="color-block"
:style="{ backgroundColor: customPrimary }"
>
Original
</div>
</div>
<div class="variation-item">
<div
class="color-block"
:style="{ backgroundColor: darken(customPrimary, 0.2) }"
>
Darken 20%
</div>
</div>
</div>
</div>
</v-col>
</v-row>
</v-card-text>
</v-card>
<!-- Material Design color tokens demo -->
<v-card class="mb-4">
<v-card-title>Material Design Color System</v-card-title>
<v-card-text>
<div class="md-colors-grid">
<div v-for="colorFamily in colorFamilies" :key="colorFamily.name" class="color-family">
<h4>{{ colorFamily.name }}</h4>
<div class="color-tokens">
<div
v-for="token in colorFamily.tokens"
:key="token.name"
class="color-token"
:style="{
backgroundColor: getThemeColor(token.name),
color: getThemeColor(token.onColor || 'on-surface')
}"
>
<span class="token-name">{{ token.name }}</span>
<span class="token-value">{{ getThemeColor(token.name) }}</span>
</div>
</div>
</div>
</div>
</v-card-text>
</v-card>
<!-- Accessibility contrast checker -->
<v-card>
<v-card-title>Contrast Checker</v-card-title>
<v-card-text>
<v-row>
<v-col cols="12" sm="6">
<v-text-field
v-model="foregroundColor"
label="Foreground Color"
placeholder="#000000"
/>
<v-color-picker
v-model="foregroundColor"
mode="hex"
hide-inputs
/>
</v-col>
<v-col cols="12" sm="6">
<v-text-field
v-model="backgroundColor"
label="Background Color"
placeholder="#FFFFFF"
/>
<v-color-picker
v-model="backgroundColor"
mode="hex"
hide-inputs
/>
</v-col>
</v-row>
<div class="contrast-results mt-4">
<div
class="contrast-preview"
:style="{
backgroundColor: backgroundColor,
color: foregroundColor,
padding: '16px',
borderRadius: '8px',
textAlign: 'center'
}"
>
Sample Text Preview
</div>
<div class="contrast-info mt-4">
<v-chip
:color="contrastRatio >= 4.5 ? 'success' : contrastRatio >= 3 ? 'warning' : 'error'"
>
Contrast Ratio: {{ contrastRatio.toFixed(2) }}:1
</v-chip>
<div class="accessibility-scores mt-2">
<v-icon
:color="contrastRatio >= 4.5 ? 'success' : 'error'"
class="mr-1"
>
{{ contrastRatio >= 4.5 ? 'mdi-check' : 'mdi-close' }}
</v-icon>
<span>AA Normal (4.5:1)</span>
<v-icon
:color="contrastRatio >= 7 ? 'success' : 'error'"
class="ml-4 mr-1"
>
{{ contrastRatio >= 7 ? 'mdi-check' : 'mdi-close' }}
</v-icon>
<span>AAA Normal (7:1)</span>
</div>
</div>
</div>
</v-card-text>
</v-card>
</div>
</template>
<script setup>
import { useTheme } from 'vuetify';
const theme = useTheme();
const customPrimary = ref('#1976D2');
const foregroundColor = ref('#000000');
const backgroundColor = ref('#FFFFFF');
// Color utility functions (these would be imported from Vuetify)
const colorToHex = (color) => {
// Implementation would use Vuetify's color utilities
return color.startsWith('#') ? color : `#${color}`;
};
const colorToRgb = (color) => {
// Implementation would use Vuetify's color utilities
const hex = color.replace('#', '');
return {
r: parseInt(hex.substr(0, 2), 16),
g: parseInt(hex.substr(2, 2), 16),
b: parseInt(hex.substr(4, 2), 16)
};
};
const colorToHsl = (color) => {
// Implementation would use Vuetify's color utilities
const { r, g, b } = colorToRgb(color);
const max = Math.max(r, g, b) / 255;
const min = Math.min(r, g, b) / 255;
const delta = max - min;
let h = 0;
if (delta !== 0) {
if (max === r / 255) h = ((g - b) / 255 / delta) % 6;
else if (max === g / 255) h = (b - r) / 255 / delta + 2;
else h = (r - g) / 255 / delta + 4;
}
h = Math.round(h * 60);
if (h < 0) h += 360;
const l = (max + min) / 2;
const s = delta === 0 ? 0 : delta / (1 - Math.abs(2 * l - 1));
return {
h: h,
s: Math.round(s * 100),
l: Math.round(l * 100)
};
};
const lighten = (color, amount) => {
// Implementation would use Vuetify's color utilities
const { r, g, b } = colorToRgb(color);
const lightenAmount = 255 * amount;
const newR = Math.min(255, Math.round(r + lightenAmount));
const newG = Math.min(255, Math.round(g + lightenAmount));
const newB = Math.min(255, Math.round(b + lightenAmount));
return `rgb(${newR}, ${newG}, ${newB})`;
};
const darken = (color, amount) => {
// Implementation would use Vuetify's color utilities
const { r, g, b } = colorToRgb(color);
const darkenAmount = 255 * amount;
const newR = Math.max(0, Math.round(r - darkenAmount));
const newG = Math.max(0, Math.round(g - darkenAmount));
const newB = Math.max(0, Math.round(b - darkenAmount));
return `rgb(${newR}, ${newG}, ${newB})`;
};
const getContrast = (color1, color2) => {
// Implementation would use Vuetify's color utilities
const getLuminance = (color) => {
const { r, g, b } = colorToRgb(color);
const [rs, gs, bs] = [r, g, b].map(c => {
c = c / 255;
return c <= 0.03928 ? c / 12.92 : Math.pow((c + 0.055) / 1.055, 2.4);
});
return 0.2126 * rs + 0.7152 * gs + 0.0722 * bs;
};
const lum1 = getLuminance(color1);
const lum2 = getLuminance(color2);
const brightest = Math.max(lum1, lum2);
const darkest = Math.min(lum1, lum2);
return (brightest + 0.05) / (darkest + 0.05);
};
const colorFamilies = [
{
name: 'Primary',
tokens: [
{ name: 'primary', onColor: 'on-primary' },
{ name: 'primary-container', onColor: 'on-primary-container' },
{ name: 'on-primary' },
{ name: 'on-primary-container' }
]
},
{
name: 'Surface',
tokens: [
{ name: 'surface', onColor: 'on-surface' },
{ name: 'surface-container', onColor: 'on-surface' },
{ name: 'surface-variant', onColor: 'on-surface-variant' },
{ name: 'on-surface' },
{ name: 'on-surface-variant' }
]
}
];
const contrastRatio = computed(() => {
return getContrast(foregroundColor.value, backgroundColor.value);
});
const getThemeColor = (colorName) => {
return theme.current.value.colors[colorName] || '#000000';
};
const formatRgb = (rgb) => {
return `rgb(${rgb.r}, ${rgb.g}, ${rgb.b})`;
};
const formatHsl = (hsl) => {
return `hsl(${hsl.h}, ${hsl.s}%, ${hsl.l}%)`;
};
const updatePrimaryColor = (color) => {
// Update theme with new primary color
const newTheme = {
...theme.current.value,
colors: {
...theme.current.value.colors,
primary: color
}
};
theme.themes.value[theme.name.value] = newTheme;
};
</script>
<style>
.color-variations {
display: flex;
gap: 8px;
margin-top: 16px;
}
.variation-item {
flex: 1;
}
.color-block {
padding: 16px 8px;
text-align: center;
border-radius: 4px;
font-size: 0.875em;
font-weight: 500;
color: white;
text-shadow: 1px 1px 2px rgba(0,0,0,0.5);
}
.md-colors-grid {
display: grid;
gap: 24px;
}
.color-family {
border: 1px solid rgba(0,0,0,0.1);
border-radius: 8px;
padding: 16px;
}
.color-tokens {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 8px;
margin-top: 12px;
}
.color-token {
padding: 12px;
border-radius: 6px;
display: flex;
flex-direction: column;
gap: 4px;
font-size: 0.875em;
}
.token-name {
font-weight: 500;
}
.token-value {
font-family: monospace;
opacity: 0.8;
font-size: 0.8em;
}
.contrast-preview {
font-size: 18px;
font-weight: 500;
}
.accessibility-scores {
display: flex;
align-items: center;
gap: 16px;
font-size: 0.875em;
}
</style>Integration with Google's Material Design specification and design tokens.
/**
* Material Design blueprint configurations
*/
interface MaterialDesignBlueprints {
/** Material Design 1 specification */
md1: Blueprint;
/** Material Design 2 specification */
md2: Blueprint;
/** Material Design 3 specification */
md3: Blueprint;
}
/**
* Material Design elevation system
*/
interface ElevationSystem {
/** Elevation levels 0-24 */
0: string;
1: string;
2: string;
3: string;
4: string;
6: string;
8: string;
12: string;
16: string;
24: string;
}
/**
* Material Design motion and easing
*/
interface MaterialMotion {
/** Standard easing curves */
easing: {
standard: string;
decelerated: string;
accelerated: string;
};
/** Duration tokens */
duration: {
short1: number;
short2: number;
medium1: number;
medium2: number;
long1: number;
long2: number;
};
}
/**
* Typography scale from Material Design
*/
interface MaterialTypography {
h1: TypographyDefinition;
h2: TypographyDefinition;
h3: TypographyDefinition;
h4: TypographyDefinition;
h5: TypographyDefinition;
h6: TypographyDefinition;
subtitle1: TypographyDefinition;
subtitle2: TypographyDefinition;
body1: TypographyDefinition;
body2: TypographyDefinition;
button: TypographyDefinition;
caption: TypographyDefinition;
overline: TypographyDefinition;
}
interface TypographyDefinition {
fontFamily?: string;
fontSize?: string;
fontWeight?: number;
lineHeight?: string;
letterSpacing?: string;
}Usage Examples:
// Using Material Design blueprints
import { createVuetify } from 'vuetify';
import { md3 } from 'vuetify/blueprints';
const vuetify = createVuetify({
blueprint: md3,
theme: {
themes: {
light: {
colors: {
// Material Design 3 color tokens
primary: '#6750A4',
'primary-container': '#EADDFF',
'on-primary': '#FFFFFF',
'on-primary-container': '#21005D',
secondary: '#625B71',
'secondary-container': '#E8DEF8',
'on-secondary': '#FFFFFF',
'on-secondary-container': '#1D192B',
tertiary: '#7D5260',
'tertiary-container': '#FFD8E4',
'on-tertiary': '#FFFFFF',
'on-tertiary-container': '#31111D',
error: '#BA1A1A',
'error-container': '#FFDAD6',
'on-error': '#FFFFFF',
'on-error-container': '#410002',
background: '#FFFBFE',
'on-background': '#1C1B1F',
surface: '#FFFBFE',
'on-surface': '#1C1B1F',
'surface-variant': '#E7E0EC',
'on-surface-variant': '#49454F',
outline: '#79747E',
'outline-variant': '#CAC4D0',
shadow: '#000000',
scrim: '#000000'
}
}
}
}
});
// Custom Material Design theme
const materialTheme = {
dark: false,
colors: {
// Semantic color roles
primary: '#1976D2',
secondary: '#424242',
tertiary: '#FF9800',
// Surface colors
surface: '#FFFFFF',
'surface-dim': '#DDD8CE',
'surface-bright': '#FFF8F2',
'surface-container-lowest': '#FFFFFF',
'surface-container-low': '#F7F2EA',
'surface-container': '#F1EBE3',
'surface-container-high': '#ECE6DD',
'surface-container-highest': '#E6E0D7',
// State colors
error: '#BA1A1A',
warning: '#F57C00',
info: '#1976D2',
success: '#388E3C'
},
variables: {
// Material Design spacing
'spacing-xs': '4px',
'spacing-sm': '8px',
'spacing-md': '16px',
'spacing-lg': '24px',
'spacing-xl': '32px',
// Border radius tokens
'border-radius-none': '0px',
'border-radius-sm': '4px',
'border-radius-md': '8px',
'border-radius-lg': '12px',
'border-radius-xl': '16px',
'border-radius-pill': '9999px',
// Elevation shadows
'shadow-key-umbra-opacity': 0.2,
'shadow-key-penumbra-opacity': 0.14,
'shadow-key-ambient-opacity': 0.12
}
};// Core theme types
type ThemeName = string;
type ColorName = string;
type ColorValue = string;
// Theme configuration
interface ThemeConfiguration {
defaultTheme: ThemeName;
themes: Record<ThemeName, ThemeDefinition>;
variations?: ThemeVariations;
cspNonce?: string;
}
// Color manipulation
type ColorManipulation = 'lighten' | 'darken' | 'saturate' | 'desaturate';
type ColorFormat = 'hex' | 'rgb' | 'hsl' | 'hsv';
// Material Design tokens
type MaterialColorRole =
| 'primary' | 'secondary' | 'tertiary' | 'error'
| 'surface' | 'background' | 'outline';
type MaterialColorVariant =
| 'container' | 'on-container' | 'variant'
| 'dim' | 'bright' | 'lowest' | 'low' | 'high' | 'highest';
// Accessibility
interface AccessibilityRequirement {
level: 'AA' | 'AAA';
size: 'normal' | 'large';
ratio: number;
}
// CSS Custom Properties
type CSSCustomProperty = `--v-${string}`;
type CSSValue = string | number;
// Theme utilities
type ThemeUtility = {
parseColor: (color: string) => { r: number; g: number; b: number; a?: number };
formatColor: (color: any, format: ColorFormat) => string;
getContrast: (foreground: string, background: string) => number;
isAccessible: (foreground: string, background: string, requirement?: AccessibilityRequirement) => boolean;
};Install with Tessl CLI
npx tessl i tessl/npm-vuetify