Skip to content
Back to Blog

vue-multiple-themes v4: Dynamic Multi-Theme Support for Vue 2 & 3

AI Transformation Lead
  • Vue
  • TypeScript
  • Open Source
  • CSS
  • Themes
  • TailwindCSS
  • Dark Mode
  • WCAG

I have been building UIs with Vue for years and one pattern comes up constantly, you need more than dark/light. Clients want seasonal themes, brand-specific palettes, and accessibility-compliant contrasts. I extracted all of that into a standalone, typed library: vue-multiple-themes.

Abstract visualization of multiple color themes applied to a Vue.js application

Full Documentation & Demo

What It Solves

The standard approach is toggling a .dark class on <html> and writing a wall of CSS overrides. That works for two themes. Scale to three or more and you get duplicated selectors, fragile specificity battles, and no tooling for generating accessible palettes.

vue-multiple-themes replaces that with:

  • CSS custom properties (--vmt-*) injected at the target element: every theme is a swap of values at one cascade layer
  • A reactive useTheme() composable accessible anywhere in the component tree
  • 7 preset themes ready to use immediately
  • A TailwindCSS plugin that exposes those tokens as Tailwind utilities
  • WCAG color utilities for contrast checking, mixing, and palette generation: all SSR-safe

Installation

bash
pnpm add vue-multiple-themes

Requires Vue 2.7+ or Vue 3. Zero runtime dependencies beyond Vue itself.

Quick Start: Vue 3

Register the plugin once in main.ts:

typescript
import { createApp } from 'vue'; import { VueMultipleThemesPlugin } from 'vue-multiple-themes'; import App from './App.vue'; const app = createApp(App); app.use(VueMultipleThemesPlugin, { defaultTheme: 'dark', strategy: 'attribute', persist: true, }); app.mount('#app');

Then use useTheme() anywhere:

vue
<script setup lang="ts"> import { useTheme, PRESET_THEMES } from 'vue-multiple-themes'; const { currentTheme, setTheme, themes } = useTheme({ themes: PRESET_THEMES }); </script> <template> <button v-for="t in themes" :key="t.name" @click="setTheme(t.name)"> {{ t.label }} </button> </template>

CSS Custom Properties

Once a theme is active, --vmt-* variables are available on <html>. Style components against them:

css
.card { background: var(--vmt-background); color: var(--vmt-foreground); border: 1px solid var(--vmt-border); }

Switching themes updates every component instantly, no re-renders required.

The 7 Preset Themes

NameCharacter
lightClean white + indigo
darkDark gray + violet
sepiaWarm parchment browns
oceanDeep sea blues
forestRich greens
sunsetWarm oranges & reds
winterIcy blues & whites

Dynamic Theme Generation

typescript
import { generateThemePair, generateColorScale } from 'vue-multiple-themes'; const { light, dark } = generateThemePair('#6366f1'); const scale = generateColorScale('#6366f1', 9);

Ideal for SaaS products where each tenant sets a brand color and the full UI adapts automatically.

TailwindCSS Integration

javascript
const { createVmtPlugin } = require('vue-multiple-themes/tailwind'); module.exports = { plugins: [createVmtPlugin()] };
html
<div class="bg-vmt-surface text-vmt-foreground border-vmt-border"> Themes itself automatically on switch </div>

WCAG Utilities

Pure functions, no DOM, fully SSR-safe, tree-shakeable:

typescript
import { contrastRatio, autoContrast, checkContrast, lighten, darken } from 'vue-multiple-themes'; contrastRatio('#6366f1', '#ffffff'); // 4.54 autoContrast('#6366f1'); // '#ffffff' checkContrast('#6366f1', '#ffffff'); // { ratio: 4.54, aa: true, aaa: false, aaLarge: true, aaaLarge: true }

useTheme() API

OptionTypeDefaultDescription
themesThemeDefinition[]preset listAvailable themes
defaultThemestringlightInitial theme
strategyattribute / class / bothattributeDOM application strategy
persistbooleantrueSave to localStorage
storageKeystringvmt-themelocalStorage key

Returns: { currentTheme, currentName, themes, setTheme, nextTheme, prevTheme }

Vue 2 Support

javascript
import Vue from 'vue'; import { VueMultipleThemesPlugin } from 'vue-multiple-themes'; Vue.use(VueMultipleThemesPlugin, { defaultTheme: 'light' });

GitHub · npm · Full Documentation

Questions about this piece

Follow-ups readers ask most often about the argument above.

  • Yes. The package uses vue-demi for a single installation that supports Vue 2.7+ and Vue 3. The same composable API and plugin registration work identically across both versions.

  • The useTheme() composable is localStorage-persistent by default. The storageKey option (default: vmt-theme) controls which key is used. Set persist: false to opt out.

  • Yes. generateThemePair(hex) creates a light/dark pair from any hex color. generateColorScale(hex, steps) builds a full palette. Both are tree-shakeable and work without any DOM dependency.

  • Yes. A TailwindCSS plugin is included. After registering it in tailwind.config.js you get utilities like bg-vmt-primary, text-vmt-foreground, and border-vmt-border that map to live CSS custom properties.

  • checkContrast(fg, bg) returns the contrast ratio plus pass/fail flags for AA, AAA, AA-large, and AAA-large WCAG 2.1 levels. autoContrast(color) automatically picks white or black for maximum legibility.