@maz-ui/themes
| Install | |
|---|---|
npm install @maz-ui/themes |
|
| Latest Version: | 5.0.0 |
| Vue: | 3 |
| License: | MIT |
| Last Updated: | Oct 21, 2026 |
| Links: | GitHub · npm |
@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-schememakes 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')→ addsdarkClass(default.dark) to<html>→ CSS appliescolor-scheme: only dark.setColorMode('light')→ addslightClass(default.light) to<html>→ CSS appliescolor-scheme: only light.setColorMode('auto')→ removes both classes, leaving onlycolor-scheme: light darkon:rootso the browser follows the systemprefers-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-schemeviacolor-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
animateis 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)whenmode: '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-50through--maz-X-950are 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
:rootascolor-scheme: light dark. WithdarkModeStrategy: '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
A lightweight, framework-agnostic color palette theme switcher using CSS variabl...
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 | |
| 4.7.6 | MIT | |
| 4.7.4 | MIT | |
| 4.7.3 | MIT | |
| 4.7.2 | MIT | |
| 4.7.1 | MIT | |
| 4.7.0 | MIT | |
| 4.6.1 | MIT | |
| 4.4.0 | MIT | |
| 4.3.0 | MIT | |
| 4.2.1 | MIT | |
| 4.1.7 | MIT | |
| 4.1.5 | MIT | |
| 4.1.4 | MIT | |
| 4.1.3 | MIT | |
| 4.1.2 | MIT | |
| 4.1.1 | MIT | |
| 4.1.0 | MIT | |
| 4.0.1 | MIT | |
| 4.0.0 | 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 | |
| 5.0.0-beta.24 | MIT | |
| 5.0.0-beta.22 | MIT | |
| 5.0.0-beta.20 | MIT | |
| 5.0.0-beta.18 | MIT | |
| 5.0.0-beta.17 | MIT | |
| 5.0.0-beta.16 | MIT | |
| 5.0.0-beta.11 | MIT | |
| 5.0.0-beta.8 | MIT | |
| 5.0.0-beta.7 | MIT | |
| 5.0.0-beta.6 | MIT | |
| 5.0.0-beta.5 | MIT | |
| 5.0.0-beta.4 | MIT | |
| 5.0.0-beta.3 | MIT | |
| 5.0.0-beta.2 | MIT | |
| 5.0.0-beta.1 | MIT | |
| 5.0.0-beta.0 | MIT | |
| 5.0.0-canary.ef64a02.0 | MIT | |
| 5.0.0-canary.07195c6.0 | MIT | |
| 4.9.2-beta.0 | MIT | |
| 4.7.10-beta.0 | MIT | |
| 4.7.6-beta.0 | MIT | |
| 4.7.0-beta.1 | MIT | |
| 4.7.0-beta.0 | MIT | |
| 4.4.0-beta.0 | MIT | |
| 4.3.0-rc.0 | MIT |
Showing the latest 30 of 59. See every release on npm