Application

Shell

One .shell, seven attributes. The sidebar is a container: narrow it and it becomes a rail by itself; leave it out and its column goes too.

Examples

Default

Rail · data-rail

Header · data-header

main

Inspector · data-inspector

canvas

Trailing · data-side="end"

Floating · data-float

Editor · no sidebar, data-header data-inspector data-float data-fill

No sidebar, so no side column. The main column is exactly as tall as the viewport leaves it, its stage takes what the other rows leave, and the inspector's tabs stay put while its body scrolls.
1600 × 900 · 16:9

Anatomy

.shellFull-height grid (at least 100dvh) that places its children by class. Under 48rem it stops being a grid: everything stacks in source order.
> header.navbarOptional, with data-header: spans the top row across every column and, from 48rem, is exactly --header tall, so the sticky panels line up under it. Below that it grows if its links wrap.
> nav.sidebarOptional. The sidebar column; see Sidebar. Sticky, scrolls on its own, and switches to its rail layout when the column is narrower than 7rem. Under 48rem it becomes a horizontally scrolling row. Without one the shell drops the side column and its area: header, main and inspector need no --sidebar: 0.
> mainThe working column. A <main> or any element with .main; it may shrink below its content width. With data-fill it is a column exactly as tall as the viewport leaves it.
main > [data-grow]With data-fill: the child that takes the height the others leave, such as a .stage.
> aside.inspectorOptional, with data-inspector: the trailing properties column. Sticky and scrolling like the sidebar, with wider padding and a hairline on its start edge. Under 48rem it drops below the main column.
.inspector > header, .inspector > .tabsOptional header part, as the first child: it stays put while what follows scrolls. Each child after it scrolls on its own, so give it one body, or tab panels of which one is shown.

API

AttributeValuesDescription
data-railon .shellbooleanNarrows the sidebar column to --rail. The sidebar notices its own width: headings and group summaries hide, labels drop under the icons.
data-collapsibleon .shellbooleanFull sidebar from 64rem, a rail below that. No script: it follows the viewport width.
data-headeron .shellbooleanAdds a top row that a .navbar child spans across every column. The navbar is made exactly --header tall from 48rem, so nothing needs measuring.
--headeron .shell<length>Height of the header row and of its navbar (from 48rem; below, the navbar's minimum). The sidebar and inspector stick this far from the top and shorten by the same amount. With data-header φ^2.5 ≈ 3.3 units (--ae-unit, so it follows the density), else 0px.
data-inspectoron .shellbooleanAdds a third, trailing column for an .inspector, --inspector wide. Combines with data-header.
data-sideon .shellendMoves the sidebar to the trailing side and its hairline to the start edge. Default start. With data-inspector, the inspector moves to the start.
data-floaton .shellbooleanInsets the shell by a step on every side and floats the sidebar and the inspector as glass panels with shadow and frost edge. With data-header they stick a step below the header. Under 48rem they lie flat again.
data-fillon .shellbooleanMakes the shell exactly one viewport tall, for editors: nothing scrolls the page. Main becomes a column of the height the header, padding and gaps leave (scrolling if its content needs more), and its [data-grow] child takes what the other children leave. The side panels scroll inside their rows. Under 48rem the shell stacks and the page scrolls as usual.
data-growon main > booleanWith data-fill: grows to fill the main column. It shrinks only as far as its flex minimum: a scrolling element to nothing, a .stage to its φ⁵ ≈ 11rem, anything else to its content, after which main scrolls.
--sidebaron .shell<length>Sidebar column width. Default φ⁶ ≈ 18rem.
--railon .shell<length>Sidebar width with data-rail or a collapsed data-collapsible. Default φ³ ≈ 4.2rem; keep it under 7rem or the sidebar keeps its full layout.
--inspectoron .shell<length>Inspector column width. Default φ⁶ ≈ 18rem.

Accessibility

  • The working column should be the <main> landmark, and a .skip-link pointing at it the first thing in <body>, so keyboard users can pass the sidebar.
  • With a .navbar and a nav.sidebar there are two <nav> landmarks: give each an aria-label ("Primary", "Workspace"). Give an aside.inspector one too.
  • Columns are placed by grid area, so source order is free; under 48rem the shell stacks in source order. With data-side="end" put the sidebar after <main> in the markup if it should also be read after it.
  • With data-fill the page itself never scrolls from 48rem up: main and the side panels scroll inside. Not every browser lets the keyboard into a scroller that holds nothing focusable (Safari doesn't), so give such a scroller tabindex="0" (a tab panel without controls, as in the demo), and make <main tabindex="-1"> the skip link's target. A .stage scales its media down instead of clipping it.
  • From 48rem the navbar is exactly --header tall; below that it is at least that tall and grows when its links wrap, so zoomed and reflowed text stays inside it.
  • In the rail the labels are still there, only smaller (or visually hidden with the sidebar's data-labels="hidden"): keep each label in its <span> so the link has a name, and hide glyph icons with aria-hidden="true".
Jump to
ProportionFoundations
MaterialFoundations
PresetsFoundations
ShellLayouts · Application
Container and stacksLayouts · Pages
SplitLayouts · Pages
Grid, bento, masonryLayouts · Pages
Switcher and rowLayouts · Pages
↑↓Move↵OpenescClose