Adapting to width

Components follow the space they sit in, and four window classes name where a layout changes.

On this page

Import

import { useContainerSize } from '@envsemble/envui';

Usage

import type { ReactNode } from 'react';
import { useRef } from 'react';
import { useContainerSize, windowClassOf, windowClasses } from '@envsemble/envui';

function ChartPanel({ chart, legend }: { chart: ReactNode; legend: ReactNode }) {
  // The legend moves under the chart when the panel itself is compact, wherever it is placed.
  const panel = useRef<HTMLDivElement>(null);
  const size = useContainerSize(panel);
  const narrow = size !== undefined && windowClassOf(size.inlineSize) === 'compact';
  return (
    <div ref={panel}>
      {chart}
      {narrow ? <details>{legend}</details> : legend}
    </div>
  );
}

// The width where a window class starts, for code of your own: '37.5rem'.
const medium = windowClasses.medium;
/* Details beside the report from the expanded window class up; compact first. */
.report {
  display: grid;
  gap: var(--env-space-6);
}
@container env-window (width >= 52.5rem) {
  .report {
    grid-template-columns: minmax(0, 2fr) minmax(0, 1fr);
  }
}
NameTakes
windowClasses{ compact: '0rem', medium: '37.5rem', expanded: '52.5rem', large: '75rem' }: where each window class starts
WindowClass'compact', 'medium', 'expanded' or 'large'
env-windowThe container the outermost theme boundary is; a query on it measures the window class
useContainerSize(target, { box })An element's { inlineSize, blockSize } in CSS pixels, measured before it paints; undefined on the server
windowClassOf(inlineSize)The window class of a width in CSS pixels, such as useContainerSize's, at the root font size
--env-z-base … --env-z-tooltipThe stacking order of envUI's layers and the application's own, lowest first

Window classes

Window classWidthWhere it falls
compactNarrower than 37.5 remA phone held upright, or a narrow pane
medium37.5 to 52.5 remA tablet held upright, or a narrow window
expanded52.5 to 75 remA tablet held sideways, or a laptop window
largeFrom 75 remA desktop window
  • The classes are Material's window size classes. They start at 600, 840 and 1200 px at the default text size, and are in rem, so they follow the reader's text size: with larger text, a window counts as a narrower class.
  • Measured on a container, never the viewport: a page embedded in a smaller frame gets the class of its own width. Menus, dialogs and sheets measure the window they open in.
  • container="env-window" on a primitive makes a region a window of its own, such as a preview. Don't give envUI's other env-* container names to your own.
  • Compact first: write the compact layout as the default and switch up with width >=. Where no env-window is found, no query matches and the compact layout shows. CSS can't read the --env-window-class-* tokens inside a query, so write the width.

The words of the figures

  • Components follow their space: nothing to do; envUI components query their container, never the viewport.
  • Popups fit the window: nothing to do; menus, dialogs and sheets measure the window they open in.

Composition

Containers only

envUI has no width media queries: components respond to their container, so a component in a narrow pane lays out as on a phone.

  • Do the same for a region of your own with container: my-region / inline-size and @container my-region (width >= 37.5rem).
  • Media queries not about width stay: pointer, forced colors, reduced motion, contrast and color scheme.
  • useContainerSize is for what CSS can't do, such as rendering something else when a panel is narrow. Where only styles change, a container query does it without script and without a first render that differs.

Customize

Stacking order

TokenValueFor
--env-z-base0The flow of a page or a component
--env-z-raised1A part over its siblings inside a component, such as a sticky table header
--env-z-sticky20The frame's sticky parts that content scrolls under: the top bar, the navigation
--env-z-supporting-pane30A pane over the content that doesn't dock, such as a supporting pane as a sheet
--env-z-backdrop90The backdrop behind a modal layer
--env-z-modal100Dialogs, sheets and the command palette
--env-z-popup110Menus, popovers and the lists of Select and Combobox
--env-z-toast120Notifications
--env-z-tooltip130Tooltips
  • Take a level for what a layer is: a sticky toolbar of your own takes var(--env-z-sticky). A step between levels is a small calc(), such as calc(var(--env-z-sticky) + 1).
  • envUI's popups are portaled to the end of the document; keep your own layers below --env-z-backdrop. envUI's Stylelint config reports a literal z-index.

TypeScript declarations provide the complete public API.

Words in code

WordsIn code
On a phoneThe compact window class, the default layout every query starts from
In a narrow paneThe pane's own width; a region of your own becomes a window with container="env-window"
Side by side from expanded@container env-window (width >= 52.5rem) around the two-column layout
Stack on a phoneThe default, one-column layout, before any width >= query
Wrapwrap on Row, so its children run onto the next line