Monsoon v0.2.0
Made by Alexandre Kohler Star

Getting started

Variants

A variant is a prefix that applies a utility under a condition: hover:bg-primary/90, md:flex-row, dark:bg-slate-800. Monsoon predeclares 10,237 variant classes, generated for the utility groups where each condition is useful.

5 min read

How variants work

Write the variant, a colon and the utility. The variant only changes when the rule applies, never what it does:

button.html
<button class="bg-primary text-primary-foreground hover:bg-primary/90 active:scale-95 focus-visible:ring-2 disabled:opacity-50">
  Save
</button>

Because the stylesheet is prebuilt, a variant class exists only where it was generated. hover:bg-primary exists, hover:p-4 does not. The class reference lists the variants of every class, and npx monsooncss classes --filter hover: lists them in a terminal.

Every variant

VariantApplies whenCSS
hover:the pointer is over the element:hover
focus:the element has focus:focus
focus-visible:the element has keyboard focus:focus-visible
focus-within:the element or a descendant has focus:focus-within
active:the element is being pressed:active
disabled:a form control is disabled:disabled
group-hover:an ancestor with the group class is hovered.group:hover .x
peer-checked:an earlier sibling with the peer class is checked.peer:checked ~ .x
first:, last:the element is the first or last child:first-child, :last-child
odd:, even:the element is an odd or even child:nth-child(odd), :nth-child(even)
motion-safe:the visitor has not asked for reduced motion@media (prefers-reduced-motion: no-preference)
motion-reduce:the visitor asked for reduced motion@media (prefers-reduced-motion: reduce)
dark:the nearest themed ancestor is dark, or none is set and the system is darkselectors, @scope and prefers-color-scheme
sm: to 2xl:the window is at least 640, 768, 1024, 1280 or 1536px wide@media (min-width: ...)
@xs: to @xl:the nearest container is at least 20, 24, 28, 32 or 36rem wide@container (min-width: ...)

Details on the last three: Dark mode, Responsive design and Container queries.

Which utilities get which variants

Each variant is generated for a curated set of utility groups. This table is computed from the class index that ships with this release:

VariantTypical useClasses per category
hover:semantic colors, white, black, palette steps 500 to 700, opacity modifiers, shadows, opacity, transforms, underlinecolors 574, transforms 113, effects 26, typography 2
focus:semantic colors, rings, outlines, sr-onlycolors 137, borders 25, accessibility 2
focus-visible:the same as focus:colors 137, borders 25, accessibility 2
focus-within:semantic colors, rings, outlinescolors 137, borders 25
active:semantic colors, white, black, opacity modifiers, shadows, opacity, transforms, underlinecolors 301, transforms 113, effects 26, typography 2
disabled:semantic colors, opacity, cursor-not-allowed, pointer-events-nonecolors 69, effects 16, interactivity 3
group-hover:colors, display, visibility, shadows, opacity, transformscolors 342, transforms 113, effects 26, layout 16, typography 2
peer-checked:semantic colors, display, visibility, shadows, opacity, transformstransforms 113, colors 69, effects 26, layout 16, typography 2
first: last: odd: even:semantic backgrounds and borders, light neutral backgrounds, zero padding, margin and border widthscolors 56, spacing 14, borders 5
dark:palette colors, white, black, opacity modifierscolors 593
motion-safe: motion-reduce:transitions and animationstransitions 7, animation 5
sm: to 2xl:display, position, flex, grid, spacing, alignment, sizing, textspacing 449, sizing 94, flexbox 39, typography 28, grid 27, layout 19, alignment 19, accessibility 2
@xs: to @xl:display, flex direction, grid, gap, padding, alignment, width, textspacing 108, grid 25, typography 16, sizing 14, alignment 13, layout 7, flexbox 4

The counts are per variant: odd: has the same classes as first:, and every breakpoint has the same classes as md:.

State variants in practice

Buttons and inputs combine several state variants. transition smooths the change, and the focus ring appears only for keyboard focus:

states.html
<button class="rounded-lg bg-primary text-primary-foreground px-4 py-2 font-medium transition hover:bg-primary/90 active:scale-95 focus-visible:ring-2 focus-visible:ring-offset-2">Save</button>
<button class="rounded-lg border bg-surface px-4 py-2 font-medium transition hover:bg-surface-2 active:scale-95">Cancel</button>
<button disabled class="rounded-lg bg-primary text-primary-foreground px-4 py-2 font-medium disabled:opacity-50 disabled:cursor-not-allowed">Disabled</button>

<div class="flex items-center gap-2 rounded-lg border bg-surface px-3 py-2 focus-within:border-primary focus-within:ring-2">
  <input class="flex-1 bg-transparent text-sm outline-none" placeholder="Search" aria-label="Search">
</div>
Hover, press, tab and type

Group and peer

group-hover: styles a child when an ancestor is hovered. Mark the ancestor with group, and the whole card reacts as one target:

group.html
<div class="group max-w-sm cursor-pointer rounded-xl border bg-surface p-5 transition hover:shadow-lg">
  <p class="font-semibold group-hover:text-primary">Read the theming guide</p>
  <p class="text-sm text-muted-foreground">Hover anywhere on the card.</p>
  <span class="mt-3 inline-block text-sm font-medium text-primary transition-transform group-hover:translate-x-1">Open</span>
</div>
group-hover

Read the theming guide

Hover anywhere on the card.

Open

peer-checked: styles an element when an earlier sibling with the peer class is checked. It is how you build reveal panels and custom choices without JavaScript:

peer.html
<label class="flex flex-wrap items-center gap-3 text-sm">
  <input type="checkbox" class="peer size-4 accent-primary">
  <span class="font-medium peer-checked:text-primary">Add a gift note</span>
  <span class="hidden w-full rounded-lg bg-muted p-3 text-muted-foreground peer-checked:block">
    The note is printed on the packing slip.
  </span>
</label>
peer-checked: tick the box

The peer must come before the element it controls and share its parent, because CSS can only look at earlier siblings. Keep the input visible or give it a visible focus style, so keyboard users can see where they are.

Structural and motion variants

Structural variants style children by position, which keeps repeated markup identical. Motion variants respect the reduced motion setting of the visitor:

structure.html
<ul class="overflow-hidden rounded-xl border bg-surface text-sm">
  <li class="border-t first:border-t-0 odd:bg-surface-2 px-4 py-2">Invoice 1041</li>
  <li class="border-t first:border-t-0 odd:bg-surface-2 px-4 py-2">Invoice 1042</li>
  <li class="border-t first:border-t-0 odd:bg-surface-2 px-4 py-2">Invoice 1043</li>
</ul>

<span class="size-3 rounded-full bg-success motion-safe:animate-pulse"></span>
first:, odd: and motion-safe:
  • Invoice 1041
  • Invoice 1042
  • Invoice 1043
  • Invoice 1044
Pulses only when the visitor allows motion.

What is not supported, and what to do instead

A prebuilt file cannot contain every combination, so a few things are deliberately missing:

Not availableExampleDo this instead
Stacked variantsmd:hover:bg-muted, dark:hover:bg-slate-700Split the conditions across two elements, rely on semantic colors that already adapt to dark mode, or write the rule in CSS.
A variant the class does not havehover:p-4, md:bg-primaryCheck the class reference; otherwise write a small rule.
Opacity modifiers on palette colorsbg-rose-500/50Use a lighter step, or color-mix() with var(--color-rose-500).
The important modifier!p-0Not needed: the reset is in a cascade layer and utilities come last.

Your own CSS can use the theme, so a missing combination takes a few lines and stays correct in every theme and scheme:

site.css
/* md:hover:bg-surface-2, written by hand */
@media (min-width: 768px) {
  .nav-link:hover {
    background-color: var(--surface-2);
  }
}

/* a palette color at 50 percent */
.highlight {
  background-color: color-mix(in oklab, var(--color-rose-500) 50%, transparent);
}

Splitting conditions across elements often reads better than a stacked class anyway. Put the breakpoint on a wrapper and the state on the child: <div class="hidden md:block"> around <a class="hover:bg-muted"> gives you a link that only exists from 768px and reacts to hover.

Five more prefixes cover the media and the writing direction a page is shown in. Like the others they are generated only where they are useful, which the table spells out.

PrefixAppliesClassesGenerated for
print:When the page is printed (@media print).677Layout utilities, the same set as the breakpoints: display, position, flex and grid, spacing, sizing and text size.
screen:Only on screens (@media screen).677The same set as print:.
rtl:Inside an element with dir="rtl", or the element itself.494Utilities that name a physical side: left and right margins and padding, positions, rounded corners, borders, translate, space between, flex row and transform origin.
ltr:Inside an element with dir="ltr".494The same set as rtl:.
forced-colors:When a high contrast theme is active (@media (forced-colors: active)).198Display, borders, outlines, underlines, screen reader helpers and forced-color-adjust.
variants.html
<!-- hide the navigation and show the URL when printing -->
<nav class="flex print:hidden">...</nav>
<p class="hidden print:block">monsoon.example/report</p>

<!-- mirror a physical margin in right to left languages -->
<div dir="rtl">
  <span class="ml-4 rtl:ml-0 rtl:mr-4">Badge</span>
</div>

<!-- keep a visible border when a high contrast theme removes backgrounds -->
<button class="rounded-lg bg-primary text-primary-foreground forced-colors:border">Save</button>

Prefer logical utilities such as ms-4, ps-4 and border-s-2: they follow the writing direction on their own, so rtl: is only needed for the physical ones.

Frequently asked questions

Why can I not combine two variants like md:hover:?

Every combination would multiply the size of the prebuilt file. Use two elements, semantic colors, or a few lines of CSS with the theme variables.

How do I know if a variant exists for a class?

Search the class reference, which lists the variants of every class, or run npx monsooncss classes --filter with the class name.

Do variants work with component classes?

No. Variants are generated for utilities only. Components handle their own states, such as hover and focus, internally.

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.