Skip to main content
Browse documentation
On this page

Container

.container is an editorial container: a centered content column capped at a reading-friendly measure, with fluid side gutters. .container-fluid is a dashboard/app-shell container: edge to edge, no cap, gutters only. Both are only available in the default build with classes enabled; the classless build applies .container's behavior to body > header/main/footer directly instead (see Landmarks).

Live UI · Default buildAuthentic Cirth · shell overrides declared in source

Centered, width-constrained content using .container.

Show HTML
<div class="container">
  <p>Centered, width-constrained content using <code>.container</code>.</p>
</div>
<main class="container">
  <p>Centered content with a capped reading measure.</p>
</main>

How it works #

Both classes are three-track CSS grids with named full and content lines — two gutter tracks that never drop out, and a center track that holds the content:

.container {
  display: grid;
  grid-template-columns:
    [full-start] minmax(var(--cirth-container-gutter), 1fr)
    [content-start]
      minmax(0, var(--cirth-container-max-width))
    [content-end]
    minmax(var(--cirth-container-gutter), 1fr) [full-end];
}
.container > *,
.container-fluid > * {
  grid-column: content;
}

.container's center track is capped at --cirth-container-max-width (default 60rem, 960px at the standard 16px root); .container-fluid's isn't. The cap resizes continuously with the viewport — there's no breakpoint table to memorize, and no jump between fixed widths as the window is resized. The gutters are controlled by --cirth-container-gutter, which defaults to clamp(1rem, 4%, 3rem): they grow with the container itself but never drop out. A container nested in a narrow panel therefore gets a narrow gutter even on a large viewport. This is deliberately separate from --cirth-spacing, so opening up a page shell does not also enlarge controls, cards, and grid gaps.

.container-fluid fixes each outer track to that gutter and gives all the remaining width to content. It does not split the box into three equal 1fr columns.

Override --cirth-container-max-width per instance for layouts that sit between the two — for example a docs shell with a sidebar might want a wider measure than an article:

.container {
  --cirth-container-max-width: 76rem;
}

Override the gutter independently when a particular shell needs a fixed edge:

.dashboard-shell {
  --cirth-container-gutter: 1.5rem;
}

.breakout #

A direct child of .container normally stops at the center track. Give it .breakout to fill the container's own box instead — for a wide table, image, or figure that shouldn't be squeezed to the reading measure:

Live UI · Default buildAuthentic Cirth · shell overrides declared in source

Regular content stays inside the container's reading measure.

Quarter Revenue Costs Margin Headcount Region
Q1 $482,000 $310,500 35.6% 42 EMEA
Q2 $511,200 $322,900 36.8% 45 EMEA

Back inside the measure after the table.

Show HTML
<div class="container">
  <p>Regular content stays inside the container's reading measure.</p>
  <table class="breakout">
    <thead>
      <tr>
        <th scope="col">Quarter</th>
        <th scope="col">Revenue</th>
        <th scope="col">Costs</th>
        <th scope="col">Margin</th>
        <th scope="col">Headcount</th>
        <th scope="col">Region</th>
      </tr>
    </thead>
    <tbody>
      <tr>
        <td>Q1</td>
        <td>$482,000</td>
        <td>$310,500</td>
        <td>35.6%</td>
        <td>42</td>
        <td>EMEA</td>
      </tr>
      <tr>
        <td>Q2</td>
        <td>$511,200</td>
        <td>$322,900</td>
        <td>36.8%</td>
        <td>45</td>
        <td>EMEA</td>
      </tr>
    </tbody>
  </table>
  <p>Back inside the measure after the table.</p>
</div>
<div class="container">
  <p>Regular content stays inside the reading measure.</p>
  <table class="breakout">
    <!-- wide table -->
  </table>
</div>
.container > .breakout {
  grid-column: full;
}

.breakout deliberately isn't the classic width: 100vw; margin-inline: calc(50% - 50vw) trick — that only works when the container itself is centered in the viewport, which isn't true everywhere a container is used (a docs layout with a sidebar, for example). Breaking out via grid-column instead escapes only to the container's own edges, so it's correct regardless of what surrounds it, and it doesn't have the 100vw trick's scrollbar-gutter overflow bug. .container-fluid doesn't need .breakout — it's already full width.

The selector is deliberately limited to a direct child of .container. A .breakout inside an unrelated .grid remains an ordinary grid item, and a nested descendant cannot jump into an ancestor's grid. Put .container on the semantic element whose direct children need to switch between the two tracks:

<section class="container">
  <h1>Release notes</h1>
  <p>Regular prose.</p>
  <figure class="breakout"></figure>
</section>

Composition contract #

One class should own an element's layout. Do not combine .container with .grid, .row, or another class that defines grid-template-columns on the same node; nest the second layout instead:

<main class="container">
  <div class="grid"></div>
</main>

Cirth intentionally exposes one escape lane, .breakout, rather than a scale of popout/feature/full classes. More lanes would add vocabulary and markup before the library has distinct recurring uses for them; local CSS can add a specialized editorial grid without enlarging the core API.

.breakout is only available in the default build with classes enabled.

Breaking change #

Before this release, .container's max width stepped up at five fixed breakpoints (from 510px to 1450px) and .container-fluid was little more than full width with padding. Both are now the grid described above. If you relied on the old stepped widths, set --cirth-container-max-width to the value you need — there's no longer a breakpoint table, just one continuously-resizing cap.

Search documentation

Type at least two characters to search.