Breakpoints, media queries, responsive style utilities and hooks for layouts that adapt to the viewport.
Pluma has one set of breakpoints, shared by components and apps. Use them instead of writing pixel widths into your own media queries, so that every part of the product changes layout at the same widths.
Each breakpoint is a minimum width. A style for md applies from 768px up, so write the narrow layout as the
default and add changes for wider screens ("mobile first"). Below sm, no breakpoint applies; the hooks call
this base.
| Name | Applies from | Media query |
|---|---|---|
sm | 576px | screen and (min-width: 576px) |
md | 768px | screen and (min-width: 768px) |
lg | 1024px | screen and (min-width: 1024px) |
xl | 1280px | screen and (min-width: 1280px) |
Prefer CSS over JavaScript for responsive layout. CSS changes with the viewport before the page paints, and it works during server rendering. Use the hooks only when the content itself has to change, not just its layout. The AppLayout responsive header uses both in an app header.
@customerio/pluma-components/css exports mediaQueries, ready to use as a vanilla-extract @media key, and
breakpoints, the same widths as numbers.
Media and container queries can't read CSS variables, which is why breakpoints are plain values rather than
tokens. For a container query, build the condition from breakpoints:
Layout props accept an object keyed by breakpoint as well as a single value. Each key applies from that width up,
and base applies below sm. This works on Box and every component that takes Box props, and on the layout
props of Flex, Stack, InlineStack and Grid.
You don't need to set base. Without it, the component's own value applies below the first breakpoint you set, so
you only describe what changes on wider screens. <Flex display={{ md: 'none' }}> is a flex container below md and
hidden from md up, and <Spinner gap={{ md: '400' }}> keeps Spinner's own gap below md.
These component props accept per-breakpoint values:
direction, wrap, justifyContent, alignItems, gap, rowGap, columnGap, display.gap, alignBlock, alignInline, display.gap, alignBlock, alignInline, wrap, display.columns, rows, areas, autoFlow, gap, rowGap, columnGap, justifyContent, alignItems.textAlign.gap.mt, mb and my. Below the first breakpoint you set, the section keeps its gap to the sections around it.sprinkles(), the function behind the style utilities, takes the same values.
Use it for elements that aren't Pluma components.
Style utility props and sprinkles() accept per-breakpoint values for these properties:
p, py, px, m, my, mx, display, flexDirection, justifyContent, alignItems, flexWrap, textAlign, pt, pr, pb, pl, mt, mr, mb, ml, gap, rowGap, columnGap, w, minW, maxW, h, minH, maxH
Responsive utility props cover layout, flexbox alignment, spacing and sizing. If you need another property to
change by breakpoint, write a vanilla-extract style with mediaQueries.
Props also accept raw CSS values in square brackets at each breakpoint. For example, React can use
px={{ md: '[2rem]' }} and Ember can use @px={{hash md="[2rem]"}}. In Vite apps, register the
style utility plugin for these values.
When the content has to change with the viewport, read the breakpoint from JavaScript. Both frameworks share one listener per query, however many components use it.
useMediaQuery(query) returns whether a media query matches. useBreakpoint() returns the largest breakpoint
the viewport matches: 'base', 'sm', 'md', 'lg' or 'xl'. Both update when the viewport crosses a
breakpoint.
During server rendering, useMediaQuery returns false and useBreakpoint returns 'base', then both update
on the client.