maz-ui 5.0.0 → 5.1.0-beta.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/assets/{MazDropzone.DJ8au25t.css → MazDropzone.BW2grpTi.css} +1 -1
- package/dist/chunks/{MazDrawer.DANxKYxJ.js → MazDrawer.CC8zomfx.js} +2 -2
- package/dist/chunks/{MazDropzone.POH3pmfi.js → MazDropzone.Dez5j0WP.js} +2 -2
- package/dist/components/MazDrawer.js +1 -1
- package/dist/components/MazDropzone.js +1 -1
- package/dist/components/MazDropzone.vue.d.ts +2 -2
- package/dist/components/MazInputPrice.vue.d.ts +1 -1
- package/dist/components/index.js +2 -2
- package/docs/README.md +207 -0
- package/docs/agents/agents-md.md +52 -0
- package/docs/agents/claude-rule.md +52 -0
- package/docs/components/maz-accordion.md +76 -0
- package/docs/components/maz-alert.md +241 -0
- package/docs/components/maz-animated-counter.md +87 -0
- package/docs/components/maz-animated-element.md +46 -0
- package/docs/components/maz-animated-text.md +53 -0
- package/docs/components/maz-avatar.md +193 -0
- package/docs/components/maz-backdrop.md +178 -0
- package/docs/components/maz-badge.md +167 -0
- package/docs/components/maz-bottom-sheet.md +257 -0
- package/docs/components/maz-btn-group.md +316 -0
- package/docs/components/maz-btn.md +423 -0
- package/docs/components/maz-card-spotlight.md +84 -0
- package/docs/components/maz-card.md +321 -0
- package/docs/components/maz-carousel.md +153 -0
- package/docs/components/maz-chart.md +206 -0
- package/docs/components/maz-checkbox.md +138 -0
- package/docs/components/maz-checklist.md +251 -0
- package/docs/components/maz-circular-progress-bar.md +109 -0
- package/docs/components/maz-code-highlight.md +155 -0
- package/docs/components/maz-container.md +241 -0
- package/docs/components/maz-date-picker.md +791 -0
- package/docs/components/maz-dialog-confirm.md +215 -0
- package/docs/components/maz-dialog.md +143 -0
- package/docs/components/maz-drawer.md +106 -0
- package/docs/components/maz-dropdown.md +419 -0
- package/docs/components/maz-dropzone.md +387 -0
- package/docs/components/maz-expand-animation.md +82 -0
- package/docs/components/maz-fullscreen-loader.md +58 -0
- package/docs/components/maz-gallery.md +79 -0
- package/docs/components/maz-icon.md +203 -0
- package/docs/components/maz-input-code.md +73 -0
- package/docs/components/maz-input-number.md +79 -0
- package/docs/components/maz-input-phone-number.md +536 -0
- package/docs/components/maz-input-price.md +68 -0
- package/docs/components/maz-input-tags.md +112 -0
- package/docs/components/maz-input.md +334 -0
- package/docs/components/maz-lazy-img.md +113 -0
- package/docs/components/maz-link.md +146 -0
- package/docs/components/maz-loading-bar.md +38 -0
- package/docs/components/maz-markdown-editor.md +320 -0
- package/docs/components/maz-pagination.md +82 -0
- package/docs/components/maz-popover.md +794 -0
- package/docs/components/maz-pull-to-refresh.md +222 -0
- package/docs/components/maz-radio-buttons.md +313 -0
- package/docs/components/maz-radio.md +119 -0
- package/docs/components/maz-read-more.md +209 -0
- package/docs/components/maz-reading-progress-bar.md +96 -0
- package/docs/components/maz-select-country.md +513 -0
- package/docs/components/maz-select.md +318 -0
- package/docs/components/maz-sidebar-content.md +25 -0
- package/docs/components/maz-sidebar-footer.md +25 -0
- package/docs/components/maz-sidebar-group.md +27 -0
- package/docs/components/maz-sidebar-header.md +25 -0
- package/docs/components/maz-sidebar-menu-button.md +41 -0
- package/docs/components/maz-sidebar-menu-item.md +25 -0
- package/docs/components/maz-sidebar-menu-sub.md +32 -0
- package/docs/components/maz-sidebar-menu.md +25 -0
- package/docs/components/maz-sidebar-separator.md +21 -0
- package/docs/components/maz-sidebar-trigger.md +25 -0
- package/docs/components/maz-sidebar.md +507 -0
- package/docs/components/maz-skeleton.md +226 -0
- package/docs/components/maz-slider.md +165 -0
- package/docs/components/maz-spinner.md +84 -0
- package/docs/components/maz-stepper.md +301 -0
- package/docs/components/maz-switch.md +86 -0
- package/docs/components/maz-table-cell.md +27 -0
- package/docs/components/maz-table-row.md +28 -0
- package/docs/components/maz-table-title.md +27 -0
- package/docs/components/maz-table-virtualizer.md +31 -0
- package/docs/components/maz-table.md +426 -0
- package/docs/components/maz-tabs-bar.md +43 -0
- package/docs/components/maz-tabs-content-item.md +27 -0
- package/docs/components/maz-tabs-content.md +27 -0
- package/docs/components/maz-tabs.md +308 -0
- package/docs/components/maz-textarea.md +255 -0
- package/docs/components/maz-ticker.md +427 -0
- package/docs/components/maz-timeline.md +304 -0
- package/docs/components/maz-ui-provider.md +26 -0
- package/docs/components/maz-window-mockup.md +182 -0
- package/docs/composables/use-aos.md +41 -0
- package/docs/composables/use-breakpoints.md +42 -0
- package/docs/composables/use-dialog.md +58 -0
- package/docs/composables/use-display-names.md +130 -0
- package/docs/composables/use-drag.md +185 -0
- package/docs/composables/use-form-validator.md +1123 -0
- package/docs/composables/use-idle-timeout.md +172 -0
- package/docs/composables/use-pull-to-refresh.md +86 -0
- package/docs/composables/use-reading-time.md +132 -0
- package/docs/composables/use-string-matching.md +41 -0
- package/docs/composables/use-swipe.md +171 -0
- package/docs/composables/use-timer.md +104 -0
- package/docs/composables/use-toast.md +50 -0
- package/docs/composables/use-user-visibility.md +127 -0
- package/docs/composables/use-wait.md +50 -0
- package/docs/composables/use-window-size.md +25 -0
- package/docs/directives/click-outside.md +182 -0
- package/docs/directives/fullscreen-img.md +83 -0
- package/docs/directives/lazy-img.md +182 -0
- package/docs/directives/tooltip.md +298 -0
- package/docs/directives/zoom-img.md +100 -0
- package/docs/ecosystem/eslint-config.md +289 -0
- package/docs/ecosystem/icons/icon-set.md +25 -0
- package/docs/ecosystem/icons/index.md +584 -0
- package/docs/ecosystem/mcp.md +165 -0
- package/docs/ecosystem/node/exec-promise.md +93 -0
- package/docs/ecosystem/node/index.md +53 -0
- package/docs/ecosystem/node/logger.md +152 -0
- package/docs/ecosystem/node/print-banner.md +101 -0
- package/docs/ecosystem/node/setup-agents.md +102 -0
- package/docs/ecosystem/nuxt.md +551 -0
- package/docs/ecosystem/stylelint-config.md +310 -0
- package/docs/ecosystem/themes.md +966 -0
- package/docs/ecosystem/translations.md +1173 -0
- package/docs/ecosystem/utils/camel-case.md +37 -0
- package/docs/ecosystem/utils/capitalize.md +37 -0
- package/docs/ecosystem/utils/check-availability.md +85 -0
- package/docs/ecosystem/utils/cookie.md +86 -0
- package/docs/ecosystem/utils/country-code-to-unicode-flag.md +128 -0
- package/docs/ecosystem/utils/currency.md +53 -0
- package/docs/ecosystem/utils/date.md +53 -0
- package/docs/ecosystem/utils/debounce-callback.md +44 -0
- package/docs/ecosystem/utils/debounce-id.md +75 -0
- package/docs/ecosystem/utils/debounce.md +71 -0
- package/docs/ecosystem/utils/fetch-locale-ip.md +39 -0
- package/docs/ecosystem/utils/format-json.md +39 -0
- package/docs/ecosystem/utils/format-phone-number.md +43 -0
- package/docs/ecosystem/utils/get-browser-locale.md +35 -0
- package/docs/ecosystem/utils/get-country-flag-url.md +67 -0
- package/docs/ecosystem/utils/get-error-message.md +45 -0
- package/docs/ecosystem/utils/idle-timeout.md +96 -0
- package/docs/ecosystem/utils/index.md +57 -0
- package/docs/ecosystem/utils/is-client.md +38 -0
- package/docs/ecosystem/utils/is-equal.md +44 -0
- package/docs/ecosystem/utils/is-ios.md +43 -0
- package/docs/ecosystem/utils/is-server.md +37 -0
- package/docs/ecosystem/utils/is-standalone-mode.md +49 -0
- package/docs/ecosystem/utils/kebab-case.md +42 -0
- package/docs/ecosystem/utils/normalize-string.md +83 -0
- package/docs/ecosystem/utils/number.md +51 -0
- package/docs/ecosystem/utils/pascal-case.md +41 -0
- package/docs/ecosystem/utils/prevent-ios-input-zoom.md +72 -0
- package/docs/ecosystem/utils/script-loader.md +85 -0
- package/docs/ecosystem/utils/sleep.md +65 -0
- package/docs/ecosystem/utils/snake-case.md +42 -0
- package/docs/ecosystem/utils/swipe-handler.md +97 -0
- package/docs/ecosystem/utils/textarea-autogrow.md +47 -0
- package/docs/ecosystem/utils/throttle-id.md +54 -0
- package/docs/ecosystem/utils/throttle.md +63 -0
- package/docs/ecosystem/utils/truthy-filter.md +37 -0
- package/docs/ecosystem/utils/types/deep-key-of.md +54 -0
- package/docs/ecosystem/utils/types/deep-partial.md +48 -0
- package/docs/ecosystem/utils/types/deep-required.md +45 -0
- package/docs/ecosystem/utils/types/flatten-object-keys.md +50 -0
- package/docs/ecosystem/utils/types/generic-instance-type.md +48 -0
- package/docs/ecosystem/utils/types/infer-maybe-ref.md +41 -0
- package/docs/ecosystem/utils/upper-first.md +38 -0
- package/docs/ecosystem/utils/user-visibility.md +75 -0
- package/docs/guide/ai-agents.md +237 -0
- package/docs/guide/browser-support.md +88 -0
- package/docs/guide/getting-started.md +191 -0
- package/docs/guide/global-defaults.md +103 -0
- package/docs/guide/maz-ui-provider.md +231 -0
- package/docs/guide/migration-v5.md +871 -0
- package/docs/guide/resolvers.md +650 -0
- package/docs/guide/tailwind.md +483 -0
- package/docs/guide/vue.md +286 -0
- package/docs/manifest.json +1 -0
- package/docs/plugins/aos.md +312 -0
- package/docs/plugins/dialog.md +269 -0
- package/docs/plugins/toast.md +221 -0
- package/docs/plugins/wait.md +78 -0
- package/docs/reference/icons.md +46 -0
- package/docs/reference/tailwind-tokens.md +191 -0
- package/docs/rules.md +57 -0
- package/package.json +31 -4
|
@@ -0,0 +1,966 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: "Theming"
|
|
3
|
+
description: "Modern and performant theme system for Maz-UI built on native CSS features (light-dark(), color-scheme, color-mix(in oklch)) for v5."
|
|
4
|
+
package: "@maz-ui/themes"
|
|
5
|
+
url: "https://maz-ui.com/ecosystem/themes"
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Theming
|
|
9
|
+
|
|
10
|
+
> Modern and performant theme system for Maz-UI built on native CSS features (light-dark(), color-scheme, color-mix(in oklch)) for v5.
|
|
11
|
+
|
|
12
|
+
- Package: `@maz-ui/themes`
|
|
13
|
+
- Import: `import { definePreset, useTheme } from '@maz-ui/themes'` or `import { mazUi } from '@maz-ui/themes/presets/mazUi'`
|
|
14
|
+
- Web: https://maz-ui.com/ecosystem/themes
|
|
15
|
+
|
|
16
|
+
Modern and performant theme system for Maz-UI built on native CSS features (`light-dark()`, `color-scheme`, `color-mix(in oklch)`) for v5.
|
|
17
|
+
|
|
18
|
+
## Features
|
|
19
|
+
|
|
20
|
+
- **Native `light-dark()` + `color-scheme`** - Modern CSS theming with zero JS overhead for the light/dark switch
|
|
21
|
+
- **Native widget theming** - `color-scheme` makes scrollbars, native `<select>`, date pickers and autofill follow the active mode automatically
|
|
22
|
+
- **Animated theme switch** - Optional full-page View Transitions via `setColorMode(..., { animate: true })`
|
|
23
|
+
- **Anti-FART** - `<meta name="color-scheme">` injected at boot to prevent any Flash of inAccurate coloR Theme (no setup needed)
|
|
24
|
+
- **Automatic color scales** - `--maz-X-50` through `--maz-X-950` derived via OKLCh relative color syntax - perceptually uniform and chroma-preserving
|
|
25
|
+
- **Performance Strategies** - Runtime injection or build-time generation according to your needs
|
|
26
|
+
- **Strict TypeScript** - Complete types for perfect DX
|
|
27
|
+
- **Zero FOUC** - Pass the preset object up front; the full CSS is injected synchronously on first paint
|
|
28
|
+
- **Flexible Presets** - Ready-to-use and customizable configurations
|
|
29
|
+
|
|
30
|
+
## Theme Editor
|
|
31
|
+
|
|
32
|
+
Create your own theme with the Theme Editor.
|
|
33
|
+
|
|
34
|
+
## Quick Usage
|
|
35
|
+
|
|
36
|
+
### 1. Plugin Configuration with `MazUi` plugin
|
|
37
|
+
|
|
38
|
+
To avoid FOUC (Flash of Unstyled Content), **you should provide the theme preset**.
|
|
39
|
+
|
|
40
|
+
```typescript
|
|
41
|
+
// main.ts
|
|
42
|
+
import { MazUi } from 'maz-ui/plugins/maz-ui'
|
|
43
|
+
import { mazUi } from '@maz-ui/themes/presets/mazUi'
|
|
44
|
+
import { createApp } from 'vue'
|
|
45
|
+
|
|
46
|
+
const app = createApp(App)
|
|
47
|
+
|
|
48
|
+
app.use(MazUi, {
|
|
49
|
+
theme: {
|
|
50
|
+
preset: mazUi, // pristine | ocean | obsidian | nova
|
|
51
|
+
strategy: 'runtime', // 'runtime' | 'buildtime'
|
|
52
|
+
darkModeStrategy: 'class', // 'class' | 'media' (only if mode is `both`)
|
|
53
|
+
mode: 'both', // 'light' | 'dark' | 'both' (supported color modes)
|
|
54
|
+
colorMode: 'auto', // 'auto' | 'light' | 'dark' (initial color mode, only if mode is 'both')
|
|
55
|
+
// Class added to <html> when dark mode is forced (default: 'dark')
|
|
56
|
+
darkClass: 'dark',
|
|
57
|
+
// Class added to <html> when light mode is forced (default: 'light')
|
|
58
|
+
lightClass: 'light',
|
|
59
|
+
persistPreset: true, // remember the active preset name across reloads
|
|
60
|
+
}
|
|
61
|
+
})
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
**Tip: Alternative: MazUiProvider**
|
|
65
|
+
|
|
66
|
+
You can also initialize the theme via the [`MazUiProvider`](../guide/maz-ui-provider.md) component for lazy-loaded pages or subtree-scoped themes.
|
|
67
|
+
|
|
68
|
+
### 2. Setup your CSS to support theme foundation and dark mode
|
|
69
|
+
|
|
70
|
+
```css
|
|
71
|
+
html {
|
|
72
|
+
font-size: var(--maz-base-font-size);
|
|
73
|
+
font-family: var(--maz-font-family);
|
|
74
|
+
background-color: var(--maz-surface);
|
|
75
|
+
color: var(--maz-foreground);
|
|
76
|
+
}
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
### 3. Using in your components
|
|
80
|
+
|
|
81
|
+
```vue
|
|
82
|
+
<script setup>
|
|
83
|
+
import { useTheme } from '@maz-ui/themes'
|
|
84
|
+
|
|
85
|
+
const { toggleDarkMode, isDark, updateTheme } = useTheme()
|
|
86
|
+
</script>
|
|
87
|
+
|
|
88
|
+
<template>
|
|
89
|
+
<div class="bg-surface text-foreground">
|
|
90
|
+
<button
|
|
91
|
+
class="rounded-md bg-primary text-primary-foreground"
|
|
92
|
+
@click="toggleDarkMode"
|
|
93
|
+
>
|
|
94
|
+
{{ isDark ? '☀️' : '🌙' }} Toggle theme
|
|
95
|
+
</button>
|
|
96
|
+
</div>
|
|
97
|
+
</template>
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
## Configuration
|
|
101
|
+
|
|
102
|
+
- `preset`: The theme preset to use
|
|
103
|
+
- `overrides` (optional): Override specific parts of the theme
|
|
104
|
+
- `strategy` (optional, default `'runtime'`): The rendering strategy to use - `'runtime' | 'buildtime'`
|
|
105
|
+
- `darkModeStrategy` (optional, default `'class'`): The dark mode strategy - `'class' | 'media'`, only if you use mode `both`
|
|
106
|
+
- `mode` (optional, default `'both'`): The supported color modes - `'light' | 'dark' | 'both'`
|
|
107
|
+
- `colorMode` (optional, default `'auto'`): The initial color mode - `'light' | 'dark' | 'auto'` (only if mode is `'both'`)
|
|
108
|
+
- `darkClass` (optional, default `'dark'`): Class added to `<html>` when `colorMode === 'dark'`
|
|
109
|
+
- `lightClass` (optional, default `'light'`): Class added to `<html>` when `colorMode === 'light'` - mirror of `darkClass`
|
|
110
|
+
- `persistPreset` (optional, default `true`): Persist the active preset name in the `maz-preset` cookie so it is restored on reload.
|
|
111
|
+
|
|
112
|
+
### Preset persistence
|
|
113
|
+
|
|
114
|
+
When `persistPreset` is enabled (default), `@maz-ui/themes` stores the resolved preset name in a `maz-preset` cookie (1-year TTL, `SameSite=Lax`) - exactly like `maz-color-mode` for the dark/light state. The cookie is:
|
|
115
|
+
|
|
116
|
+
- **Read at boot** and used to resolve the active preset - even when `options.preset` is provided. `options.preset` (string name **or** preset object) is treated as the *default* the app boots with, while the cookie carries the user's last explicit choice. The cookie wins.
|
|
117
|
+
- **Written** after every successful preset resolution and after every `useTheme().updateTheme()` call (idempotent - no write if the value already matches).
|
|
118
|
+
- **Auto-cleared** when the saved name no longer resolves (e.g. typo, preset removed in a downgrade) - the runtime falls back to `options.preset` (or default) and clears the cookie silently.
|
|
119
|
+
|
|
120
|
+
Custom preset names are stored exactly like bundled ones: `@maz-ui/themes` does not maintain a whitelist. Switch the option off to disable any cookie read/write:
|
|
121
|
+
|
|
122
|
+
```typescript
|
|
123
|
+
app.use(MazUi, {
|
|
124
|
+
theme: {
|
|
125
|
+
preset: mazUi,
|
|
126
|
+
persistPreset: false, // never write or read the maz-preset cookie
|
|
127
|
+
},
|
|
128
|
+
})
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
Useful when:
|
|
132
|
+
- You want zero theme-related cookies (privacy / regulatory).
|
|
133
|
+
- The active preset is fully driven by the consumer app and no end-user switching is exposed.
|
|
134
|
+
|
|
135
|
+
## Color modes and class toggling
|
|
136
|
+
|
|
137
|
+
`darkClass` and `lightClass` interact with `darkModeStrategy` to control how `<html>` is decorated when the user picks an explicit mode.
|
|
138
|
+
|
|
139
|
+
### `darkModeStrategy: 'class'` (default)
|
|
140
|
+
|
|
141
|
+
- `setColorMode('dark')` adds `darkClass` (default `.dark`) to `<html>` → `color-scheme: only dark`.
|
|
142
|
+
- `setColorMode('light')` adds `lightClass` (default `.light`) to `<html>` → `color-scheme: only light`.
|
|
143
|
+
- `setColorMode('auto')` removes both classes → `color-scheme: light dark` lets the browser follow the system `prefers-color-scheme`.
|
|
144
|
+
|
|
145
|
+
Forcing both classes on the root ensures native widgets (scrollbars, native `<select>`, date pickers, autofill backgrounds, …) match the explicit user choice rather than the OS preference.
|
|
146
|
+
|
|
147
|
+
### `darkModeStrategy: 'media'`
|
|
148
|
+
|
|
149
|
+
- No class is ever added to `<html>`.
|
|
150
|
+
- The browser always follows `prefers-color-scheme` via `color-scheme: light dark`.
|
|
151
|
+
- `setColorMode()` still updates the persisted cookie but does **not** force a visual override.
|
|
152
|
+
|
|
153
|
+
## Animated theme switch
|
|
154
|
+
|
|
155
|
+
For a full-page animated swap (rather than per-variable color tweens), pass `{ animate: true }` to `setColorMode` or `toggleDarkMode`:
|
|
156
|
+
|
|
157
|
+
```vue
|
|
158
|
+
<script setup>
|
|
159
|
+
import { useTheme } from '@maz-ui/themes'
|
|
160
|
+
|
|
161
|
+
const { toggleDarkMode, setColorMode } = useTheme()
|
|
162
|
+
|
|
163
|
+
async function onToggle() {
|
|
164
|
+
await toggleDarkMode({ animate: true })
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
async function forceDark() {
|
|
168
|
+
await setColorMode('dark', { animate: true })
|
|
169
|
+
}
|
|
170
|
+
</script>
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
- Wraps the switch in `document.startViewTransition()` so the browser captures a snapshot of the page before/after and crossfades between them.
|
|
174
|
+
- The View Transition glue is **lazy-imported** - when `animate` is not used, it stays out of the boot bundle.
|
|
175
|
+
- **Graceful degradation:** in browsers that don't support the API, the change is applied immediately with no animation (no error thrown).
|
|
176
|
+
|
|
177
|
+
## Interactive Demo
|
|
178
|
+
|
|
179
|
+
**Real-time theme control**
|
|
180
|
+
|
|
181
|
+
```vue
|
|
182
|
+
<script setup>
|
|
183
|
+
import { useTheme } from '@maz-ui/themes'
|
|
184
|
+
|
|
185
|
+
const { presetName, updateTheme, colorMode, setColorMode } = useTheme()
|
|
186
|
+
</script>
|
|
187
|
+
|
|
188
|
+
<template>
|
|
189
|
+
<div class="demo-theme-controls">
|
|
190
|
+
<div class="space-y-4">
|
|
191
|
+
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
|
192
|
+
<MazBtn color="primary">Primary Button</MazBtn>
|
|
193
|
+
<MazBtn color="secondary">Secondary Button</MazBtn>
|
|
194
|
+
<MazBtn color="success">Success Button</MazBtn>
|
|
195
|
+
<MazBtn color="warning">Warning Button</MazBtn>
|
|
196
|
+
</div>
|
|
197
|
+
<div class="theme-controls space-y-4">
|
|
198
|
+
<div class="flex items-center gap-4">
|
|
199
|
+
<label class="text-sm font-medium">Mode:</label>
|
|
200
|
+
<MazBtn
|
|
201
|
+
size="sm"
|
|
202
|
+
:color="colorMode === 'light' ? 'primary' : 'secondary'"
|
|
203
|
+
@click="setColorMode('light')"
|
|
204
|
+
>
|
|
205
|
+
☀️ Light
|
|
206
|
+
</MazBtn>
|
|
207
|
+
<MazBtn
|
|
208
|
+
size="sm"
|
|
209
|
+
:color="colorMode === 'dark' ? 'primary' : 'secondary'"
|
|
210
|
+
@click="setColorMode('dark')"
|
|
211
|
+
>
|
|
212
|
+
🌙 Dark
|
|
213
|
+
</MazBtn>
|
|
214
|
+
<MazBtn
|
|
215
|
+
size="sm"
|
|
216
|
+
:color="colorMode === 'auto' ? 'primary' : 'secondary'"
|
|
217
|
+
@click="setColorMode('auto')"
|
|
218
|
+
>
|
|
219
|
+
🔄 Auto
|
|
220
|
+
</MazBtn>
|
|
221
|
+
</div>
|
|
222
|
+
<div class="space-y-2">
|
|
223
|
+
<label class="text-sm font-medium">Preset:</label>
|
|
224
|
+
<div class="flex gap-2">
|
|
225
|
+
<MazBtn
|
|
226
|
+
size="sm"
|
|
227
|
+
:color="presetName === 'maz-ui' ? 'primary' : 'secondary'"
|
|
228
|
+
@click="updateTheme('mazUi')"
|
|
229
|
+
>
|
|
230
|
+
Maz-UI
|
|
231
|
+
</MazBtn>
|
|
232
|
+
<MazBtn
|
|
233
|
+
size="sm"
|
|
234
|
+
:color="presetName === 'ocean' ? 'primary' : 'secondary'"
|
|
235
|
+
@click="updateTheme('ocean')"
|
|
236
|
+
>
|
|
237
|
+
Ocean
|
|
238
|
+
</MazBtn>
|
|
239
|
+
<MazBtn
|
|
240
|
+
size="sm"
|
|
241
|
+
:color="presetName === 'pristine' ? 'primary' : 'secondary'"
|
|
242
|
+
@click="updateTheme('pristine')"
|
|
243
|
+
>
|
|
244
|
+
Pristine
|
|
245
|
+
</MazBtn>
|
|
246
|
+
<MazBtn
|
|
247
|
+
size="sm"
|
|
248
|
+
:color="presetName === 'obsidian' ? 'primary' : 'secondary'"
|
|
249
|
+
@click="updateTheme('obsidian')"
|
|
250
|
+
>
|
|
251
|
+
Obsidian
|
|
252
|
+
</MazBtn>
|
|
253
|
+
</div>
|
|
254
|
+
</div>
|
|
255
|
+
</div>
|
|
256
|
+
</div>
|
|
257
|
+
</div>
|
|
258
|
+
</template>
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
## Available Presets
|
|
262
|
+
|
|
263
|
+
### Maz-UI (Default)
|
|
264
|
+
|
|
265
|
+
The default theme inspired by Maz-UI identity with vibrant and modern colors.
|
|
266
|
+
|
|
267
|
+
```typescript
|
|
268
|
+
import { mazUi } from '@maz-ui/themes/presets'
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
### Pristine
|
|
272
|
+
|
|
273
|
+
A minimalist and elegant theme inspired by Apple design with pure black and white aesthetics.
|
|
274
|
+
|
|
275
|
+
```typescript
|
|
276
|
+
import { pristine } from '@maz-ui/themes/presets'
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
### Ocean
|
|
280
|
+
|
|
281
|
+
A vibrant ocean-inspired theme with deep blues, aquatic greens, and coral accents. Features rounded borders and Poppins font for a fresh, modern look.
|
|
282
|
+
|
|
283
|
+
```typescript
|
|
284
|
+
import { ocean } from '@maz-ui/themes/presets'
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
### Obsidian
|
|
288
|
+
|
|
289
|
+
A dark and elegant theme with a focus on readability and minimalism.
|
|
290
|
+
|
|
291
|
+
```typescript
|
|
292
|
+
import { obsidian } from '@maz-ui/themes/presets'
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
### Nova
|
|
296
|
+
|
|
297
|
+
A modern startup / AI / creative preset - electric violet primary, hot coral secondary, cyan accent. Tight 0.5rem `md` radius, Geist + Inter font stack, snappy ease-out spring. Built for product / AI surfaces (think Linear, Vercel, OpenAI energy) where the UI itself should feel alive.
|
|
298
|
+
|
|
299
|
+
```typescript
|
|
300
|
+
import { nova } from '@maz-ui/themes/presets'
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
## Rendering Strategies
|
|
304
|
+
|
|
305
|
+
### ⚡ Runtime (Recommended)
|
|
306
|
+
|
|
307
|
+
CSS is generated and injected synchronously on first paint. Pass the preset object directly (`preset: mazUi`) so the full stylesheet is rendered before the first frame - no FOUC.
|
|
308
|
+
|
|
309
|
+
```typescript
|
|
310
|
+
import { mazUi } from '@maz-ui/themes/presets'
|
|
311
|
+
|
|
312
|
+
app.use(MazUi, {
|
|
313
|
+
theme: {
|
|
314
|
+
preset: mazUi,
|
|
315
|
+
strategy: 'runtime'
|
|
316
|
+
}
|
|
317
|
+
})
|
|
318
|
+
```
|
|
319
|
+
|
|
320
|
+
### 🏗️ Buildtime
|
|
321
|
+
|
|
322
|
+
CSS generated at build-time and included in the bundle.
|
|
323
|
+
You have to build your CSS files before running your app.
|
|
324
|
+
|
|
325
|
+
See Build-time for more details.
|
|
326
|
+
|
|
327
|
+
```typescript
|
|
328
|
+
import { mazUi } from '@maz-ui/themes/presets'
|
|
329
|
+
|
|
330
|
+
app.use(MazUi, {
|
|
331
|
+
theme: {
|
|
332
|
+
preset: mazUi,
|
|
333
|
+
strategy: 'buildtime'
|
|
334
|
+
}
|
|
335
|
+
})
|
|
336
|
+
```
|
|
337
|
+
|
|
338
|
+
## Creating Custom Themes
|
|
339
|
+
|
|
340
|
+
### Basic Theme
|
|
341
|
+
|
|
342
|
+
**Create your own theme**
|
|
343
|
+
|
|
344
|
+
```typescript
|
|
345
|
+
import { definePreset, MazUiTheme, type ThemePresetOverrides, ocean } from '@maz-ui/themes'
|
|
346
|
+
|
|
347
|
+
const customTheme = await definePreset({
|
|
348
|
+
base: 'ocean',
|
|
349
|
+
overrides: {
|
|
350
|
+
name: 'custom-purple',
|
|
351
|
+
foundation: {
|
|
352
|
+
'font-family': 'Inter, sans-serif'
|
|
353
|
+
},
|
|
354
|
+
scales: {
|
|
355
|
+
rounded: { md: '1rem' }
|
|
356
|
+
},
|
|
357
|
+
colors: {
|
|
358
|
+
light: {
|
|
359
|
+
primary: 'oklch(0.62 0.24 305)',
|
|
360
|
+
secondary: 'oklch(0.94 0.04 320)',
|
|
361
|
+
accent: 'oklch(0.7 0.22 290)'
|
|
362
|
+
},
|
|
363
|
+
dark: {
|
|
364
|
+
primary: 'oklch(0.72 0.22 305)',
|
|
365
|
+
secondary: 'oklch(0.28 0.06 320)',
|
|
366
|
+
accent: 'oklch(0.78 0.2 290)'
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
},
|
|
370
|
+
})
|
|
371
|
+
/**
|
|
372
|
+
* or with preset object
|
|
373
|
+
* The function is synchronous when using a preset object
|
|
374
|
+
*/
|
|
375
|
+
const customTheme = definePreset({
|
|
376
|
+
base: ocean,
|
|
377
|
+
overrides,
|
|
378
|
+
})
|
|
379
|
+
|
|
380
|
+
// Usage
|
|
381
|
+
|
|
382
|
+
// with plugin
|
|
383
|
+
app.use(MazUiTheme, {
|
|
384
|
+
preset: customTheme,
|
|
385
|
+
})
|
|
386
|
+
|
|
387
|
+
// or with composable
|
|
388
|
+
const { updateTheme } = useTheme()
|
|
389
|
+
updateTheme(customTheme)
|
|
390
|
+
```
|
|
391
|
+
|
|
392
|
+
### Advanced Theme with Overrides
|
|
393
|
+
|
|
394
|
+
```typescript
|
|
395
|
+
import { definePreset } from '@maz-ui/themes'
|
|
396
|
+
|
|
397
|
+
const brandTheme = await definePreset({
|
|
398
|
+
base: 'maz-ui',
|
|
399
|
+
overrides: {
|
|
400
|
+
name: 'brand',
|
|
401
|
+
foundation: {
|
|
402
|
+
'border-width': '2px',
|
|
403
|
+
'font-family': 'Inter, sans-serif'
|
|
404
|
+
},
|
|
405
|
+
scales: {
|
|
406
|
+
rounded: { md: '0.75rem' }
|
|
407
|
+
},
|
|
408
|
+
colors: {
|
|
409
|
+
light: {
|
|
410
|
+
primary: 'oklch(0.6 0.18 254)',
|
|
411
|
+
secondary: 'oklch(0.96 0.01 254)',
|
|
412
|
+
surface: 'oklch(0.99 0.005 254)',
|
|
413
|
+
accent: 'oklch(0.7 0.22 305)'
|
|
414
|
+
},
|
|
415
|
+
dark: {
|
|
416
|
+
primary: 'oklch(0.7 0.18 254)',
|
|
417
|
+
secondary: 'oklch(0.22 0.03 254)',
|
|
418
|
+
surface: 'oklch(0.18 0.02 254)',
|
|
419
|
+
accent: 'oklch(0.78 0.2 305)'
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
})
|
|
424
|
+
```
|
|
425
|
+
|
|
426
|
+
### Customizing scales
|
|
427
|
+
|
|
428
|
+
Each `ThemeScales` entry maps to a Tailwind utility - override the keys you care about and the rest stays untouched:
|
|
429
|
+
|
|
430
|
+
```typescript
|
|
431
|
+
const denseTheme = definePreset({
|
|
432
|
+
base: 'maz-ui',
|
|
433
|
+
overrides: {
|
|
434
|
+
name: 'dense',
|
|
435
|
+
foundation: {
|
|
436
|
+
// Tighter spacing - every p-N / gap-N / m-N rescales
|
|
437
|
+
space: '0.2rem',
|
|
438
|
+
},
|
|
439
|
+
scales: {
|
|
440
|
+
// Sharper corners on the whole radius scale
|
|
441
|
+
rounded: {
|
|
442
|
+
'xs': '0.0625rem',
|
|
443
|
+
'sm': '0.125rem',
|
|
444
|
+
'md': '0.375rem',
|
|
445
|
+
'lg': '0.5rem',
|
|
446
|
+
'xl': '0.75rem',
|
|
447
|
+
'2xl': '1rem',
|
|
448
|
+
'3xl': '1.5rem',
|
|
449
|
+
},
|
|
450
|
+
// Custom elevated-surface shadow
|
|
451
|
+
shadow: {
|
|
452
|
+
elevation: '0 8px 24px rgba(0, 0, 0, 0.15)',
|
|
453
|
+
},
|
|
454
|
+
},
|
|
455
|
+
},
|
|
456
|
+
})
|
|
457
|
+
```
|
|
458
|
+
|
|
459
|
+
Foundation gets new optional keys for fonts and disabled-state behaviour:
|
|
460
|
+
|
|
461
|
+
```typescript
|
|
462
|
+
const codeTheme = definePreset({
|
|
463
|
+
base: 'maz-ui',
|
|
464
|
+
overrides: {
|
|
465
|
+
foundation: {
|
|
466
|
+
'font-mono-stack': '"JetBrains Mono", ui-monospace, SFMono-Regular, monospace',
|
|
467
|
+
'font-display-stack': '"Cal Sans", Manrope, sans-serif',
|
|
468
|
+
'disabled-opacity': '0.4',
|
|
469
|
+
'disabled-cursor': 'wait',
|
|
470
|
+
},
|
|
471
|
+
},
|
|
472
|
+
})
|
|
473
|
+
```
|
|
474
|
+
|
|
475
|
+
### Per-component overrides
|
|
476
|
+
|
|
477
|
+
`components` is a small, validated set of per-component knobs. Every entry is per-mode where appropriate so light and dark can drift independently:
|
|
478
|
+
|
|
479
|
+
```typescript
|
|
480
|
+
const surfaceTheme = definePreset({
|
|
481
|
+
base: 'maz-ui',
|
|
482
|
+
overrides: {
|
|
483
|
+
name: 'cool-surfaces',
|
|
484
|
+
components: {
|
|
485
|
+
btn: {
|
|
486
|
+
// Bias every button text heavier
|
|
487
|
+
'font-weight': '600',
|
|
488
|
+
},
|
|
489
|
+
container: {
|
|
490
|
+
// Cards / dialogs / popovers / drawers - light = page surface, dark = a tier above
|
|
491
|
+
bg: { light: 'var(--maz-surface)', dark: 'var(--maz-surface-300)' },
|
|
492
|
+
},
|
|
493
|
+
input: {
|
|
494
|
+
// All form-control surfaces
|
|
495
|
+
bg: { light: 'var(--maz-surface-100)', dark: 'var(--maz-surface-400)' },
|
|
496
|
+
},
|
|
497
|
+
},
|
|
498
|
+
},
|
|
499
|
+
})
|
|
500
|
+
```
|
|
501
|
+
|
|
502
|
+
Components consume these via `bg-container` / `bg-input` Tailwind utilities, so a single override propagates everywhere - no per-component class hunt.
|
|
503
|
+
|
|
504
|
+
### Dialog sizing
|
|
505
|
+
|
|
506
|
+
`components.dialog` controls the width of [`MazDialog`](../components/maz-dialog.md) on tablet and up. The two keys are emitted as `--maz-dialog-max-width` / `--maz-dialog-min-width` and consumed by the component through `var(--maz-dialog-max-width, 38rem)` / `var(--maz-dialog-min-width, 32rem)`:
|
|
507
|
+
|
|
508
|
+
```typescript
|
|
509
|
+
const wideDialogTheme = definePreset({
|
|
510
|
+
base: 'maz-ui',
|
|
511
|
+
overrides: {
|
|
512
|
+
name: 'wide-dialogs',
|
|
513
|
+
components: {
|
|
514
|
+
dialog: {
|
|
515
|
+
'max-width': '48rem',
|
|
516
|
+
'min-width': '36rem',
|
|
517
|
+
},
|
|
518
|
+
},
|
|
519
|
+
},
|
|
520
|
+
})
|
|
521
|
+
```
|
|
522
|
+
|
|
523
|
+
Both values are in `rem`, so they scale with the preset's `base-font-size`. Bundled presets anchored on `14px` use `38rem` / `32rem`; `ocean` (`16px`) ships scaled rem values (`33.25rem` / `28rem`) to keep the same rendered pixel width. The `max-width` / `min-width` props on `MazDialog` still override the preset per instance.
|
|
524
|
+
|
|
525
|
+
## useTheme Composable API
|
|
526
|
+
|
|
527
|
+
```typescript
|
|
528
|
+
const {
|
|
529
|
+
// Reactive state
|
|
530
|
+
preset, // ComputedRef<ThemePreset>
|
|
531
|
+
presetName, // ComputedRef<string>
|
|
532
|
+
colorMode, // Ref<'light' | 'dark' | 'auto'> - also accepts assignment (computed setter)
|
|
533
|
+
isDark, // ComputedRef<boolean>
|
|
534
|
+
strategy, // ComputedRef<'runtime' | 'buildtime'>
|
|
535
|
+
mode, // ComputedRef<'light' | 'dark' | 'both'>
|
|
536
|
+
darkModeStrategy, // ComputedRef<'class' | 'media'>
|
|
537
|
+
|
|
538
|
+
// Actions
|
|
539
|
+
updateTheme, // (preset: ThemePreset | ThemePresetName | ThemePresetOverrides) => Promise<void>
|
|
540
|
+
setColorMode, // (mode: 'light' | 'dark' | 'auto', options?: { animate?: boolean }) => Promise<void>
|
|
541
|
+
toggleDarkMode, // (options?: { animate?: boolean }) => Promise<void>
|
|
542
|
+
} = useTheme()
|
|
543
|
+
|
|
544
|
+
// Set the color mode imperatively
|
|
545
|
+
await setColorMode('dark')
|
|
546
|
+
|
|
547
|
+
// Or assign to `colorMode` - it's a computed with a setter
|
|
548
|
+
colorMode.value = 'dark'
|
|
549
|
+
|
|
550
|
+
// Animated full-page switch via View Transitions
|
|
551
|
+
await toggleDarkMode({ animate: true })
|
|
552
|
+
```
|
|
553
|
+
|
|
554
|
+
`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.
|
|
555
|
+
|
|
556
|
+
### Advanced Usage Examples
|
|
557
|
+
|
|
558
|
+
```vue
|
|
559
|
+
<script setup>
|
|
560
|
+
import { useTheme } from '@maz-ui/themes'
|
|
561
|
+
import { computed } from 'vue'
|
|
562
|
+
|
|
563
|
+
const { presetName, isDark, updateTheme, setColorMode } = useTheme()
|
|
564
|
+
|
|
565
|
+
// Computed for interface
|
|
566
|
+
const themeIcon = computed(() => isDark.value ? '☀️' : '🌙')
|
|
567
|
+
|
|
568
|
+
// Function to apply a temporary custom theme
|
|
569
|
+
function previewColor(color: string) {
|
|
570
|
+
updateTheme({
|
|
571
|
+
colors: {
|
|
572
|
+
light: { primary: color },
|
|
573
|
+
dark: { primary: color }
|
|
574
|
+
}
|
|
575
|
+
})
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
// Auto mode with system detection
|
|
579
|
+
function enableAutoMode() {
|
|
580
|
+
setColorMode('auto')
|
|
581
|
+
}
|
|
582
|
+
</script>
|
|
583
|
+
```
|
|
584
|
+
|
|
585
|
+
## Build-time Strategy: Complete Guide
|
|
586
|
+
|
|
587
|
+
The build-time strategy allows you to generate your theme CSS files at compile time, offering the best performance in production.
|
|
588
|
+
|
|
589
|
+
### Step-by-step configuration
|
|
590
|
+
|
|
591
|
+
#### 1. Generate CSS
|
|
592
|
+
|
|
593
|
+
```typescript [Vue]
|
|
594
|
+
import { mkdirSync, writeFileSync } from 'node:fs'
|
|
595
|
+
import { join } from 'node:path'
|
|
596
|
+
import {
|
|
597
|
+
// Build all css in one file
|
|
598
|
+
buildThemeCSS,
|
|
599
|
+
// Build multiple themes with one file each
|
|
600
|
+
generateThemeBundle,
|
|
601
|
+
// Build separate css files
|
|
602
|
+
buildSeparateThemeFiles,
|
|
603
|
+
definePreset,
|
|
604
|
+
mazUi,
|
|
605
|
+
} from '@maz-ui/themes'
|
|
606
|
+
|
|
607
|
+
import App from './App.vue'
|
|
608
|
+
|
|
609
|
+
const _dirname = dirname(fileURLToPath(import.meta.url))
|
|
610
|
+
|
|
611
|
+
// Custom theme
|
|
612
|
+
const customPreset = definePreset({
|
|
613
|
+
base: mazUi,
|
|
614
|
+
overrides: {
|
|
615
|
+
name: 'custom',
|
|
616
|
+
colors: {
|
|
617
|
+
light: { primary: 'oklch(0.6 0.18 254)' },
|
|
618
|
+
dark: { primary: 'oklch(0.7 0.18 254)' },
|
|
619
|
+
},
|
|
620
|
+
},
|
|
621
|
+
})
|
|
622
|
+
|
|
623
|
+
/**
|
|
624
|
+
* Generate complete CSS
|
|
625
|
+
*/
|
|
626
|
+
const fullCSS = buildThemeCSS({
|
|
627
|
+
preset: customPreset,
|
|
628
|
+
mode: 'both',
|
|
629
|
+
})
|
|
630
|
+
|
|
631
|
+
writeFileSync(join(_dirname, 'public/custom.css'), fullCSS)
|
|
632
|
+
|
|
633
|
+
/**
|
|
634
|
+
* Or generate per-mode CSS files
|
|
635
|
+
*/
|
|
636
|
+
const { full, lightOnly, darkOnly } = buildSeparateThemeFiles(customPreset, {
|
|
637
|
+
darkSelector: 'class',
|
|
638
|
+
})
|
|
639
|
+
|
|
640
|
+
writeFileSync(join(_dirname, 'public/custom-full.css'), full)
|
|
641
|
+
|
|
642
|
+
/**
|
|
643
|
+
* Or generate bundle for multiple themes in one file each
|
|
644
|
+
*/
|
|
645
|
+
const themeBundle = generateThemeBundle([customPreset, mazUi], {
|
|
646
|
+
mode: 'both',
|
|
647
|
+
})
|
|
648
|
+
|
|
649
|
+
Object.entries(themeBundle).forEach(([name, css]) => {
|
|
650
|
+
writeFileSync(join(_dirname, `public/${name}.css`), css)
|
|
651
|
+
})
|
|
652
|
+
|
|
653
|
+
const app = createApp(App)
|
|
654
|
+
|
|
655
|
+
app.use(MazUiTheme, {
|
|
656
|
+
preset: customPreset,
|
|
657
|
+
strategy: 'buildtime', // Important!
|
|
658
|
+
darkModeStrategy: 'class',
|
|
659
|
+
})
|
|
660
|
+
|
|
661
|
+
app.mount('#app')
|
|
662
|
+
```
|
|
663
|
+
|
|
664
|
+
```typescript [Vitepress]
|
|
665
|
+
import {
|
|
666
|
+
buildThemeCSS,
|
|
667
|
+
generateThemeBundle,
|
|
668
|
+
buildSeparateThemeFiles,
|
|
669
|
+
createThemeStylesheet,
|
|
670
|
+
definePreset,
|
|
671
|
+
mazUi,
|
|
672
|
+
CSS_ID,
|
|
673
|
+
} from '@maz-ui/themes'
|
|
674
|
+
|
|
675
|
+
// Custom theme
|
|
676
|
+
const customPreset = definePreset({
|
|
677
|
+
base: mazUi,
|
|
678
|
+
overrides: {
|
|
679
|
+
name: 'custom',
|
|
680
|
+
colors: {
|
|
681
|
+
light: { primary: 'oklch(0.6 0.18 254)' },
|
|
682
|
+
dark: { primary: 'oklch(0.7 0.18 254)' },
|
|
683
|
+
},
|
|
684
|
+
},
|
|
685
|
+
})
|
|
686
|
+
|
|
687
|
+
// Generate complete CSS
|
|
688
|
+
const fullCSS = buildThemeCSS({
|
|
689
|
+
preset: customPreset,
|
|
690
|
+
mode: 'both',
|
|
691
|
+
})
|
|
692
|
+
|
|
693
|
+
// Or generate per-mode CSS files
|
|
694
|
+
const { full, lightOnly, darkOnly } = buildSeparateThemeFiles(customPreset, {
|
|
695
|
+
darkSelector: 'class',
|
|
696
|
+
})
|
|
697
|
+
|
|
698
|
+
// Or generate bundle for multiple themes
|
|
699
|
+
const themeBundle = generateThemeBundle([customPreset, mazUi], {
|
|
700
|
+
mode: 'both',
|
|
701
|
+
})
|
|
702
|
+
|
|
703
|
+
export default defineConfig<DefaultTheme.Config>({
|
|
704
|
+
head: [
|
|
705
|
+
['style', { id: CSS_ID, type: 'text/css' }, full],
|
|
706
|
+
] satisfies HeadConfig[],
|
|
707
|
+
})
|
|
708
|
+
```
|
|
709
|
+
|
|
710
|
+
```typescript [Nuxt]
|
|
711
|
+
/*
|
|
712
|
+
* You dont need to do anything,
|
|
713
|
+
* The module will handle it for you.
|
|
714
|
+
*
|
|
715
|
+
* Just provide a preset to the module
|
|
716
|
+
*/
|
|
717
|
+
|
|
718
|
+
const customPreset = definePreset({
|
|
719
|
+
base: mazUi,
|
|
720
|
+
overrides: {
|
|
721
|
+
name: 'custom',
|
|
722
|
+
colors: {
|
|
723
|
+
light: { primary: 'oklch(0.6 0.18 254)' },
|
|
724
|
+
dark: { primary: 'oklch(0.7 0.18 254)' },
|
|
725
|
+
},
|
|
726
|
+
},
|
|
727
|
+
})
|
|
728
|
+
|
|
729
|
+
export default defineConfig({
|
|
730
|
+
modules: ['@maz-ui/nuxt'],
|
|
731
|
+
mazUi: {
|
|
732
|
+
theme: {
|
|
733
|
+
preset: customPreset,
|
|
734
|
+
mode: 'both',
|
|
735
|
+
colorMode: 'auto',
|
|
736
|
+
},
|
|
737
|
+
},
|
|
738
|
+
})
|
|
739
|
+
```
|
|
740
|
+
|
|
741
|
+
#### 2. Include in your HTML (Only for Vue users)
|
|
742
|
+
|
|
743
|
+
```html
|
|
744
|
+
<link rel="stylesheet" href="/themes/custom-full.css">
|
|
745
|
+
```
|
|
746
|
+
|
|
747
|
+
### Utility functions
|
|
748
|
+
|
|
749
|
+
#### `buildThemeCSS(options)`
|
|
750
|
+
|
|
751
|
+
Generates complete CSS for a theme.
|
|
752
|
+
|
|
753
|
+
```typescript
|
|
754
|
+
const css = buildThemeCSS({
|
|
755
|
+
preset: customPreset,
|
|
756
|
+
mode: 'both', // 'light' | 'dark' | 'both'
|
|
757
|
+
darkSelector: 'class', // 'class' | 'media'
|
|
758
|
+
})
|
|
759
|
+
```
|
|
760
|
+
|
|
761
|
+
#### `generateThemeBundle(presets, options)`
|
|
762
|
+
|
|
763
|
+
Generates a bundle containing multiple themes.
|
|
764
|
+
|
|
765
|
+
```typescript
|
|
766
|
+
const bundle = generateThemeBundle([theme1, theme2], {
|
|
767
|
+
mode: 'both',
|
|
768
|
+
darkSelector: 'class',
|
|
769
|
+
})
|
|
770
|
+
// Result: { 'theme1': 'css...', 'theme2': 'css...' }
|
|
771
|
+
```
|
|
772
|
+
|
|
773
|
+
#### `buildSeparateThemeFiles(preset, options)`
|
|
774
|
+
|
|
775
|
+
Generates separate files for different use cases.
|
|
776
|
+
|
|
777
|
+
```typescript
|
|
778
|
+
const files = buildSeparateThemeFiles(preset)
|
|
779
|
+
// Result: { full, lightOnly, darkOnly }
|
|
780
|
+
```
|
|
781
|
+
|
|
782
|
+
#### `createThemeStylesheet(css, options)`
|
|
783
|
+
|
|
784
|
+
Creates a `
|
|
785
|
+
@reference "../../.vitepress/theme/main.css";
|
|
786
|
+
` tag with the provided CSS.
|
|
787
|
+
|
|
788
|
+
```typescript
|
|
789
|
+
const styleTag = createThemeStylesheet(css, {
|
|
790
|
+
id: 'my-theme',
|
|
791
|
+
media: 'screen', // optional
|
|
792
|
+
})
|
|
793
|
+
```
|
|
794
|
+
|
|
795
|
+
### Build-time strategy advantages
|
|
796
|
+
|
|
797
|
+
- **Optimal performance** - No client-side CSS generation
|
|
798
|
+
- **Efficient caching** - CSS files cached by CDN
|
|
799
|
+
- **Reduced bundle** - Generation utilities excluded from client
|
|
800
|
+
- **Full compatibility** - Works even with JavaScript disabled
|
|
801
|
+
|
|
802
|
+
### Recommended use cases
|
|
803
|
+
|
|
804
|
+
- Production applications with static themes
|
|
805
|
+
- Sites with critical performance requirements
|
|
806
|
+
- Projects with multiple predefined themes
|
|
807
|
+
- Applications requiring fine control over CSS loading
|
|
808
|
+
|
|
809
|
+
## Token Reference
|
|
810
|
+
|
|
811
|
+
Every preset emits the same set of CSS variables on `:root`. With `mode: 'both'`, each base color is a single `light-dark(L, D)` declaration that the browser resolves to the active scheme. Use these names directly in your own CSS - `var(--maz-primary)`, `calc(var(--maz-space) * 4)`, etc.
|
|
812
|
+
|
|
813
|
+
### Foundation
|
|
814
|
+
|
|
815
|
+
Single-value design tokens. Set via `foundation.<key>` on the preset.
|
|
816
|
+
|
|
817
|
+
| Preset key | CSS variable | Default | Notes |
|
|
818
|
+
| --- | --- | --- | --- |
|
|
819
|
+
| `base-font-size` | `--maz-base-font-size` | `14px` | Anchors `rem` for the whole app. Apply on `html { font-size: var(--maz-base-font-size) }`. |
|
|
820
|
+
| `border-width` | `--maz-border-width` | `1px` | Default border for components and the global `*::before/::after` reset. |
|
|
821
|
+
| `space` | `--maz-space` | `0.25rem` | Base step for every Tailwind spacing utility (`p-1`, `gap-2`, …). |
|
|
822
|
+
| `font-family` | `--maz-font-family` | preset-specific | Sans / body font stack. Bridged to Tailwind `--font-sans`. |
|
|
823
|
+
| `font-mono-stack` | `--maz-font-mono-stack` | `ui-monospace, …` | Monospace stack - `MazInputCode`, `<code>`, `<kbd>`. Bridged to `--font-mono`. |
|
|
824
|
+
| `font-display-stack` | `--maz-font-display-stack` | same as `font-family` | Display / heading stack. Bridged to `--font-display`. |
|
|
825
|
+
| `motion-fast` | `--maz-motion-fast` | `100ms` | Bridged to Tailwind `--duration-fast`. |
|
|
826
|
+
| `motion-normal` | `--maz-motion-normal` | `200ms` | Default transition duration (`--default-transition-duration`). |
|
|
827
|
+
| `motion-slow` | `--maz-motion-slow` | `300ms` | Bridged to `--duration-slow`. |
|
|
828
|
+
| `easing-out` | `--maz-easing-out` | `cubic-bezier(0.4, 0, 0.2, 1)` | Default easing (`--ease-out`, also `--default-transition-timing-function`). |
|
|
829
|
+
| `easing-in` | `--maz-easing-in` | `cubic-bezier(0.4, 0, 1, 1)` | Bridged to `--ease-in`. |
|
|
830
|
+
| `easing-in-out` | `--maz-easing-in-out` | `cubic-bezier(0.4, 0, 0.2, 1)` | Bridged to `--ease-in-out`. |
|
|
831
|
+
| `disabled-opacity` | `--maz-disabled-opacity` | `0.5` | Applied to disabled buttons / inputs. |
|
|
832
|
+
| `disabled-cursor` | `--maz-disabled-cursor` | `not-allowed` | Cursor for disabled interactive elements. |
|
|
833
|
+
|
|
834
|
+
> **Why `font-mono-stack` / `font-display-stack` / `motion-*`?** Under Tailwind v4 `prefix(maz)`, the bridge LHS gets prefixed (`--font-mono` → `--maz-font-mono`), so a same-named source token would self-cycle. The `-stack` and `motion-` suffixes break the collision.
|
|
835
|
+
|
|
836
|
+
### Colors
|
|
837
|
+
|
|
838
|
+
Defined per mode under `colors.light` / `colors.dark`. Each base color also gets an automatic 50-950 scale.
|
|
839
|
+
|
|
840
|
+
| Preset key | CSS variable (base) | Auto scale | Notes |
|
|
841
|
+
| --- | --- | --- | --- |
|
|
842
|
+
| `surface` | `--maz-surface` | `--maz-surface-50..900` | Page / container background. |
|
|
843
|
+
| `foreground` | `--maz-foreground` | `--maz-foreground-50..900` | Default text color. |
|
|
844
|
+
| `primary` | `--maz-primary` | `--maz-primary-50..900` | Brand color. |
|
|
845
|
+
| `primary-foreground` | `--maz-primary-foreground` | - | Text on `bg-primary`. |
|
|
846
|
+
| `secondary` | `--maz-secondary` | `--maz-secondary-50..900` | |
|
|
847
|
+
| `secondary-foreground` | `--maz-secondary-foreground` | - | |
|
|
848
|
+
| `accent` | `--maz-accent` | `--maz-accent-50..900` | |
|
|
849
|
+
| `accent-foreground` | `--maz-accent-foreground` | - | |
|
|
850
|
+
| `info` | `--maz-info` | `--maz-info-50..900` | Status - informational. |
|
|
851
|
+
| `info-foreground` | `--maz-info-foreground` | - | |
|
|
852
|
+
| `success` | `--maz-success` | `--maz-success-50..900` | Status - success. |
|
|
853
|
+
| `success-foreground` | `--maz-success-foreground` | - | |
|
|
854
|
+
| `warning` | `--maz-warning` | `--maz-warning-50..900` | Status - warning. |
|
|
855
|
+
| `warning-foreground` | `--maz-warning-foreground` | - | |
|
|
856
|
+
| `destructive` | `--maz-destructive` | `--maz-destructive-50..900` | Status - error / danger. |
|
|
857
|
+
| `destructive-foreground` | `--maz-destructive-foreground` | - | |
|
|
858
|
+
| `contrast` | `--maz-contrast` | `--maz-contrast-50..900` | High-contrast accent (light = near-black, dark = near-white). |
|
|
859
|
+
| `contrast-foreground` | `--maz-contrast-foreground` | - | |
|
|
860
|
+
| `divider` | `--maz-divider` | `--maz-divider-50..900` | Default border / separator color. |
|
|
861
|
+
| `muted` | `--maz-muted` | `--maz-muted-50..900` | Muted / secondary text. |
|
|
862
|
+
| `overlay` | `--maz-overlay` | `--maz-overlay-50..900` | Backdrop / dialog scrim. |
|
|
863
|
+
| `shadow` | `--maz-shadow` | `--maz-shadow-50..900` | Tint used by Tailwind `shadow-*` utilities (via `--color-elevation`). |
|
|
864
|
+
|
|
865
|
+
### Scales
|
|
866
|
+
|
|
867
|
+
Multi-step scales under `scales.<key>`.
|
|
868
|
+
|
|
869
|
+
| Preset key | CSS variables | Tailwind utility |
|
|
870
|
+
| --- | --- | --- |
|
|
871
|
+
| `rounded.xs..3xl` | `--maz-rounded-xs..3xl` | `rounded-{key}` (bridged to `--radius-{key}`) |
|
|
872
|
+
| `shadow.{sm, md, lg, xl, elevation}` | `--maz-shadow-style-{key}` | `shadow-{key}` (bridged to `--shadow-{key}`) |
|
|
873
|
+
|
|
874
|
+
> Same naming-collision avoidance as foundation: `rounded.*` (instead of `radius.*`) and `shadow-style-*` (instead of plain `shadow-*`) keep the bridge cycle-free under `prefix(maz)`.
|
|
875
|
+
|
|
876
|
+
### Components
|
|
877
|
+
|
|
878
|
+
Per-component knobs under `components.<key>`. All optional - omit to fall back to the surface tokens.
|
|
879
|
+
|
|
880
|
+
| Preset key | CSS variable | Notes |
|
|
881
|
+
| --- | --- | --- |
|
|
882
|
+
| `btn.font-weight` | `--maz-btn-font-weight` | Font-weight on `.m-btn`. Defaults to `'500'`. |
|
|
883
|
+
| `container.bg.light` / `.dark` | `--maz-container-bg` (per mode) | Background of `Card`, `Container`, `Dialog`, `Popover`, `Drawer`, … Defaults to `var(--maz-surface)`. Bridged to `--color-container`. |
|
|
884
|
+
| `input.bg.light` / `.dark` | `--maz-input-bg` (per mode) | Background of `Input`, `Textarea`, `Select`, `Checkbox`, … Defaults to `var(--maz-surface)` light, `var(--maz-surface-400)` dark. Bridged to `--color-input`. |
|
|
885
|
+
| `dialog.max-width` | `--maz-dialog-max-width` | Max-width of `MazDialog` on tablet and up. Defaults to `38rem` (`33.25rem` on `ocean`). Overridable via the `max-width` prop. |
|
|
886
|
+
| `dialog.min-width` | `--maz-dialog-min-width` | Min-width of `MazDialog` on tablet and up. Defaults to `32rem` (`28rem` on `ocean`). Overridable via the `min-width` prop. |
|
|
887
|
+
|
|
888
|
+
### Sample output
|
|
889
|
+
|
|
890
|
+
Each base color is emitted as a `light-dark()` value; scales `--maz-X-50` through `--maz-X-950` are derived via OKLCh relative color syntax so they automatically follow base color overrides at runtime, preserving chroma at every step.
|
|
891
|
+
|
|
892
|
+
```css
|
|
893
|
+
:root {
|
|
894
|
+
color-scheme: light dark;
|
|
895
|
+
|
|
896
|
+
/* Base colors - one declaration each, resolved by the browser */
|
|
897
|
+
--maz-primary: light-dark(oklch(0.65 0.19 254), oklch(0.72 0.18 254));
|
|
898
|
+
--maz-primary-foreground: light-dark(oklch(1 0 0), oklch(0.16 0.01 254));
|
|
899
|
+
--maz-surface: light-dark(oklch(1 0 0), oklch(0.18 0.01 254));
|
|
900
|
+
--maz-foreground: light-dark(oklch(0.26 0.01 254), oklch(0.96 0.005 254));
|
|
901
|
+
|
|
902
|
+
/* Auto-generated 50-950 scale - derived via relative color syntax */
|
|
903
|
+
--maz-primary-500: var(--maz-primary);
|
|
904
|
+
--maz-primary-600: oklch(from var(--maz-primary) clamp(0, calc(l - 0.07), 1) c h);
|
|
905
|
+
/* ... */
|
|
906
|
+
|
|
907
|
+
/* Foundation */
|
|
908
|
+
--maz-base-font-size: 14px;
|
|
909
|
+
--maz-border-width: 1px;
|
|
910
|
+
--maz-space: 0.25rem;
|
|
911
|
+
--maz-font-family: Manrope, sans-serif, …;
|
|
912
|
+
--maz-motion-normal: 200ms;
|
|
913
|
+
|
|
914
|
+
/* Scales */
|
|
915
|
+
--maz-rounded-md: 0.7rem;
|
|
916
|
+
--maz-shadow-style-md: 0 4px 6px -1px rgb(0 0 0 / 0.1), …;
|
|
917
|
+
}
|
|
918
|
+
|
|
919
|
+
/* With darkModeStrategy: 'class' - explicit user choice overrides the system */
|
|
920
|
+
.dark { color-scheme: only dark; }
|
|
921
|
+
.light { color-scheme: only light; }
|
|
922
|
+
```
|
|
923
|
+
|
|
924
|
+
For deeper detail on the generated CSS contract, see the [`@maz-ui/themes` README](https://github.com/LouisMazel/maz-ui/tree/master/packages/themes#generated-css-variables).
|
|
925
|
+
|
|
926
|
+
## Usage with Nuxt
|
|
927
|
+
|
|
928
|
+
For Nuxt users, check the [dedicated Nuxt documentation](./nuxt.md) which covers installation and framework-specific configuration.
|
|
929
|
+
|
|
930
|
+
## Migration from Legacy System
|
|
931
|
+
|
|
932
|
+
If you're using the legacy theme system with CLI:
|
|
933
|
+
|
|
934
|
+
```typescript [Before (CLI)]
|
|
935
|
+
// maz-ui.config.ts
|
|
936
|
+
export default defineConfig({
|
|
937
|
+
theme: {
|
|
938
|
+
colors: {
|
|
939
|
+
primary: 'hsl(210, 100%, 56%)',
|
|
940
|
+
secondary: 'hsl(164, 76%, 46%)'
|
|
941
|
+
}
|
|
942
|
+
}
|
|
943
|
+
})
|
|
944
|
+
```
|
|
945
|
+
|
|
946
|
+
```typescript [After (@maz-ui/themes)]
|
|
947
|
+
// main.ts
|
|
948
|
+
import { definePreset, mazUi } from '@maz-ui/themes'
|
|
949
|
+
import { MazUiTheme } from '@maz-ui/themes/plugin'
|
|
950
|
+
|
|
951
|
+
const myTheme = definePreset({
|
|
952
|
+
base: mazUi,
|
|
953
|
+
overrides: {
|
|
954
|
+
colors: {
|
|
955
|
+
light: {
|
|
956
|
+
primary: 'oklch(0.65 0.19 254)',
|
|
957
|
+
secondary: 'oklch(0.72 0.16 168)'
|
|
958
|
+
}
|
|
959
|
+
}
|
|
960
|
+
}
|
|
961
|
+
})
|
|
962
|
+
|
|
963
|
+
app.use(MazUiTheme, { preset: myTheme })
|
|
964
|
+
```
|
|
965
|
+
|
|
966
|
+
The new system offers much more flexibility and performance!
|