Breakpoints
A breakpoint is the viewport width where a layout stops working well one way and switches to another, going from a single stacked column on a phone to several columns side by side on a wider screen, for example. Reaching for one shared set of widths instead of a new number each time keeps every layout switch in an app landing on the same handful of points, so the whole page still feels consistent as it resizes.
EUNOMIA_BREAKPOINTS is that shared set for this library: four widths
named mobile, tablet, laptop, and desktop. Reach for mobile
when a change only matters on the smallest phones. Reach for tablet
once a layout generally has enough room to stop behaving like a phone,
the most common switch in practice. laptop and desktop are there
for a change that only makes sense once a screen has even more room to
spare, at the same steps as the Sass page grid scale (see "See also"
below).
Try it
Drag the box's bottom-right corner, or drag the cursor on the ruler
below. The two are connected, so moving one moves the other, and you
can also focus the cursor and use the arrow keys. Each checkpoint
lights up the moment the width reaches it, using the exact same
width >= EUNOMIA_BREAKPOINTS.<token> test a component's own @media
rule makes internally.
Usage
EUNOMIA_BREAKPOINTS is a plain TypeScript object. Import it, both
for a component's own css template and for your own responsive
code:
import { css } from "lit";
import { EUNOMIA_BREAKPOINTS } from "@eunomia/elements";
css`
@media (max-width: ${EUNOMIA_BREAKPOINTS.tablet}px) {
:host {
flex-direction: column;
}
}
`;
The same object works for a plain matchMedia listener too, for a
layout switch driven from your own JavaScript instead of CSS:
import { EUNOMIA_BREAKPOINTS } from "@eunomia/elements";
const isMobile = window.matchMedia(
`(max-width: ${EUNOMIA_BREAKPOINTS.tablet}px)`,
);
In Sass
The same widths are also available as Sass mixins, built on the same
numbers as EUNOMIA_BREAKPOINTS. Every eunomia-lte-<token> mixin
applies below that width, the same - 1 convention the components use
internally, and its eunomia-gte-<token> counterpart applies from
that width upward, so the two together tile the full range with no
gap and no overlap at the breakpoint itself:
| Mixin | Applies |
|---|---|
eunomia-lte-mobile |
width below 600px |
eunomia-lte-tablet |
width below 768px |
eunomia-lte-laptop |
width below 1024px |
eunomia-lte-desktop |
width below 1280px |
eunomia-gte-mobile |
width from 600px up |
eunomia-gte-tablet |
width from 768px up |
eunomia-gte-laptop |
width from 1024px up |
eunomia-gte-desktop |
width from 1280px up |
eunomia-mobile |
shorthand for eunomia-lte-tablet |
eunomia-desktop |
shorthand for eunomia-gte-tablet |
eunomia-mobile and eunomia-desktop exist because splitting at
tablet is the single most common switch in practice (see "Try it"
above); reach for the full eunomia-lte-<token>/eunomia-gte-<token>
set for any other split.
@use "@eunomia/elements/styles/tools/indexes/eunomia-media-queries.index" as *;
.my-panel {
@include eunomia-lte-tablet {
flex-direction: column;
}
}
In CSS
The same four widths are also published as CSS custom properties once a theme stylesheet is loaded, so a plain stylesheet can reach for them without importing anything from TypeScript or Sass:
| Token | Value |
|---|---|
--eun-breakpoint-mobile |
600px |
--eun-breakpoint-tablet |
768px |
--eun-breakpoint-laptop |
1024px |
--eun-breakpoint-desktop |
1280px |
.custom-panel {
max-width: var(--eun-breakpoint-tablet);
}
A @media condition still can't read a custom property, the CSS spec
doesn't allow it, so this doesn't replace the Sass mixins above for
switching a layout at a given width. Reach for it instead where a
breakpoint value is needed inside a regular declaration (a max-width,
a calc()) or read from JavaScript with getComputedStyle, so that
value stays in sync with the same numbers everything else here uses.
Container queries
Every switch point above reacts to the browser viewport. A @container
condition reacts to an ancestor's own rendered size instead, so a
component embedded in a narrow dashboard panel compacts the same way
it would in a narrow browser window, regardless of how wide the page
around it actually is. eun-calendar's hour grid uses exactly this to
size its own layout off its rendered width rather than the page's.
An element only becomes a query container once something declares it
one with container-type, and EUNOMIA_BREAKPOINTS plugs into the
condition the same way it does in a @media rule, again as a literal
pixel value rather than a var():
import { css } from "lit";
import { EUNOMIA_BREAKPOINTS } from "@eunomia/elements";
css`
:host {
container-type: inline-size;
}
@container (max-width: ${EUNOMIA_BREAKPOINTS.tablet}px) {
.content {
flex-direction: column;
}
}
`;
Give the container a container-name when a query needs to skip past
a nearer container and reach a specific named ancestor instead of the
closest one:
:host {
container-type: inline-size;
container-name: my-panel;
}
@container my-panel (max-width: 768px) {
.content {
flex-direction: column;
}
}
Container styles
A container query can also read a custom property's value instead of
a size, with style(). Combined with a size query that sets the
property, this turns a one-off width check into something the rest of
an app can also read, inherit, or override directly:
:host {
--my-panel-compact: false;
}
@container (max-width: 768px) {
:host {
--my-panel-compact: true;
}
}
@container style(--my-panel-compact: true) {
.content {
flex-direction: column;
}
}
Because --my-panel-compact is a real custom property, it inherits
like any other, so an app can force it on an ancestor
(.my-panel { --my-panel-compact: true; }) to opt into the compact
layout regardless of the container's actual rendered width. This is
exactly the pattern behind eun-stepper's --stepper-mobile and
eun-calendar's --scheduler-mobile/--scheduler-tablet custom
properties, see the "See also" links below for both in context.
See also
Space : the spacing scale used alongside these breakpoints inside componentsGrid utilities : page-level layout classes, built on a separate Sass breakpoint scale for whole-page grids rather than a single component's own layoutStepper : its "Compact display" section shows the container style-query flag pattern above on a real component, via--stepper-mobileCalendar : its hour grid sizes itself off its own rendered width with a@containersize query, via--scheduler-mobile/--scheduler-tablet