Tells which breakpoints the viewport has passed, so a stylesheet branches on one by name, CSS offering no other way.
Remarks
Why these exist — a media feature cannot read a custom property, so @media (min-width: var(--x)) never
matches and a breakpoint cannot be a width token. Each of these carries the answer instead of the question: the
stylesheet states the width once, in its own media query, and hands the result on as a token any rule can read.
The breakpoints — four widths, each naming the layout from it upwards, stated in the companion stylesheet alone:
Token
From
What it answers
viewportSmall
30rem
a phone held upright, the one-column floor
viewportMedium
48rem
a tablet or a split window, fitting a second column
viewportLarge
64rem
a laptop, where navigation becomes a rail
viewportXlarge
90rem
a desktop, where the measure is capped not stretched
Floors, not bands — every width is a lower bound, so a wide viewport leaves the narrower flags on and a rule
reads them as a ladder. A rule needing a band states the wider flag as off alongside the narrower one as on,
rather than reaching for a max-width query, where two rules can both match at the boundary or neither can.
Why rem — each width asks whether the content fits, not how big the glass is, and content is measured in
text: a comfortable line runs 65 to 75 characters, which is a multiple of the font size rather than of physical
pixels. A reader who raises the browser default to 24px needs about half as much width again for the same words,
so the layout that fitted at 768 pixels no longer does; a px query cannot see that and hands them the wide
layout with the text crammed into it, while these move them down the ladder to the simpler one.
Inside a media query rem resolves against the browser's initial font size, never against the root font size the
page sets, so neither fontSize nor an app overriding it moves these thresholds: only the
reader's own browser setting does. Browser zoom scales the CSS pixel and so moves a px query and a rem query
alike, and is not a case either unit handles better.
How a rule reads one — through a style query, which matches on the value a custom property holds on an
enclosing element. The tokens are assigned on the root element and inherit, so every element is inside a matching
container and no rule declares a container of its own:
Both branches — each token defaults to off and turns on from its width up, so a rule matches either value.
Reusable, not retunable — the width stays in the stylesheet's own media query, so overriding one of these
tokens forces the flag without moving the threshold. What they remove is the width repeated across every component,
not the need to revise it in one place. An app wanting thresholds of its own writes its own media queries.
Outside the cascade — code needing the same answer reads the token as it reads any other, through
getComputedStyle, which is a point-in-time read; a component that has to react to a threshold being crossed
watches the element rather than polling.
Container queries first — a widget changing shape because of the space it was given states a @container size
query against its own inline size and reads none of these. They answer the page-level questions a container query
cannot: how many columns a screen offers, whether navigation is a rail or a drawer.
Viewport tokens.
Tells which breakpoints the viewport has passed, so a stylesheet branches on one by name, CSS offering no other way.
Remarks
Why these exist — a media feature cannot read a custom property, so
@media (min-width: var(--x))never matches and a breakpoint cannot be a width token. Each of these carries the answer instead of the question: the stylesheet states the width once, in its own media query, and hands the result on as a token any rule can read.The breakpoints — four widths, each naming the layout from it upwards, stated in the companion stylesheet alone:
viewportSmall30remviewportMedium48remviewportLarge64remviewportXlarge90remFloors, not bands — every width is a lower bound, so a wide viewport leaves the narrower flags on and a rule reads them as a ladder. A rule needing a band states the wider flag as
offalongside the narrower one ason, rather than reaching for amax-widthquery, where two rules can both match at the boundary or neither can.Why
rem— each width asks whether the content fits, not how big the glass is, and content is measured in text: a comfortable line runs 65 to 75 characters, which is a multiple of the font size rather than of physical pixels. A reader who raises the browser default to 24px needs about half as much width again for the same words, so the layout that fitted at 768 pixels no longer does; apxquery cannot see that and hands them the wide layout with the text crammed into it, while these move them down the ladder to the simpler one.Inside a media query
remresolves against the browser's initial font size, never against the root font size the page sets, so neitherfontSizenor an app overriding it moves these thresholds: only the reader's own browser setting does. Browser zoom scales the CSS pixel and so moves apxquery and aremquery alike, and is not a case either unit handles better.How a rule reads one — through a style query, which matches on the value a custom property holds on an enclosing element. The tokens are assigned on the root element and inherit, so every element is inside a matching container and no rule declares a container of its own:
Both branches — each token defaults to
offand turnsonfrom its width up, so a rule matches either value.Reusable, not retunable — the width stays in the stylesheet's own media query, so overriding one of these tokens forces the flag without moving the threshold. What they remove is the width repeated across every component, not the need to revise it in one place. An app wanting thresholds of its own writes its own media queries.
Outside the cascade — code needing the same answer reads the token as it reads any other, through
getComputedStyle, which is a point-in-time read; a component that has to react to a threshold being crossed watches the element rather than polling.Container queries first — a widget changing shape because of the space it was given states a
@containersize query against its own inline size and reads none of these. They answer the page-level questions a container query cannot: how many columns a screen offers, whether navigation is a rail or a drawer.