CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-vuetify

Vue Material Component Framework implementing Google's Material Design specification with comprehensive UI components, theming system, and accessibility features.

Pending
Overview
Eval results
Files

theming.mddocs/

Theming

Comprehensive theming system for customizing colors, typography, spacing, and visual design across all Vuetify components with Material Design integration.

Capabilities

ThemeDefinition Interface

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>

Theme Options

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'
        }
      }
    }
  }
});

Color System

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>

Material Design Integration

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
  }
};

Types

// 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

docs

components.md

composables.md

data-display.md

directives.md

feedback.md

forms.md

framework-core.md

icons.md

index.md

internationalization.md

lab-components.md

navigation.md

theming.md

transitions.md

utilities.md

tile.json