or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

components.mdcomposables.mddata-display.mddirectives.mdfeedback.mdforms.mdframework-core.mdicons.mdindex.mdinternationalization.mdlab-components.mdnavigation.mdtheming.mdtransitions.mdutilities.md

theming.mddocs/

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

```