DocumentationComponentsTheme CreatorGitHub
Theme CreatorGitHubIntroduction
InstallationUsageTheming
ComponentsAccordionAction SheetAlertAlertDialogArea ChartAspectRatioAvatarBadgeBannerBar ChartBottom NavBreadcrumbButtonButtonGroupCalendarCardCarouselChartChatBubbleChatBubbleNewCheckboxChipCoachMarkCodeBlockCollapsibleColor PickerComboboxCommandContainerContextMenuDate PickerDividerDrawerDropdown MenuEmptyStateFABFieldFieldsetFile UploadFlexFormGridHoverCardIconButtonLabelLine ChartLinkMenubarMeterModalNavigation MenuNumberFieldOTP InputPaginationPickerPie ChartPopoverProgressPromptInputRadar ChartRadial ChartRadioRatingScroll AreaSearchSegmentedControlSelectShortcutSidebarSkeletonSliderCircularSliderMediaTrimmerSpacerSpinnerSplit ButtonStackStatusStepperSurfaceSwitchTableTabsTextareaTime PickerToastToggleButtonToggleGroupTokenizerToolbarTooltipTop Header DesktopTop Header Mobile
Contributing
Components

Container

Max-width wrapper with page gutters. Seven widths on the field's common scale, a responsive gutter ladder, a breakout grid for full-bleed children, published CSS variables for custom widths and edge-to-edge rails, and polymorphic rendering.

Playground

Installation

pnpm add @tessinaui/ui

Usage

import { Container } from "@tessinaui/ui";
{/* The default: a 1152px cap, responsive gutters, centred */}
<Container>…</Container>

{/* A reading column, rendered as the page's <article> */}
<Container size="sm" render={<article />}>…</Container>

{/* Fluid width, gutters only — a dashboard */}
<Container size="full">…</Container>

{/* One container for a flowing page; one child breaks out to full width */}
<Container layout="breakout" size="sm">
  <p>In the column.</p>
  <figure data-breakout>…edge to edge…</figure>
  <p>Back in the column.</p>
</Container>

{/* A width off the scale */}
<Container style={{ "--container-size": "40rem" } as React.CSSProperties}>…</Container>

Examples

Default

Content capped at lg (1152px), with gutters that step with the viewport, centred in its parent.

Sizes

Seven widths, plus a custom one set through the published variable. A size is a cap: when the parent is narrower — as this preview is for the larger sizes — the Container renders at the parent's width, which the readout shows live.

Padding

The gutter inside the box. responsive, the default, steps 16 → 24 → 32 → 48px at the md, lg and xl breakpoints.

Alignment

center={false} holds the box to the start edge. Start is logical, so under dir="rtl" the same prop lands it on the right.

Reading column

sm is the reading column. With the widest gutter its content box is 576px — about 75 characters of 16px body text per line, the top of the 45–75 range typographers recommend — so the eye's return sweep stays short. Here it renders as the page's <article>.

Form column

A settings or profile form reads best in a narrow column — long inputs are harder to scan and invite over-long answers. xs (448px) is the form width on this scale.

Fluid dashboard

size="full" removes the cap and keeps the gutters, which is the shape of most dashboards. Let rows reflow by their own width (auto-fit) rather than the viewport's, so they behave inside any pane.

Nested widths

Two widths on one page: a narrow inner Container keeps a headline short while the row below uses the outer width. Give the inner one padding="none" so the gutter is not counted twice.

Full-bleed band

A painted section wrapping a Container: the background runs edge to edge while the copy stays readable. The section is the band — no outer size="full" Container is needed.

Breakout

layout="breakout" turns one Container into a three-track grid for flowing content. Every child lands in the centre column; a child marked data-breakout spans the full width. A band whose own content should line up with the column nests a Container of the same size and padding.

Edge-to-edge rail

The published --container-px lets a child escape the gutter. A horizontal rail runs to the Container's edge — the screen edge on a phone — while its first card still lines up with the text.

Semantic HTML

render keeps Container's styling on a semantic element.

Sizes

SizeCapUseWhere the field lands
xs448px (28rem)Forms, settings, modal-width contentRadix size-1 448 · MUI xs 444
sm672px (42rem)Reading columnApple's readable content guide 672pt · Radix 688 · Polaris narrow 662
md896px (56rem)Long-form articleAtlassian fixed-narrow 864 · Radix 880
lg1152px (72rem)Default — app content, pricingRadix 1136 · Bootstrap and Mantine 1140 · MUI lg 1200
xl1280px (80rem)Wide pagesPrimer xl 1280 · Spectrum 1280
2xl1536px (96rem)Ultra-wide dashboards, data tablesMUI xl 1536
full100%Fluid — gutters onlyEvery system's "fluid"

The cap is the box's outer width, gutters included: at lg with 48px gutters the content is 1056px wide.

Padding

PaddingGutter per sideUse
none0The parent already provides the gutter
xs8pxDense layouts
sm16pxPhones — Material's compact margin and the common iOS page margin
md24pxStandard
lg32pxGenerous
xl48pxMarketing and wide desktop pages
responsive16 → 24 → 32 → 48pxDefault — steps at md / lg / xl

responsive is written as four disjoint bands — max-md: · md:max-lg: · lg:max-xl: · xl: — never as one base value with min-width overrides. In Tailwind v4 the winner of a min-width ladder is decided by stylesheet emission order, and the old one rendered 24px at 1400px where it asked for 48.

The bands step with the viewport. Inside a narrow pane on a wide screen, pick a fixed padding instead.

Layout: block and breakout

block (the default) is a max-width box with inline padding, centred with mx-auto.

breakout is a grid of three tracks — 1fr · column · 1fr. The column is exactly as wide as a block Container's content box, so the two layouts line up on one page, and the side tracks are never narrower than the gutter.

  • Every direct child goes in the column. A child with data-breakout spans all three tracks.
  • Both rules have zero specificity, so a child's own col-* class wins: col-[2/-1] bleeds to one side only.
  • Put text inside elements. A bare text node cannot be targeted by the column rule, so the grid would place it in a side track.
  • Grid items do not collapse their margins. Space the rows with gap-y-* on the Container.
  • center has no effect here — the column is always centred.

CSS variables

Container publishes its geometry on the root, and every descendant inherits it.

VariableSet byValue
--container-sizesizeThe cap: 28rem … 96rem, or 100%
--container-pxpaddingThe gutter per side — per band for responsive
  • A width off the scale. Set the variable: style={{ "--container-size": "40rem" }}. An inline style beats the size class, and it works in both layouts. On the render path Container's style is merged with the host's, never replaced.
  • A rail to the edge. -mx-[var(--container-px)] px-[var(--container-px)] cancels the gutter with a negative margin and puts it back as padding, so the first item still lines up with the text. Because it reads the variable, it stays right at every band.
  • Custom padding. Use the padding prop, the variable, or the logical sides ps-* / pe-*. Not px-*: tailwind-merge treats px-* and the logical sides as separate groups, so both classes would ship and stylesheet order would pick the winner.

When to use

Use it for the page-level width: the column an app screen, an article, a settings page or a marketing section sits in.

Reach for something else when:

  • You want a surface. Container paints nothing. Put a Card or Surface inside it, or a painted section around it.
  • You want side-by-side panes — a checkout's form and summary, or a docs page's navigation, article and table of contents. That is Grid and Sidebar; each pane can hold its own Container.
  • You want vertical rhythm. Spacing between sections is Stack or Spacer, or py-* on the section. Container owns the inline axis only.
  • The content lives in an overlay. A Modal or Drawer sets its own width.
  • You reached for Tailwind's container class. Don't combine the two: that utility sets its own per-breakpoint max-width, which races Container's.

Accessibility

  • Pattern: none — layout only. Container renders a plain <div> and adds no role, name or state. Give it meaning with render: <main> once per page, <article> for a self-contained post, or <section> with a heading or aria-label when it should be a region landmark.
  • Keyboard: none. It adds no tab stop and handles no keys.
  • Reflow. Its width follows the parent, so Container never forces horizontal scrolling on its own at any zoom level (WCAG 1.4.10).
  • RTL. Padding uses logical sides, so the gutters mirror under dir="rtl" with no extra work.
  • Contrast. Container paints nothing. The plates in the examples on this page pass axe in both themes.
  • AT matrix. Chromium's accessibility tree verified: the plain <div> exposes no role, and render={<article />} exposes one article. VoiceOver, NVDA and JAWS were not separately exercised — Container contributes no role, name, state or focusable node for them to announce.

API Reference

Container

PropTypeDefaultDescription
size"xs" | "sm" | "md" | "lg" | "xl" | "2xl" | "full""lg"Width cap, published as --container-size
padding"none" | "xs" | "sm" | "md" | "lg" | "xl" | "responsive""responsive"Inline gutter, published as --container-px
layout"block" | "breakout""block"A max-width box, or a three-track grid whose data-breakout children span the full width
centerbooleantrueCentre the block box; false holds it to the start edge. No effect in breakout
renderReactElement—Render as another element — <main />, <section />, <article />
dir"ltr" | "rtl"—Text direction. Stamped only when set
classNamestring—Merged last
styleCSSProperties—Merged — the way to set --container-size or --container-px

Every other HTML attribute — id, aria-*, data-*, event handlers — is forwarded to the root, which carries data-slot="container".

Notes

  • On the render path the host's own props win, with three exceptions: className and style merge, and dir stays the host's unless Container sets one. A ref on the host element is kept alongside Container's.
  • data-slot="container" is stamped on a DOM host only; a composite host keeps its own slot.
  • No effects and no measurement — safe to render on the server.
CommandContextMenu

On this page

PlaygroundInstallationUsageExamplesDefaultSizesPaddingAlignmentReading columnForm columnFluid dashboardNested widthsFull-bleed bandBreakoutEdge-to-edge railSemantic HTMLSizesPaddingLayout: block and breakoutCSS variablesWhen to useAccessibilityAPI ReferenceContainerNotes