Responsive
Why buttery components respond to their container rather than the window, and how to do the same.
A responsive buttery component changes its layout by the width of the container it's in, through CSS container queries, not by the width of the window. Build your own components the same way, so one component works in a 340px inspector, a sheet on a phone and a 1200px page.
Containers
Page, AppPage, AppShell's column and the patterns that change their own layout, such as SplitHero or Bento, are size containers; Container, Surface and Stack aren't. Grid's columns and the display type step at 640px and 1024px of the nearest one, so a responsive grid needs one above it, and development builds warn when it has none.
Your own component can query them too:
.order-summary {
display: grid;
grid-template-columns: minmax(0, 1fr);
gap: var(--bt-space-4);
}
@container (width >= 640px) {
.order-summary {
grid-template-columns: minmax(0, 2fr) minmax(0, 1fr);
}
}The summary takes two columns once its container is 640px wide, whether that's a page or a wide panel.
@media (width >= 640px) {
.order-summary {
grid-template-columns: minmax(0, 2fr) minmax(0, 1fr);
}
}A viewport query is wrong as soon as the component sits in a panel.
When things don't fit
A row runs out of room
OverflowClustermoves the row's least important items into a "More" menu, rather than wrapping the row or shrinking its text.Text runs long
Text truncatecuts a line that doesn't fit short with an ellipsis, and keeps the whole text in a tooltip.Content has a fixed aspect
FitBoxscales content of a fixed aspect, such as a canvas, into whatever space it has.
Media queries in code
Lay out with container queries in CSS first. When code has to know about the device itself, such as whether it has a fine pointer, useMediaQuery(query) returns whether a media query matches, and stays current as that changes.
import type { ReactElement } from "react";
import { Text } from "@butteryui/core";
import { useMediaQuery } from "@butteryui/react";
export function DragHint(): ReactElement {
const finePointer = useMediaQuery("(pointer: fine)");
return (
<Text variant="caption">
{finePointer
? "Drag a card to reorder it"
: "Press and hold a card to reorder it"}
</Text>
);
}On a server. useMediaQuery returns false when it's rendered on a server.
Checking it
expectButtery checks a page at the widths you give it, from a 390px phone to a 1728px desktop, so a layout that only works at one width fails its test. Check your layout shows how to run it.
Related
- The layout contract
The 13 rules every buttery screen follows, so nothing overlaps, overflows or shifts, and how they're checked.
- Page grid and containers
The page grid, the content column, sections and centered content, for marketing and content pages.