DEV Community

Cover image for vue-multiple-themes v4: Dynamic Multi-Theme Support for Vue 2 & 3
Pooya Golchian
Pooya Golchian

Posted on • Originally published at pooya.blog

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

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.

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

pnpm add vue-multiple-themes
Enter fullscreen mode Exit fullscreen mode

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

Quick Start: Vue 3

Register the plugin once in main.ts:


const app = createApp(App);
app.use(VueMultipleThemesPlugin, {
  defaultTheme: 'dark',
  strategy: 'attribute',
  persist: true,
});
app.mount('#app');
Enter fullscreen mode Exit fullscreen mode

Then use useTheme() anywhere:

<script setup lang="ts">

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>
Enter fullscreen mode Exit fullscreen mode

CSS Custom Properties

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

.card {
  background: var(--vmt-background);
  color: var(--vmt-foreground);
  border: 1px solid var(--vmt-border);
}
Enter fullscreen mode Exit fullscreen mode

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

The 7 Preset Themes

Name Character
light Clean white + indigo
dark Dark gray + violet
sepia Warm parchment browns
ocean Deep sea blues
forest Rich greens
sunset Warm oranges & reds
winter Icy blues & whites

Dynamic Theme Generation


const { light, dark } = generateThemePair('#6366f1');
const scale = generateColorScale('#6366f1', 9);
Enter fullscreen mode Exit fullscreen mode

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

TailwindCSS Integration

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

WCAG Utilities

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


contrastRatio('#6366f1', '#ffffff');  // 4.54
autoContrast('#6366f1');              // '#ffffff'
checkContrast('#6366f1', '#ffffff');
// { ratio: 4.54, aa: true, aaa: false, aaLarge: true, aaaLarge: true }
Enter fullscreen mode Exit fullscreen mode

useTheme() API

Option Type Default Description
themes ThemeDefinition[] preset list Available themes
defaultTheme string light Initial theme
strategy attribute / class / both attribute DOM application strategy
persist boolean true Save to localStorage
storageKey string vmt-theme localStorage key

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

Vue 2 Support


Vue.use(VueMultipleThemesPlugin, { defaultTheme: 'light' });
Enter fullscreen mode Exit fullscreen mode

GitHub ยท npm ยท Full Documentation

Top comments (0)