Layout
Container queries with utility classes
Viewport breakpoints answer "how wide is the screen". Components usually need to know "how wide is my column". Container queries answer that, and Monsoon exposes them as utility classes you can write straight in your HTML.
The two pieces
First, mark a parent as a container with the @container class. It sets container-type: inline-size, which makes the element a size container for its width. Then, on any descendant, prefix a utility with a container breakpoint such as @md::
<div class="@container">
<div class="flex flex-col @md:flex-row gap-4">
<img class="rounded-xl @md:w-1/3" src="cover.jpg" alt="Cover">
<div class="space-y-2">
<h3 class="text-lg @lg:text-2xl font-bold">Title</h3>
<p class="text-muted-foreground">Stacks when narrow, sits in a row when wide.</p>
</div>
</div>
</div>The same card now works in a wide main column and in a narrow sidebar, without knowing anything about the page around it. The class container-q does the same as @container if you prefer a name without the at sign.
Container breakpoints
Container breakpoints are measured in rem because containers are usually smaller than screens:
| Prefix | Container width at least |
|---|---|
@xs: | 20rem (320px) |
@sm: | 24rem (384px) |
@md: | 28rem (448px) |
@lg: | 32rem (512px) |
@xl: | 36rem (576px) |
They are min-width queries, mobile first like the viewport breakpoints: write the narrow layout without a prefix and add the wider layout with a prefix.
Which utilities support them
To keep the stylesheet small, container variants are generated for the utilities you use to rearrange a component:
- display:
block,inline-block,inline,flex,inline-flex,grid,hidden - flex direction and wrapping:
flex-row,flex-col,flex-wrap,flex-nowrap - grid:
grid-cols-1togrid-cols-12,col-span-1tocol-span-12,col-span-full - gap,
items-*andjustify-* - padding
p-*,px-*,py-*for the common steps - width fractions,
w-fullandw-auto - text size
text-xstotext-9xland text alignment
Search the class reference with the "Has @md:" filter for the complete list.
A responsive card grid that needs no breakpoints
Combine the two ideas: the grid uses viewport breakpoints to decide how many columns the page has, and each card uses container queries to decide its own layout inside whatever column width it gets.
<div class="grid grid-cols-1 lg:grid-cols-3 gap-6">
<div class="lg:col-span-2 @container rounded-2xl border bg-surface p-4">
<div class="grid grid-cols-1 @md:grid-cols-2 gap-4">...</div>
</div>
<aside class="@container rounded-2xl border bg-surface p-4">
<div class="grid grid-cols-1 @md:grid-cols-2 gap-4">...</div>
</aside>
</div>Wide column: this card is at least 28rem, so it uses a row.
Narrow column: same markup, stacked.
Breakpoints or container queries?
Both have a place, and they answer different questions. Use viewport breakpoints (md:, lg:) for decisions about the whole page: how many columns the layout has, whether the navigation collapses, how wide the main content is. Use container queries for decisions inside a component: whether a card shows its image beside or above the text, how many items a list shows per row, how large a heading should be.
A good test is to ask where the component might be reused. A product card that appears in a three column grid, a sidebar and a full width search result cannot know the viewport width that matters to it, because the same viewport gives it three different widths. That card should use container queries. A site header always spans the viewport, so viewport breakpoints are the natural fit.
Because container queries are just classes in Monsoon, you can adopt them gradually. Start with the one component that looks wrong in a sidebar, wrap it in an element with @container, and replace its md: prefixes with @md:. Nothing else on the page has to change, and there is no configuration to update.
Things to know
- A container cannot query itself. Put
@containeron a parent and the prefixed classes on its children. container-type: inline-sizemakes the element ignore the width of its contents for sizing, so give containers a width from their layout (a grid cell,w-full, a flex item).- Containers nest. A prefixed class responds to the nearest ancestor container.
- Container queries are supported in all current major browsers.
Frequently asked questions
What is the difference between md: and @md:?
md: checks the viewport width (768px and up). @md: checks the width of the nearest @container ancestor (28rem and up).
Do I need a build step for container queries?
No. The @container class and the @xs: to @xl: variants are in the stylesheet you link from the CDN.
Can I use container queries and breakpoints together?
Yes. A common pattern is breakpoints for the page grid and container queries inside each component.
Which browsers support container queries?
Current versions of Chrome, Edge, Firefox and Safari. Older browsers ignore the prefixed classes and show the unprefixed, narrow layout.
Try it in ten seconds
Paste this into any HTML page. No install, no build.
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/monsooncss">