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
main
Rail · data-rail
main
Header · data-header
main
Inspector · data-inspector
canvas
Trailing · data-side="end"
main
Floating · data-float
main
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.Untitled
1600 × 900 · 16:9
Anatomy
.shell | Full-height grid (at least 100dvh) that places its children by class. Under 48rem it stops being a grid: everything stacks in source order. |
> header.navbar | Optional, 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.sidebar | Optional. 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. |
> main | The 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.inspector | Optional, 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 > .tabs | Optional 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
| Attribute | Values | Description |
|---|---|---|
data-railon .shell | boolean | Narrows the sidebar column to --rail. The sidebar notices its own width: headings and group summaries hide, labels drop under the icons. |
data-collapsibleon .shell | boolean | Full sidebar from 64rem, a rail below that. No script: it follows the viewport width. |
data-headeron .shell | boolean | Adds 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 .shell | boolean | Adds a third, trailing column for an .inspector, --inspector wide. Combines with data-header. |
data-sideon .shell | end | Moves 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 .shell | boolean | Insets 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 .shell | boolean | Makes 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 > | boolean | With 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-linkpointing at it the first thing in<body>, so keyboard users can pass the sidebar. - With a
.navbarand anav.sidebarthere are two<nav>landmarks: give each anaria-label("Primary", "Workspace"). Give anaside.inspectorone 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-fillthe 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 scrollertabindex="0"(a tab panel without controls, as in the demo), and make<main tabindex="-1">the skip link's target. A.stagescales its media down instead of clipping it. - From 48rem the navbar is exactly
--headertall; 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 witharia-hidden="true".