Monsoon v0.2.0
Made by Alexandre Kohler Star

Installation

Install Monsoon in Svelte with Vite

Svelte gives you two tidy tools that map straight onto Monsoon: the class: directive for conditional classes and the style: directive for the seed color. Monsoon itself is one import in src/main.js.

2 min readSvelte with Vite

Install

Start from npm create vite@latest my-app -- --template svelte. Building a full app with routing? Use the SvelteKit guide.

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>

Use the classes

Static names go in class, and each class:name={condition} directive adds one class when the condition is true:

src/lib/PlanCard.svelte
<script>
  let { name, price, featured = false } = $props();
</script>

<article
  class="rounded-2xl border bg-surface p-6 space-y-4"
  class:shadow-sm={!featured}
  class:ring-2={featured}
  class:ring-ring={featured}
>
  <h2 class="text-lg font-semibold">{name}</h2>
  <p class="text-3xl font-bold">{price}</p>
  <button
    type="button"
    class={featured
      ? 'w-full rounded-lg px-4 py-2 font-medium bg-primary text-primary-foreground hover:bg-primary/90'
      : 'w-full rounded-lg px-4 py-2 font-medium bg-secondary text-secondary-foreground hover:bg-secondary/80'}
  >
    Choose {name}
  </button>
</article>
src/App.svelte
<script>
  import PlanCard from './lib/PlanCard.svelte';
</script>

<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>

Svelte 5.16 and newer also accept objects and arrays in class, for example class={['rounded-lg px-4 py-2', { 'bg-primary': active }]}.

Dark mode

src/lib/ThemeToggle.svelte
<script>
  let isDark = $state(document.documentElement.classList.contains('dark'));

  function toggle() {
    isDark = !isDark;
    const theme = isDark ? 'dark' : 'light';
    const root = document.documentElement;
    root.classList.remove('dark', 'light');
    root.classList.add(theme);
    try { localStorage.setItem('monsoon-theme', theme); } catch (e) {}
  }
</script>

<button
  type="button"
  onclick={toggle}
  aria-pressed={isDark}
  class="rounded-lg border px-3 py-2 text-sm hover:bg-muted"
>
  {isDark ? 'Light mode' : 'Dark mode'}
</button>

The class: directive is also the shortest way to pin one region to a scheme: <aside class="bg-background text-foreground p-6" class:dark={alwaysDark}>.

Theme with --seed

The style: directive accepts custom properties, so the seed follows a variable:

src/lib/BrandPreview.svelte
<script>
  let seed = $state('#0d9488');
</script>

<section class="rounded-2xl bg-surface p-6 space-y-4" style:--seed={seed}>
  <input type="color" bind:value={seed} />
  <button type="button" class="rounded-lg bg-primary text-primary-foreground px-4 py-2">Primary</button>
</section>

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/**/*.{svelte,js,ts}\" -o src/monsoon.purged.css",
    "build": "npm run css:purge && vite build",
    "preview": "vite preview"
  }
}
vite.config.js
import { defineConfig } from 'vite';
import { svelte } from '@sveltejs/vite-plugin-svelte';
import { fileURLToPath } from 'node:url';

const purged = fileURLToPath(new URL('./src/monsoon.purged.css', import.meta.url));

export default defineConfig(({ command }) => ({
  plugins: [svelte()],
  resolve: {
    alias: command === 'build' ? { monsooncss: purged } : {},
  },
}));

The alias only applies to vite build, so development keeps the full stylesheet and the build bundles the purged one. With the CDN setup, write to public/monsoon.css and link /monsoon.css instead.

The purge CLI reads class:ring-2={featured} as one token and does not keep ring-2 for it. Classes that appear only in class: directives must either also appear in a quoted string (as in the button above) or go in --safelist ring-2,ring-ring.

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.

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

Frequently asked questions

Do Svelte scoped styles conflict with Monsoon?

No. Scoped styles only target your own selectors. Monsoon classes are global and work in every component.

Can I use the theme variables inside a Svelte style block?

Yes. Every token is a custom property, so color: var(--primary) works in component styles.

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.