GitHub

Container

Centered, max-width layout wrapper with a symmetric side gutter. The single place the kit owns “constrain page content to a readable width.”

Stable .astro import EsaContainer from '@esa/ecology/esa-container.astro';

Preview

The container fills its parent, caps at maxWidth, centers, and applies a gutter on both sides. The dashed rule below marks its edges.

Content constrained to 480px and centered, with the standard gutter inside the edge.

<Container maxWidth="480px">
  <p>Content constrained to 480px and centered.</p>
</Container>

Widths

The 1556px default is the kit's wide app width — the same figure esa-app-bar centers its own bar at, so a bar and the content beneath it line up without either knowing about the other. Narrow it for reading-width layouts: article bodies, settings forms, single-column detail pages.

640px

420px

260px

<Container maxWidth="1556px">…</Container>  <!-- default: wide app -->
<Container maxWidth="1080px">…</Container>  <!-- dense forms, detail pages -->
<Container maxWidth="720px">…</Container>   <!-- reading width -->

Gutter

The side gutter is --spacing-600 (32px), read directly in the component's own rule — padding-inline: var(--spacing-600, 2rem). It is deliberately neither a prop nor a per-scope hook: every container on a page should breathe the same amount, so the gutter moves when the spacing scale moves and not otherwise.

This page documented a --container-gutter hook until 2026-08-19, and shipped a live demo setting it to 0. That hook was deleted by the container-hooks-removed migration on 2026-08-16, so the demo had been a no-op — rendering identically to the default beside it — for three days. If a region genuinely needs to sit flush to the edge, do not use a container there.

API

PropTypeDefaultDescription
maxWidth string '1556px' Max content width, as any CSS length. The kit's wide page width by default.

Slots

SlotTypeDefaultDescription
default slot — The content being constrained.

Accessibility

  • Renders a plain <div> with no role — it is pure layout and adds nothing to the accessibility tree. Put landmarks (<main>, <section>) around or inside it, not on it.
  • Never remove the gutter globally to fit more in: text touching the viewport edge is a real readability and zoom problem at 320px.
  • The max-width caps the measure, not the font size. Long-form text still wants a narrower maxWidth — around 65–75 characters — rather than a wide container with big type.

Theming surface

Every public token this component reads, extracted from its source at build time, split by blast radius: the hooks that belong to this component, then the shared and system tokens it consumes alongside everything else. The Resolves to column walks each token's real lineage — component → semantic → primitive → raw value — so you can see exactly where re-pointing it lands. A spoke re-skins by overriding these in its theme-<slug>.css; component internals are never edited. No hook for what you need? File it with /request-lego.

Type roles it names

This component does not set a size, weight, face or tracking of its own — it names a composite, and the composite carries all five. A composite is a class rather than a token, so none of it appears in the tables below; re-theme text by re-pointing the token family, not by editing the component.

RoleRe-pointReach
.typography-body-md
  • --typography-body-md-font-family 'DM Sans', sans-serif via --typography-font-family-sans → --font-family-dm-sans
  • --typography-body-md-font-size clamp(0.75rem, 0.66rem + 0.44vw, 0.9375rem) via --font-size-200
  • --typography-body-md-font-weight 350 via --typography-font-weight-regular → --font-weight-350
  • --typography-body-md-letter-spacing 0.01em via --letter-spacing-default
  • --typography-body-md-line-height 1.6 via --line-height-normal
+28 more components esa-app-shell, esa-breadcrumbs, esa-card, esa-checkbox, esa-checkbox-group, esa-collapsible, esa-combobox, esa-command-palette, esa-confirm-dialog, esa-danger-zone, esa-dialog, esa-dropdown-menu, esa-empty-state, esa-entity-search, esa-form-field, esa-header-nav, esa-input-tag, esa-nav-dropdown, esa-page-header, esa-popover, esa-progress-bar, esa-radio-group, esa-select, esa-side-dialog, esa-snackbar-item, esa-switch-toggle, esa-tab-layout, esa-textarea

Wired to this component

None. This component has no hooks of its own — it themes entirely through the shared tokens below, so every override here also moves its siblings.

Shared tokens it reads

Owned by a family or by the semantic layer. Still overridable — but scope the override (esa-foo { --form-border-color: … }) unless you mean to move everything that reads it. Reach says what else that is.

Token Reach Resolves to
--spacing-600 primitive every component 2rem