Theme โ
mono-helper has two independent axes you can mix freely:
| Axis | What it controls | Default | Options |
|---|---|---|---|
| Theme | Structure โ radius, typography (font), elevation, component shapes | Mono | Mono, Material |
| Theme color | The color palette (brand + status colors) | Navy & Sky Blue | Navy & Sky, Navy Teal, Royal Blue + Gold, Biru Cerah, Material Blue |
They are orthogonal: e.g. Material structure with a Navy Teal palette, or Mono structure with the Material Blue palette. Components don't change โ once a theme/color is active, every mono-* component picks it up automatically through CSS variables.
Stylesheets โ
ts
// Required โ base styles + the default (Mono) theme + every color preset.
import 'mono-helper/index.css'
// Optional โ the Material structure. Import this ONLY if you use Material.
import 'mono-helper/ui/theme/mui.css'index.css already contains all the color presets, so you never need a second import for colors โ only the Material structure lives in its own opt-in file.
How activation works โ
Both axes are plain CSS classes on a root element (<html>, <body>, or any wrapper). Add a class โ that theme/color applies to everything inside. Remove it โ back to the default.
| Default (no class) | Activate with class | |
|---|---|---|
| Theme | Mono | theme-material |
| Theme color | Navy & Sky | theme-color-navy-sky ยท theme-color-navy-teal ยท theme-color-blue-gold ยท theme-color-light-blue ยท theme-color-material |
Color preset โ name reference:
| Class | Palette |
|---|---|
(none) / theme-color-navy-sky | Navy & Sky Blue (default) |
theme-color-navy-teal | Navy Teal |
theme-color-blue-gold | Royal Blue + Gold |
theme-color-light-blue | Biru Cerah |
theme-color-material | Material Blue (MUI) |
Manual (CSS classes) โ
No JavaScript needed โ just import the CSS and put the classes on a root element.
Default (Mono + Navy & Sky): nothing to do beyond importing index.css.
html
<html>
<!-- everything is Mono + Navy & Sky -->
</html>Material theme (remember to import mui.css):
html
<html class="theme-material">
<!-- Material structure, default palette -->
</html>A different color palette:
html
<html class="theme-color-blue-gold">
<!-- Mono structure, Royal Blue + Gold palette -->
</html>Material + a custom palette (mix both axes):
html
<html class="theme-material theme-color-navy-teal">
<!-- Material structure, Navy Teal palette -->
</html>Scoped theming โ because it's class-based, you can theme just a part of the page by putting the class on a wrapper instead of <html>:
html
<div class="theme-material theme-color-material">
<!-- only this subtree is Material + Material Blue -->
<mono-button>Save</mono-button>
</div>Automatic (Vue app) โ
mono-helper exports a small theming API so you can switch at runtime (settings page, dark-mode-style toggle, etc.). It applies the right classes for you.
Naming note: for backwards-compatibility the API calls the color axis
theme(applyTheme,themes,ThemeName) and the structure axisflavor(applyFlavor,flavors,FlavorName). The combinedapplyTheme({ color, flavor })is the clearest entry point โcolor= palette,flavor= Mono/Material.
1. Set a theme on app start โ
ts
// main.ts
import { createApp } from 'vue'
import App from './App.vue'
import 'mono-helper/index.css'
import 'mono-helper/ui/theme/mui.css' // only if you use Material
import { applyTheme } from 'mono-helper'
// color = palette, flavor = Mono | Material
applyTheme({ color: 'navy-teal', flavor: 'material' })
createApp(App).mount('#app')Shortcuts:
ts
import { applyTheme, applyFlavor } from 'mono-helper'
applyTheme('blue-gold') // change only the color palette
applyFlavor('material') // change only Mono โ Material
applyFlavor('mono') // back to the default structure2. A settings toggle with <mono-select> โ
themes / flavors give you the option lists (with display names), and getCurrentTheme / getCurrentFlavor read the active values.
vue
<script setup lang="ts">
import { ref } from 'vue'
import 'mono-helper/ui/select'
import {
applyTheme, applyFlavor,
themes, flavors,
getCurrentTheme, getCurrentFlavor,
type ThemeName, type FlavorName,
} from 'mono-helper'
const colorOptions = Object.values(themes) // [{ name, displayName }, โฆ]
const themeOptions = Object.values(flavors)
const color = ref<ThemeName>(getCurrentTheme())
const theme = ref<FlavorName>(getCurrentFlavor())
function onColor(e: CustomEvent) {
color.value = e.detail.modelValue as ThemeName
applyTheme(color.value)
}
function onTheme(e: CustomEvent) {
theme.value = e.detail.modelValue as FlavorName
applyFlavor(theme.value)
}
</script>
<template>
<label>Theme</label>
<mono-select
:model-value="theme"
:options.prop="themeOptions"
key-value="name"
display-value="displayName"
@mno-change="onTheme"
/>
<label>Theme color</label>
<mono-select
:model-value="color"
:options.prop="colorOptions"
key-value="name"
display-value="displayName"
@mno-change="onColor"
/>
</template>3. Persist the choice (composable) โ
Save the selection and re-apply it on load so it survives refreshes.
ts
// composables/use-theme.ts
import {
applyTheme,
getCurrentTheme, getCurrentFlavor,
isValidTheme, isValidFlavor,
type ThemeName, type FlavorName,
} from 'mono-helper'
const COLOR_KEY = 'app-theme-color'
const THEME_KEY = 'app-theme'
/** Call once, as early as possible (e.g. in main.ts before mount). */
export function restoreTheme() {
if (typeof document === 'undefined') return // SSR guard
const savedColor = localStorage.getItem(COLOR_KEY)
const savedTheme = localStorage.getItem(THEME_KEY)
applyTheme({
color: isValidTheme(savedColor ?? '') ? (savedColor as ThemeName) : 'navy-sky',
flavor: isValidFlavor(savedTheme ?? '') ? (savedTheme as FlavorName) : 'mono',
})
}
export function setColor(color: ThemeName) {
applyTheme(color)
localStorage.setItem(COLOR_KEY, color)
}
export function setTheme(flavor: FlavorName) {
applyTheme({ flavor })
localStorage.setItem(THEME_KEY, flavor)
}
export function currentTheme() {
return { color: getCurrentTheme(), theme: getCurrentFlavor() }
}ts
// main.ts
import { restoreTheme } from './composables/use-theme'
restoreTheme() // before createApp(...).mount(...)4. React to changes โ
Every change dispatches a theme-changed event on window โ handy for syncing other parts of the app:
ts
window.addEventListener('theme-changed', (e) => {
const { color, themeData, flavor, flavorData } = (e as CustomEvent).detail
// color: ThemeName (palette), flavor: FlavorName (Mono/Material)
// themeData / flavorData: { name, displayName }
})SSR note: the API touches
document, so callapplyTheme/applyFlavoron the client only (insideonMounted, inmain.ts, or behind atypeof document !== 'undefined'guard).
API reference โ
| Export | Purpose |
|---|---|
applyTheme(name | { color?, flavor? }) | Set the color palette and/or the Mono/Material structure |
applyFlavor(name) | Set only the Mono/Material structure |
themes / flavors | Records of { name, displayName } for color presets / structures |
getCurrentTheme() / getCurrentFlavor() | Read the active color / structure |
getAllThemes() / getAllFlavors() | Arrays of the configs (for dropdowns) |
isValidTheme(s) / isValidFlavor(s) | Type-guards for stored strings |
resetTheme() / resetFlavor() | Reset to defaults (navy-sky / mono) |
createThemeClass(name) / createFlavorClass(name) | Build the CSS class string (e.g. for SSR markup) |
ThemeName / FlavorName | Union types: 'navy-sky' | โฆ | 'material' / 'mono' | 'material' |
The API applies classes to
<body>and mirrors the state on<html>viadata-theme-color/data-theme. If you only ever theme statically, skip the API and use the CSS classes from the Manual section.