Monsoon v0.2.0
Made by Alexandre Kohler Star

Installation

Install Monsoon in PHP

A plain PHP site usually shares one header include across pages. Put the Monsoon <link> there and every page that includes it gets the utilities, the seed theme and dark mode.

2 min readPHP

Install

includes/header.php
<?php $title = $title ?? 'My site'; ?>
<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title><?= htmlspecialchars($title, ENT_QUOTES) ?></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">
public/pricing.php
<?php
$title = 'Pricing';
require __DIR__ . '/../includes/header.php';
?>
<main class="mx-auto max-w-5xl px-6 py-12">
  <h1 class="text-3xl font-bold tracking-tight">Pricing</h1>
</main>
<?php require __DIR__ . '/../includes/footer.php'; ?>

Use the classes

A small render function keeps markup in one place. Escape every value, and keep each class branch a full string:

includes/components.php
<?php
function plan_card(array $plan): void
{
    $featured = !empty($plan['featured']);
    $card = $featured
        ? 'rounded-2xl border bg-surface p-6 space-y-4 ring-2 ring-ring'
        : 'rounded-2xl border bg-surface p-6 space-y-4 shadow-sm';
    $button = $featured
        ? 'block rounded-lg bg-primary text-primary-foreground px-4 py-2 text-center font-medium hover:bg-primary/90'
        : 'block rounded-lg bg-secondary text-secondary-foreground px-4 py-2 text-center font-medium hover:bg-secondary/80';
    $name = htmlspecialchars($plan['name'], ENT_QUOTES);
    ?>
    <article class="<?= $card ?>">
      <h2 class="text-lg font-semibold"><?= $name ?></h2>
      <p class="text-3xl font-bold"><?= htmlspecialchars($plan['price'], ENT_QUOTES) ?></p>
      <a href="/signup.php?plan=<?= urlencode($plan['slug']) ?>" class="<?= $button ?>">Choose <?= $name ?></a>
    </article>
    <?php
}
public/pricing.php
<div class="grid grid-cols-1 md:grid-cols-3 gap-6">
  <?php foreach ($plans as $plan) { plan_card($plan); } ?>
</div>

Dark mode

The head script in the header include sets the scheme before paint. Put the button in the header and the toggle script in the footer include:

includes/header.php
<button type="button" id="theme-toggle" class="rounded-lg border px-3 py-2 text-sm hover:bg-muted">
  Toggle theme
</button>
includes/footer.php
<script src="/js/theme-toggle.js" defer></script>
</body>
</html>
public/js/theme-toggle.js
const button = document.getElementById('theme-toggle');

button.addEventListener('click', () => {
  const root = document.documentElement;
  const next = root.classList.contains('dark') ? 'light' : 'dark';
  root.classList.remove('dark', 'light');
  root.classList.add(next);
  button.setAttribute('aria-pressed', String(next === 'dark'));
  try { localStorage.setItem('monsoon-theme', next); } catch (e) {}
});

Theme with --seed

Check the color against a strict pattern before printing it. htmlspecialchars stops HTML injection, but only the pattern keeps the value a valid CSS color:

public/team.php
<?php
$seed = $team['brand_color'] ?? '';
if (!preg_match('/^#[0-9a-f]{6}$/i', $seed)) {
    $seed = '#0d9488';
}
?>
<section style="--seed: <?= $seed ?>" class="rounded-2xl bg-surface p-6 space-y-4">
  <h2 class="text-xl font-bold text-primary"><?= htmlspecialchars($team['name'], ENT_QUOTES) ?></h2>
  <a href="/team.php" class="inline-block rounded-lg bg-primary text-primary-foreground px-4 py-2">Open</a>
</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

Point the globs at your PHP folders, not the whole project, so vendor/ stays out of the scan:

package.json
{
  "private": true,
  "scripts": {
    "css:purge": "monsooncss purge \"public/**/*.php\" \"includes/**/*.php\" \"templates/**/*.php\" \"public/js/**/*.js\" -o public/css/monsoon.css"
  },
  "devDependencies": {
    "monsooncss": "^0.2.0"
  }
}

Then link /css/monsoon.css in the header include. Node.js is only needed on the machine that runs the script; the server just serves a CSS file.

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

Does Monsoon need Composer?

No. It is a static CSS file. Link it from the CDN or copy it into your web root.

Can I keep the toggle choice on the server?

Yes. Save it in a cookie, read $_COOKIE, and print class="dark" or class="light" on <html> after checking the value against those two words.

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.