Skip to content

src/layout - 8 files

Layout

Six primitives and two helpers. None of them uses a breakpoint class. Where a layout has to change shape it does so from its own width, so the same markup is correct on a phone, inside a grid cell and inside a drawer.

Every example below is live, and every one sits in a container you can resize with the slider under it. That control is the whole argument of this page: these primitives reflow from their own width, so shrinking the box they are in has to change their shape without the viewport moving at all. Drag one and watch. If the claim were false the box would just clip.

The markup panels below are read out of the live examples by site.js, and the width controls come from src/behaviour/demo-stage.js. With scripting off the examples still render at their natural width, the sliders do nothing and the panels stay empty.

Shell

Centres a column, bounds its width and owns the gutter between content and the viewport edge. The block is bs-wrap rather than bs-container, because --bs-container-* is already the global width scale and a block named container would sit in the namespace of the scale it reads from.

Put the class on the element that already means something rather than wrapping a landmark in a spare div. The gutter is declared twice, the second time through env(), so content never sits under a notch.

src/layout/container.css, adds 1,044 B gzip
A shell bounded to the small width, centred in whatever it sits in.
auto
Show codeHide code

Widths: --xs --sm --md --lg --xl --2xl from the container scale, --prose for a reading measure in ch, --full for a section that owns its own background, and --flush to drop the gutter. A shell inside a shell pays the gutter once.

Stack

One consistent gap between every child, owned by the parent. A margin belongs to the child, so spacing between two blocks depends on which two they are and on whose margin wins the collapse. A gap belongs to the container, so the rhythm survives reordering, filtering and conditional rendering.

src/layout/stack.css, adds 850 B gzip
First
Second
Third
auto
Show codeHide code

Modifiers: --tight --loose --flush for the gap, and --start --center --end --between for alignment. Use --bare on a ul, and add role="list": removing the marker removes the list semantics in Safari and VoiceOver, so the item count is lost without it.

Cluster

A horizontal group that wraps. Tag lists, button rows, meta lines, toolbars: anything where the item count is unknown and the container width is not. Wrapping is not an option here, it is the default, because a row that cannot wrap either overflows the page or needs a horizontal scroller, and at 320px with text scaled to 200% it will do one of the two.

src/layout/cluster.css, adds 1,113 B gzip
Ready
auto
Show codeHide code

Keep the gap at or above --bs-space-2 for a row of interactive targets so they stay separable under WCAG 2.2 SC 2.5.8. Alignment modifiers: --start --center --end --between --baseline --stretch.

Grid

No breakpoint classes. The column count is a function of the grid's own width, so the same markup is correct full bleed, inside a sidebar and inside another grid cell. A hand-written column system cannot do that, because it is keyed to the viewport and the viewport does not know how much room the component was given.

src/layout/grid.css, adds 2,044 B gzip
  • EMEA
  • APAC
  • LATAM
  • NA
auto
Show codeHide code

--bs-grid-min sets the track floor. --fill keeps empty tracks instead of collapsing them, --rows uses subgrid so headings and footers line up across a row, --query makes each cell a container query context, and bs-grid__full spans a cell across the row. Place items in DOM order: moving them visually with grid placement breaks reading and focus order.

Split

Two panes at an authored ratio, stacked when they no longer both fit. The switch comes from the layout's own width, not from a breakpoint, so a split nested in a grid cell or a drawer collapses at the right moment rather than at the viewport's.

src/layout/split.css, adds 1,143 B gzip
Two shares of the line
One share
auto
Show codeHide code

Ratios: --2-1 --3-1 --1-2 --1-3, with no modifier meaning even. --bs-split-min is the width below which the panes stop sharing a line.

Centre

Centres a column horizontally and bounds it to a readable measure. The default maximum is a measure rather than a layout width, because this primitive exists for content that is read and line length is a function of the font, not of the viewport.

src/layout/center.css, adds 950 B gzip

Centred on its own intrinsic width.

auto
Show codeHide code

--intrinsic centres children on their own width, --full and --viewport centre on both axes. Centring is a visual arrangement only and never changes DOM order.

Spacer

An empty flex item that absorbs the free space in a stack, cluster or sidebar, pushing what follows to the far end. Reach for it when the split point is data driven, so space-between is wrong the moment an item is absent or a third appears.

src/layout/spacer.css, adds 923 B gzip
Region breakdown
auto
Show codeHide code

The element stays empty. It carries no text, no icon and no role, so it contributes nothing to the accessibility tree and needs no aria-hidden. Only inside a flex parent: in a grid an empty cell consumes a track rather than the free space.