Skip to content

Theme โ€‹

mono-helper has two independent axes you can mix freely:

AxisWhat it controlsDefaultOptions
ThemeStructure โ€” radius, typography (font), elevation, component shapesMonoMono, Material
Theme colorThe color palette (brand + status colors)Navy & Sky BlueNavy & 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
ThemeMonotheme-material
Theme colorNavy & Skytheme-color-navy-sky ยท theme-color-navy-teal ยท theme-color-blue-gold ยท theme-color-light-blue ยท theme-color-material

Color preset โ†’ name reference:

ClassPalette
(none) / theme-color-navy-skyNavy & Sky Blue (default)
theme-color-navy-tealNavy Teal
theme-color-blue-goldRoyal Blue + Gold
theme-color-light-blueBiru Cerah
theme-color-materialMaterial 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 axis flavor (applyFlavor, flavors, FlavorName). The combined applyTheme({ 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 structure

2. 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 call applyTheme/applyFlavor on the client only (inside onMounted, in main.ts, or behind a typeof document !== 'undefined' guard).


API reference โ€‹

ExportPurpose
applyTheme(name | { color?, flavor? })Set the color palette and/or the Mono/Material structure
applyFlavor(name)Set only the Mono/Material structure
themes / flavorsRecords 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 / FlavorNameUnion types: 'navy-sky' | โ€ฆ | 'material' / 'mono' | 'material'

The API applies classes to <body> and mirrors the state on <html> via data-theme-color / data-theme. If you only ever theme statically, skip the API and use the CSS classes from the Manual section.