@maz-ui/themes

Theme system for Maz-UI with TypeScript support and CSS variables
158,459 565
Install
npm install @maz-ui/themes
Latest Version:5.0.0
Vue:3
License:MIT
Last Updated:Oct 21, 2026
Links: GitHub  ·  npm
Maintainer: louismazel

@maz-ui/themes

High-performance and typed theme system for Maz-UI.

Features

  • 🎨 Native light-dark() + color-scheme - Modern CSS theming with zero JS overhead for the light/dark switch
  • 🌓 Native dark mode - color-scheme makes native form controls, scrollbars, and built-in widgets adapt automatically
  • ✨ View Transitions - Optional full-page animated theme switch via document.startViewTransition()
  • 🛡️ Anti-FART - <meta name="color-scheme"> injected at boot to prevent any Flash of inAccurate coloR Theme
  • 🚀 Automatic generation - Automatically generates color variants (50-950) via OKLCh relative color syntax
  • ⚡ Flexible strategies - Runtime injection or build-time generation
  • 🛡️ Strict TypeScript - Complete types for optimal DX
  • 🎯 Zero FOUC - Pass the preset object so the full CSS renders synchronously on first paint
  • 🔧 Configurable presets - Ready-to-use and customizable presets

Installation

npm install @maz-ui/themes

Basic usage

1. Plugin installation

// main.ts
import { MazUiTheme } from '@maz-ui/themes/plugin'
import { mazUi } from '@maz-ui/themes/presets/mazUi'
import { createApp } from 'vue'

const app = createApp(App)

app.use(MazUiTheme, {
  preset: mazUi,
  strategy: 'runtime',
  darkModeStrategy: 'class',
  // Class added to <html> when dark mode is forced (default: 'dark')
  darkClass: 'dark',
  // Class added to <html> when light mode is forced (default: 'light')
  lightClass: 'light',
  // remember the active preset name across reloads (default: true)
  persistPreset: true,
})

Preset persistence

The active preset is stored in a maz-preset cookie (1-year TTL, SameSite=Lax) as a base:active pair, where base is the identity of the preset declared in options.preset and active is the one currently applied. The configured options.preset is always authoritative: a runtime switch made via useTheme().updateTheme() is only restored on reload when its base matches the configured preset, so a stale cookie - or a cookie written by another app on the same host - can never override the preset your app declares. A foreign or legacy (plain-name) cookie is ignored and healed on the next resolution. Set persistPreset: false to opt out - no cookie is read or written.

2. Usage in components

<script setup>
import { useTheme } from '@maz-ui/themes'

const { toggleDarkMode, isDark } = useTheme()
</script>

<template>
  <div class="maz:bg-surface maz:text-foreground">
    <button
      class="maz:bg-primary maz:text-primary-foreground maz:rounded-md"
      @click="toggleDarkMode"
    >
      Toggle Dark Mode
    </button>
  </div>
</template>

Color modes and class toggling

darkClass and lightClass interact with darkModeStrategy to control how <html> is decorated.

darkModeStrategy: 'class' (default)

  • setColorMode('dark') → adds darkClass (default .dark) to <html> → CSS applies color-scheme: only dark.
  • setColorMode('light') → adds lightClass (default .light) to <html> → CSS applies color-scheme: only light.
  • setColorMode('auto') → removes both classes, leaving only color-scheme: light dark on :root so the browser follows the system prefers-color-scheme.

Forcing both classes (light/dark) on the root ensures the browser-native widgets (scrollbars, native <select>, date pickers, autofill backgrounds, …) match the explicit user choice rather than the OS preference.

darkModeStrategy: 'media'

  • No class is ever added to <html>.
  • The browser always follows prefers-color-scheme via color-scheme: light dark.
  • setColorMode() still updates the persisted cookie but does not force a visual override — system preference always wins.

Animated theme switch (View Transitions)

For a full-page animated swap (rather than per-variable color tweens), pass { animate: true } to setColorMode or toggleDarkMode:

import { useTheme } from '@maz-ui/themes'

const { toggleDarkMode, setColorMode } = useTheme()

await toggleDarkMode({ animate: true })
await setColorMode('dark', { animate: true })
  • Wraps the switch in document.startViewTransition() so the browser captures a snapshot of the page before/after and crossfades between them.
  • The helper is lazy-imported — when animate is not used, the View Transitions glue stays out of the boot bundle.
  • Graceful degradation: in browsers that don't support the API (e.g., Firefox stable as of mid-2026), the change is applied immediately with no animation — no error thrown.

Available presets

Default

import { mazUi } from '@maz-ui/themes/presets/mazUi'

Pristine

import { pristine } from '@maz-ui/themes/presets/pristine'

Ocean

import { ocean } from '@maz-ui/themes/presets/ocean'

Obsidian

import { obsidian } from '@maz-ui/themes/presets/obsidian'

Nova

import { nova } from '@maz-ui/themes/presets/nova'

Creating custom presets

import { definePreset, mazUi } from '@maz-ui/themes'

const myPreset = definePreset({
  base: mazUi,
  overrides: {
    name: 'my-theme',
    scales: {
      rounded: { md: '0.75rem' },
    },
    colors: {
      light: {
        primary: 'oklch(0.6 0.2 250)',
        secondary: 'oklch(0.96 0.01 250)',
      },
      dark: {
        primary: 'oklch(0.7 0.2 250)',
        secondary: 'oklch(0.2 0.01 250)',
      },
    },
  },
})

Composable API

import { useTheme } from '@maz-ui/themes'

const {
  preset, // ComputedRef<ThemePreset>
  presetName, // ComputedRef<string>
  colorMode, // Ref<'light' | 'dark' | 'auto'>
  isDark, // ComputedRef<boolean>
  strategy, // ComputedRef<'runtime' | 'buildtime'>
  updateTheme, // (preset: ThemePreset | ThemePresetName | ThemePresetOverrides) => Promise<void>
  setColorMode, // (mode: 'light' | 'dark' | 'auto', options?: { animate?: boolean }) => Promise<void>
  toggleDarkMode, // (options?: { animate?: boolean }) => Promise<void>
} = useTheme()

setColorMode and toggleDarkMode are async (Promise<void>). The promise resolves once the change — and the optional View Transition — has been applied. Callers can ignore the return value when no transition is needed.

The optional { animate?: boolean } parameter enables the View Transitions API (see Animated theme switch).

Strategies

Runtime (recommended)

The full theme CSS is generated and injected synchronously on first paint, on both client and server (via useHead in Nuxt). updateTheme(...) re-injects the new CSS at runtime.

Buildtime

CSS generated at build-time and included in the bundle. Nothing is injected at runtime; you must include the generated CSS file yourself.

Generated CSS variables

The generator produces a modern, native CSS contract:

  • Base colors are emitted as light-dark(L, D) when mode: 'both' — a single declaration that the browser resolves to the active scheme. Example: --maz-primary: light-dark(oklch(0.6 0.2 250), oklch(0.7 0.2 250));.
  • Scale palettes --maz-X-50 through --maz-X-950 are derived from the base via OKLCh relative color syntax — oklch(from var(--maz-X) clamp(0, calc(l ± offset), 1) calc(c * mult) h). Lightness offsets follow Tailwind v4's perceptual spread; chroma is preserved (and tapered near extremes) so derived shades stay vibrant rather than washed-out.
  • Color scheme is declared on :root as color-scheme: light dark. With darkModeStrategy: 'class', the generator also emits .dark { color-scheme: only dark; } and .light { color-scheme: only light; } so an explicit user choice overrides the system preference (and native widgets follow).
  • Design tokens — --maz-rounded-*, --maz-shadow-*, --maz-font-family, motion durations, easings, etc. — are bridged into Tailwind v4 via @theme inline.

Build-time

import { buildThemeCSS, generateThemeBundle } from '@maz-ui/themes'

// CSS for a preset
const css = buildThemeCSS({
  preset: myPreset,
  darkSelector: 'class',
})

// Bundle for multiple presets
const bundle = generateThemeBundle([mazUi, darkPreset], {
  darkSelector: 'class',
})

Related Packages

@ohos-ports/maz-ui-themes

Theme system for Maz-UI with TypeScript support and CSS variables

176 6
@qingmingmu/theme

青冥组件系统 —— CSS 变量主题系统

616
js-theme-switcher

A lightweight, framework-agnostic color palette theme switcher using CSS variabl...

361 0
tailwindcss-themeable

multiple themes support for tailwindcss, with Dracula built-in

7,097 59
@gits-id/theme

GITS Default Tailwind Colors

3,772

Version History

Version Released License
5.0.0 MIT
4.9.2 MIT
4.9.0 MIT
4.8.0 MIT
4.7.9 MIT
Pre-releases (59)
Version Released License
5.0.0-beta.46 MIT
5.0.0-beta.44 MIT
5.0.0-beta.39 MIT
5.0.0-beta.28 MIT
5.0.0-beta.25 MIT