Monsoon v0.2.0
Made by Alexandre Kohler Star

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.

2 min readVue 3 with Vite

Install

Start from npm create vue@latest or npm create vite@latest my-app -- --template vue.

index.html
<!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>

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:

src/components/PlanCard.vue
<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>
src/App.vue
<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:

src/composables/useTheme.js
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 };
}
src/components/ThemeToggle.vue
<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:

src/components/BrandPreview.vue
<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

package.json
{
  "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:

vite.config.js
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.

No recent searches

Try , , or .

Recent

Loading the search index

No results for ""

Check the spelling, or search a class name such as px-4.