Installation
Install Monsoon in Vue 3 with Vite
Vue templates and utility classes fit together naturally: static classes go in class, conditional ones in :class, and the seed color in :style. Monsoon itself is one import in src/main.js.
Install
Start from npm create vue@latest or npm create vite@latest my-app -- --template vue.
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>My app</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/monsooncss@0.2.0/dist/monsoon.min.css" />
<script>(function(){var t;try{t=localStorage.getItem('monsoon-theme')}catch(e){}var d=t==='dark'||(t!=='light'&&matchMedia('(prefers-color-scheme: dark)').matches);document.documentElement.classList.add(d?'dark':'light')})()</script>
</head>
<body class="bg-background text-foreground antialiased">
<div id="app"></div>
<script type="module" src="/src/main.js"></script>
</body>
</html>npm install monsooncssimport { createApp } from 'vue';
import 'monsooncss';
import './style.css';
import App from './App.vue';
createApp(App).mount('#app');Keep the head script from the CDN tab in index.html too: it is what prevents a flash of the wrong scheme.
TypeScript 6 and newer check side effect imports by default (noUncheckedSideEffectImports), and a bare import 'monsooncss' has no type declaration, so tsc reports that it cannot find the module. Add a one line declaration file, for example monsooncss.d.ts containing declare module 'monsooncss';, in src/. This matters when vue-tsc runs as part of npm run build.
The starter src/style.css (or src/assets/main.css) sets its own colors and layout. Clear it so it does not override the theme.
Use the classes
Static classes and :class merge on the same element. The object syntax keeps each class name whole, which also keeps it visible to the purge step:
<script setup>
defineProps({
name: String,
price: String,
featured: { type: Boolean, default: false },
});
</script>
<template>
<article
class="rounded-2xl border bg-surface p-6 space-y-4"
:class="{ 'ring-2 ring-ring': featured, 'shadow-sm': !featured }"
>
<h2 class="text-lg font-semibold">{{ name }}</h2>
<p class="text-3xl font-bold">{{ price }}</p>
<button
type="button"
class="w-full rounded-lg px-4 py-2 font-medium"
:class="featured ? 'bg-primary text-primary-foreground hover:bg-primary/90' : 'bg-secondary text-secondary-foreground hover:bg-secondary/80'"
>
Choose {{ name }}
</button>
</article>
</template><script setup>
import PlanCard from './components/PlanCard.vue';
</script>
<template>
<main class="mx-auto max-w-5xl px-6 py-12">
<div class="grid grid-cols-1 md:grid-cols-3 gap-6">
<PlanCard name="Starter" price="$0" />
<PlanCard name="Team" price="$12" featured />
<PlanCard name="Business" price="$40" />
</div>
</main>
</template>Dark mode
A small composable shares the current scheme across components. It starts from the class the head script already set:
import { ref } from 'vue';
const theme = ref(document.documentElement.classList.contains('dark') ? 'dark' : 'light');
export function useTheme() {
function toggle() {
theme.value = theme.value === 'dark' ? 'light' : 'dark';
const root = document.documentElement;
root.classList.remove('dark', 'light');
root.classList.add(theme.value);
try { localStorage.setItem('monsoon-theme', theme.value); } catch (e) {}
}
return { theme, toggle };
}<script setup>
import { useTheme } from '../composables/useTheme';
const { theme, toggle } = useTheme();
</script>
<template>
<button
type="button"
class="rounded-lg border px-3 py-2 text-sm hover:bg-muted"
:aria-pressed="theme === 'dark'"
@click="toggle"
>
{{ theme === 'dark' ? 'Light mode' : 'Dark mode' }}
</button>
</template>To pin one region to a scheme, bind the class on it: <aside class="bg-background text-foreground p-6" :class="{ dark: alwaysDark }">.
Theme with --seed
Vue sets custom properties from a :style object directly:
<script setup>
import { ref } from 'vue';
const seed = ref('#0d9488');
</script>
<template>
<section class="rounded-2xl bg-surface p-6 space-y-4" :style="{ '--seed': seed }">
<input v-model="seed" type="color" />
<button type="button" class="rounded-lg bg-primary text-primary-foreground px-4 py-2">Primary</button>
</section>
</template>Every semantic token (primary, secondary, accent, surfaces, muted text, borders and the focus ring) is derived from --seed in the browser. Theming lists the tokens.
Using the purge CLI
{
"scripts": {
"dev": "vite",
"css:purge": "monsooncss purge \"index.html\" \"src/**/*.{vue,js,ts}\" -o src/monsoon.purged.css",
"build": "npm run css:purge && vite build",
"preview": "vite preview"
}
}Alias the package to the purged file for builds only, so import 'monsooncss' loads the full file in development:
import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue';
import { fileURLToPath } from 'node:url';
const purged = fileURLToPath(new URL('./src/monsoon.purged.css', import.meta.url));
export default defineConfig(({ command }) => ({
plugins: [vue()],
resolve: {
alias: command === 'build' ? { monsooncss: purged } : {},
},
}));If your config already has resolve.alias (the create-vue template defines @), merge the two objects. With the CDN setup, write to public/monsoon.css and change the <link> to /monsoon.css.
The purge CLI reads your files as plain text and keeps every class name it can see. It cannot see a name that is assembled at runtime, so write each class in full: isActive ? 'bg-primary' : 'bg-muted' survives a purge, 'bg-' + color does not. Names that only exist at runtime (from a database, a CMS or an API) go in --safelist bg-success,bg-danger. In Vue that means quoted keys in :class objects, such as { 'bg-primary': active }.
The purged file always keeps the reset, the theme tokens, the .dark and .light scopes and the keyframes, so dark mode and --seed keep working. See the purge CLI reference for every option.
Next steps
- Browse every utility and its variants in the class reference.
- Read how the scheme is chosen in Dark mode, and how tokens are derived in Theming.
- Server rendering with Vue? See Nuxt.
Frequently asked questions
Can I use Monsoon classes inside scoped styles?
Monsoon classes go in templates. Inside <style scoped> use the theme variables instead, for example color: var(--primary).
Does Monsoon conflict with Vue transitions?
No. Vue adds its own transition classes, and Monsoon utilities such as transition and duration-200 work on the same elements.