From: sarah@example.com · 2:14 PM
Thanks for pulling these together. I went through the flows and have a few comments on the onboarding path.
Layout patterns for authenticated application UIs. Each shell composes
.navbar, .sidebar / .sidebar-nav,
and content regions with persistent navigation.
Flex works in one dimension at a time. A flex column stacks regions vertically; a flex row places them side by side. To combine both -- a sidebar beside a header-plus-content column -- you nest a flex column inside a flex row. Flex is natural for layouts where regions stack in a clear hierarchy (header on top, then a row of sidebar + main below). Sidebar-width transitions are smooth because flex recalculates the remaining space as the sibling's width changes.
Grid works in two dimensions at once. A named
grid-template-areas string places each region
declaratively. A full-height sidebar is a region that spans multiple
rows (grid-row: 1 / -1). Responsive rearrangement is a
single grid-template swap at a breakpoint. Column-track
width transitions are less smooth than flex-child widths.
In practice: flex is a good default for simple header-above-row shells. Grid shines when a region spans rows or columns (full-height sidebar, three-column, subheader combinations). All the patterns on this page use grid because each has at least one region that crosses the nesting hierarchy.
Every demo lives inside <docs-resize>, so media
queries (viewport-bound) would report the full viewport width rather
than the preview's. The demos use container queries
(@container) with .cq-inline on a parent
so the queries resolve against the preview's own width. On a real
page, substitute @media queries for
@container, and the viewport-bound
.hide-above-* / .hide-below-* utilities
for .cq-hide-*.
The collapsible-sidebar and split demos include a small inline
<script> block that toggles a class on click.
Real code would wire that state to its app-layer (routing, a custom
element, localStorage persistence); the layout CSS is the reusable
part.
Header above, sidebar + main below. Two-row, two-column grid; header spans both columns.
Header above; sidebar hides below the sm breakpoint, hamburger appears.
<style>
.demo-dashboard { container-type: inline-size; }
.demo-dashboard-grid {
display: grid;
grid-template-columns: auto minmax(0, 1fr);
grid-template-rows: auto 1fr;
grid-template-areas:
"header header"
"sidebar main";
min-block-size: 22rem;
}
.demo-dashboard-grid > .navbar { grid-area: header; }
.demo-dashboard-grid > .sidebar {
grid-area: sidebar;
inline-size: auto;
--sidebar-width: 10rem;
}
.demo-dashboard-grid > main {
grid-area: main;
padding: var(--space-md);
}
@container (max-width: 36rem) {
.demo-dashboard-grid {
grid-template-columns: minmax(0, 1fr);
grid-template-areas:
"header"
"main";
}
}
</style>
<docs-resize>
<div class="demo-dashboard">
<div class="docs-page-preview demo-dashboard-grid">
<header class="navbar">
<div class="cluster is-between">
<div class="cluster is-tight">
<button class="button is-ghost as-icon cq-hide-above-sm" type="button" aria-label="Menu" disabled="">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
<line x1="3" y1="6" x2="21" y2="6"></line>
<line x1="3" y1="12" x2="21" y2="12"></line>
<line x1="3" y1="18" x2="21" y2="18"></line>
</svg>
</button>
<strong>App</strong>
</div>
<button class="button is-ghost as-icon" type="button" aria-label="Account" disabled="">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
<path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"></path>
<circle cx="12" cy="7" r="4"></circle>
</svg>
</button>
</div>
</header>
<aside class="sidebar cq-hide-below-sm">
<nav class="sidebar-nav" aria-label="Main">
<a class="sidebar-link" href="#" aria-current="page">Home</a>
<a class="sidebar-link" href="#">Users</a>
<a class="sidebar-link" href="#">Settings</a>
</nav>
</aside>
<main>
<h3>Dashboard</h3>
<p class="text-muted text--1">Header above; sidebar hides below the <code>sm</code> breakpoint, hamburger appears.</p>
</main>
</div>
</div>
</docs-resize>Sidebar spans both rows on the left; header and main sit in the second column.
<style>
.demo-fullsidebar { container-type: inline-size; }
.demo-fullsidebar-grid {
display: grid;
grid-template-columns: auto minmax(0, 1fr);
grid-template-rows: auto 1fr;
grid-template-areas:
"sidebar header"
"sidebar main";
min-block-size: 22rem;
}
.demo-fullsidebar-grid > .sidebar {
grid-area: sidebar;
inline-size: auto;
--sidebar-width: 10rem;
}
.demo-fullsidebar-grid > .navbar { grid-area: header; }
.demo-fullsidebar-grid > main {
grid-area: main;
padding: var(--space-md);
}
@container (max-width: 36rem) {
.demo-fullsidebar-grid {
grid-template-columns: minmax(0, 1fr);
grid-template-areas:
"header"
"main";
}
}
</style>
<docs-resize>
<div class="demo-fullsidebar">
<div class="docs-page-preview demo-fullsidebar-grid">
<aside class="sidebar cq-hide-below-sm">
<nav class="sidebar-nav" aria-label="Main">
<a class="sidebar-link" href="#" aria-current="page">Overview</a>
<a class="sidebar-link" href="#">Reports</a>
<a class="sidebar-link" href="#">Team</a>
<a class="sidebar-link" href="#">Billing</a>
</nav>
</aside>
<header class="navbar">
<div class="cluster is-between">
<div class="cluster is-tight">
<button class="button is-ghost as-icon cq-hide-above-sm" type="button" aria-label="Menu" disabled="">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
<line x1="3" y1="6" x2="21" y2="6"></line>
<line x1="3" y1="12" x2="21" y2="12"></line>
<line x1="3" y1="18" x2="21" y2="18"></line>
</svg>
</button>
<strong>Workspace</strong>
</div>
<button class="button is-ghost as-icon" type="button" aria-label="Account" disabled="">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
<path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"></path>
<circle cx="12" cy="7" r="4"></circle>
</svg>
</button>
</div>
</header>
<main>
<h3>Overview</h3>
<p class="text-muted text--1">Sidebar spans both rows at wide widths; collapses out of flow below <code>sm</code>.</p>
</main>
</div>
</div>
</docs-resize>Brio's own docs-shell shape. Full-height sidebar above lg; sidebar hides and a hamburger opens a drawer below that breakpoint.
Below sm the sidebar hides and a hamburger opens a drawer (inert here; wire to <brio-drawer>).
<style>
.demo-docsshell { container-type: inline-size; }
.demo-docsshell-grid {
display: grid;
grid-template-columns: auto minmax(0, 1fr);
grid-template-rows: auto 1fr;
grid-template-areas:
"sidebar header"
"sidebar main";
min-block-size: 22rem;
}
.demo-docsshell-grid > .sidebar {
grid-area: sidebar;
inline-size: auto;
--sidebar-width: 10rem;
}
.demo-docsshell-grid > .navbar { grid-area: header; }
.demo-docsshell-grid > main {
grid-area: main;
padding: var(--space-md);
}
@container (max-width: 36rem) {
.demo-docsshell-grid {
grid-template-columns: minmax(0, 1fr);
grid-template-areas:
"header"
"main";
}
.demo-docsshell-grid > .navbar,
.demo-docsshell-grid > main { grid-column: 1; }
}
</style>
<docs-resize>
<div class="demo-docsshell">
<div class="docs-page-preview demo-docsshell-grid">
<aside class="sidebar cq-hide-below-sm">
<header>
<strong>Docs</strong>
</header>
<nav class="sidebar-nav" aria-label="Documentation">
<a class="sidebar-link" href="#" aria-current="page">Intro</a>
<a class="sidebar-link" href="#">Tokens</a>
<a class="sidebar-link" href="#">Components</a>
<a class="sidebar-link" href="#">Patterns</a>
</nav>
</aside>
<header class="navbar">
<div class="cluster is-between">
<div class="cluster is-tight">
<button class="button is-ghost as-icon cq-hide-above-sm" type="button" aria-label="Menu" disabled="">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
<line x1="3" y1="6" x2="21" y2="6"></line>
<line x1="3" y1="12" x2="21" y2="12"></line>
<line x1="3" y1="18" x2="21" y2="18"></line>
</svg>
</button>
<strong>Brio</strong>
</div>
</div>
</header>
<main>
<h3>Intro</h3>
<p class="text-muted text--1">Below <code>sm</code> the sidebar hides and a hamburger opens a drawer (inert here; wire to <code><brio-drawer></code>).</p>
</main>
</div>
</div>
</docs-resize>Dashboard shell with a toggle that collapses the sidebar to an icon-rail. Below the breakpoint, the sidebar hides entirely and the hamburger takes over.
Click the toggle in the sidebar header to switch to icon-only mode. Below sm, sidebar hides entirely.
<style>
.demo-collapse-dash { container-type: inline-size; }
.demo-collapse-dash-grid {
display: grid;
grid-template-columns: auto minmax(0, 1fr);
grid-template-rows: auto 1fr;
grid-template-areas:
"header header"
"sidebar main";
min-block-size: 22rem;
}
.demo-collapse-dash-grid > .navbar { grid-area: header; }
.demo-collapse-dash-grid > .sidebar {
grid-area: sidebar;
inline-size: auto;
--sidebar-width: 10rem;
transition: --sidebar-width var(--transition-normal);
}
.demo-collapse-dash-grid.is-collapsed > .sidebar {
--sidebar-width: 3rem;
}
.demo-collapse-dash-grid.is-collapsed .sidebar-label,
.demo-collapse-dash-grid.is-collapsed .sidebar-link-text { display: none; }
.demo-collapse-dash-grid > main {
grid-area: main;
padding: var(--space-md);
}
@container (max-width: 36rem) {
.demo-collapse-dash-grid {
grid-template-columns: minmax(0, 1fr);
grid-template-areas:
"header"
"main";
}
}
</style>
<docs-resize>
<div class="demo-collapse-dash">
<div class="docs-page-preview demo-collapse-dash-grid" id="collapse-dash">
<header class="navbar">
<div class="cluster is-between">
<div class="cluster is-tight">
<button class="button is-ghost as-icon cq-hide-above-sm" type="button" aria-label="Menu" disabled="">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
<line x1="3" y1="6" x2="21" y2="6"></line>
<line x1="3" y1="12" x2="21" y2="12"></line>
<line x1="3" y1="18" x2="21" y2="18"></line>
</svg>
</button>
<strong>App</strong>
</div>
<button class="button is-ghost as-icon" type="button" aria-label="Account" disabled="">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
<path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"></path>
<circle cx="12" cy="7" r="4"></circle>
</svg>
</button>
</div>
</header>
<aside class="sidebar cq-hide-below-sm">
<header>
<div class="cluster is-between">
<strong class="sidebar-label">Menu</strong>
<button class="button is-ghost is-sm as-icon" type="button" data-collapse-toggle="" aria-label="Toggle sidebar">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
<polyline points="11 17 6 12 11 7"></polyline>
<polyline points="18 17 13 12 18 7"></polyline>
</svg>
</button>
</div>
</header>
<nav class="sidebar-nav" aria-label="Main">
<a class="sidebar-link" href="#" aria-current="page" aria-label="Home">
<svg class="sidebar-link-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="m3 9 9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"></path></svg>
<span class="sidebar-link-text">Home</span>
</a>
<a class="sidebar-link" href="#" aria-label="Users">
<svg class="sidebar-link-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"></path><circle cx="12" cy="7" r="4"></circle></svg>
<span class="sidebar-link-text">Users</span>
</a>
<a class="sidebar-link" href="#" aria-label="Settings">
<svg class="sidebar-link-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="12" cy="12" r="3"></circle><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 1 1-2.83 2.83l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-4 0v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 1 1-2.83-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1 0-4h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 1 1 2.83-2.83l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 4 0v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 1 1 2.83 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 0 4h-.09a1.65 1.65 0 0 0-1.51 1z"></path></svg>
<span class="sidebar-link-text">Settings</span>
</a>
</nav>
</aside>
<main>
<h3>Dashboard</h3>
<p class="text-muted text--1">Click the toggle in the sidebar header to switch to icon-only mode. Below <code>sm</code>, sidebar hides entirely.</p>
</main>
</div>
</div>
</docs-resize>
<script>
(() => {
const preview = document.getElementById('collapse-dash');
if (!preview) return;
preview.addEventListener('click', (e) => {
if (e.target.closest('[data-collapse-toggle]')) {
e.preventDefault();
preview.classList.toggle('is-collapsed');
}
});
})();
</script>Same collapse behavior in a full-height-sidebar shell.
Same toggle behavior; sidebar spans both rows when expanded.
<style>
.demo-collapse-full { container-type: inline-size; }
.demo-collapse-full-grid {
display: grid;
grid-template-columns: auto minmax(0, 1fr);
grid-template-rows: auto 1fr;
grid-template-areas:
"sidebar header"
"sidebar main";
min-block-size: 22rem;
}
.demo-collapse-full-grid > .sidebar {
grid-area: sidebar;
inline-size: auto;
--sidebar-width: 10rem;
transition: --sidebar-width var(--transition-normal);
}
.demo-collapse-full-grid.is-collapsed > .sidebar {
--sidebar-width: 3rem;
}
.demo-collapse-full-grid.is-collapsed .sidebar-label,
.demo-collapse-full-grid.is-collapsed .sidebar-link-text { display: none; }
.demo-collapse-full-grid > .navbar { grid-area: header; }
.demo-collapse-full-grid > main {
grid-area: main;
padding: var(--space-md);
}
@container (max-width: 36rem) {
.demo-collapse-full-grid {
grid-template-columns: minmax(0, 1fr);
grid-template-areas:
"header"
"main";
}
}
</style>
<docs-resize>
<div class="demo-collapse-full">
<div class="docs-page-preview demo-collapse-full-grid" id="collapse-full">
<aside class="sidebar cq-hide-below-sm">
<header>
<div class="cluster is-between">
<strong class="sidebar-label">Workspace</strong>
<button class="button is-ghost is-sm as-icon" type="button" data-collapse-toggle="" aria-label="Toggle sidebar">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
<polyline points="11 17 6 12 11 7"></polyline>
<polyline points="18 17 13 12 18 7"></polyline>
</svg>
</button>
</div>
</header>
<nav class="sidebar-nav" aria-label="Main">
<a class="sidebar-link" href="#" aria-current="page" aria-label="Overview">
<svg class="sidebar-link-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect width="7" height="9" x="3" y="3" rx="1"></rect><rect width="7" height="5" x="14" y="3" rx="1"></rect><rect width="7" height="9" x="14" y="12" rx="1"></rect><rect width="7" height="5" x="3" y="16" rx="1"></rect></svg>
<span class="sidebar-link-text">Overview</span>
</a>
<a class="sidebar-link" href="#" aria-label="Reports">
<svg class="sidebar-link-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 3v18h18"></path><path d="M7 17l4-8 4 4 6-6"></path></svg>
<span class="sidebar-link-text">Reports</span>
</a>
<a class="sidebar-link" href="#" aria-label="Team">
<svg class="sidebar-link-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2"></path><circle cx="9" cy="7" r="4"></circle><path d="M22 21v-2a4 4 0 0 0-3-3.87"></path><path d="M16 3.13a4 4 0 0 1 0 7.75"></path></svg>
<span class="sidebar-link-text">Team</span>
</a>
</nav>
</aside>
<header class="navbar">
<div class="cluster is-between">
<div class="cluster is-tight">
<button class="button is-ghost as-icon cq-hide-above-sm" type="button" aria-label="Menu" disabled="">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
<line x1="3" y1="6" x2="21" y2="6"></line>
<line x1="3" y1="12" x2="21" y2="12"></line>
<line x1="3" y1="18" x2="21" y2="18"></line>
</svg>
</button>
<strong>Workspace</strong>
</div>
<button class="button is-ghost as-icon" type="button" aria-label="Account" disabled="">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
<path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"></path>
<circle cx="12" cy="7" r="4"></circle>
</svg>
</button>
</div>
</header>
<main>
<h3>Overview</h3>
<p class="text-muted text--1">Same toggle behavior; sidebar spans both rows when expanded.</p>
</main>
</div>
</div>
</docs-resize>
<script>
(() => {
const preview = document.getElementById('collapse-full');
if (!preview) return;
preview.addEventListener('click', (e) => {
if (e.target.closest('[data-collapse-toggle]')) {
e.preventDefault();
preview.classList.toggle('is-collapsed');
}
});
})();
</script>Sidebar + main + aside. Aside collapses first as width narrows; sidebar collapses next.
Aside collapses first (below md); a filter button appears on the right. Sidebar collapses next (below sm); hamburger appears on the left. In real code, wire both to drawers.
<style>
.demo-threecol { container-type: inline-size; }
.demo-threecol-grid {
display: grid;
grid-template-columns: auto minmax(0, 1fr) auto;
grid-template-rows: auto 1fr;
grid-template-areas:
"sidebar header header"
"sidebar main aside";
min-block-size: 22rem;
}
.demo-threecol-grid > .sidebar {
grid-area: sidebar;
inline-size: auto;
--sidebar-width: 9rem;
}
.demo-threecol-grid > .navbar { grid-area: header; }
.demo-threecol-grid > main {
grid-area: main;
padding: var(--space-md);
}
.demo-threecol-grid > .demo-aside {
grid-area: aside;
inline-size: 9rem;
background: var(--color-surface-sunken);
border-inline-start: 1px solid var(--color-border);
padding: var(--space-md);
}
@container (max-width: 42rem) {
.demo-threecol-grid {
grid-template-columns: auto minmax(0, 1fr);
grid-template-areas:
"sidebar header"
"sidebar main";
}
.demo-threecol-grid > .demo-aside { display: none; }
}
@container (max-width: 30rem) {
.demo-threecol-grid {
grid-template-columns: minmax(0, 1fr);
grid-template-areas:
"header"
"main";
}
}
</style>
<docs-resize>
<div class="demo-threecol">
<div class="docs-page-preview demo-threecol-grid">
<aside class="sidebar cq-hide-below-sm">
<nav class="sidebar-nav" aria-label="Main">
<a class="sidebar-link" href="#" aria-current="page">Inbox</a>
<a class="sidebar-link" href="#">Sent</a>
<a class="sidebar-link" href="#">Drafts</a>
</nav>
</aside>
<header class="navbar">
<div class="cluster is-between">
<div class="cluster is-tight">
<button class="button is-ghost as-icon cq-hide-above-sm" type="button" aria-label="Menu" disabled="">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
<line x1="3" y1="6" x2="21" y2="6"></line>
<line x1="3" y1="12" x2="21" y2="12"></line>
<line x1="3" y1="18" x2="21" y2="18"></line>
</svg>
</button>
<strong>Mail</strong>
</div>
<button class="button is-ghost as-icon cq-hide-above-md" type="button" aria-label="Filters" disabled="">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
<polygon points="22 3 2 3 10 12.46 10 19 14 21 14 12.46 22 3"></polygon>
</svg>
</button>
</div>
</header>
<main>
<h3>Main</h3>
<p class="text-muted text--1">Aside collapses first (below <code>md</code>); a filter button appears on the right. Sidebar collapses next (below <code>sm</code>); hamburger appears on the left. In real code, wire both to drawers.</p>
</main>
<aside class="demo-aside">
<strong class="text--1">Filters</strong>
<nav class="sidebar-nav" aria-label="Filters">
<a class="sidebar-link" href="#">Unread</a>
<a class="sidebar-link" href="#">Starred</a>
<a class="sidebar-link" href="#">Attachments</a>
</nav>
</aside>
</div>
</div>
</docs-resize>List column on the left, detail column on the right. Below a threshold, the list fills the width and tapping an item opens the detail as an overlay with a back button.
From: sarah@example.com · 2:14 PM
Thanks for pulling these together. I went through the flows and have a few comments on the onboarding path.
<style>
.demo-split { container-type: inline-size; }
.demo-split-grid {
display: grid;
grid-template-columns: 14rem minmax(0, 1fr);
grid-template-areas: "list detail";
min-block-size: 22rem;
position: relative;
}
.demo-split-list {
grid-area: list;
border-inline-end: 1px solid var(--color-border);
overflow-y: auto;
padding: var(--space-sm);
}
.demo-split-detail {
grid-area: detail;
overflow-y: auto;
background: var(--color-surface);
}
.demo-split-detail-header {
display: flex;
align-items: center;
gap: var(--space-xs);
padding: var(--space-sm) var(--space-md);
border-block-end: 1px solid var(--color-border);
}
.demo-split-detail-body { padding: var(--space-md); }
.demo-split-back { display: none; }
.demo-split-items {
list-style: none;
margin: 0;
padding: 0;
display: flex;
flex-direction: column;
gap: var(--space-3xs);
}
.demo-split-item {
display: block;
padding: var(--space-2xs) var(--space-xs);
border-radius: var(--radius-sm);
color: var(--color-text);
text-decoration: none;
}
.demo-split-item:hover { background: var(--color-surface-sunken); }
.demo-split-item[aria-current="page"] {
background: var(--color-surface-sunken);
font-weight: 600;
}
.demo-split-item-meta {
display: block;
font-size: var(--step--1);
color: var(--color-text-muted);
}
@container (max-width: 32rem) {
.demo-split-grid {
grid-template-columns: minmax(0, 1fr);
grid-template-areas: "list";
}
.demo-split-list { border-inline-end: none; }
.demo-split-detail { display: none; }
.demo-split-grid.is-open .demo-split-detail {
display: block;
position: absolute;
inset: 0;
z-index: 1;
}
.demo-split-back { display: inline-flex; }
}
</style>
<docs-resize>
<div class="demo-split">
<div class="docs-page-preview demo-split-grid is-open" id="split-standalone">
<aside class="demo-split-list" aria-label="Messages">
<ul class="demo-split-items">
<li><a class="demo-split-item" aria-current="page" data-split-open="" href="#"><strong>Sarah Chen</strong><span class="demo-split-item-meta">Re: design review</span></a></li>
<li><a class="demo-split-item" data-split-open="" href="#"><strong>Build system</strong><span class="demo-split-item-meta">Deploy succeeded</span></a></li>
<li><a class="demo-split-item" data-split-open="" href="#"><strong>Alex Park</strong><span class="demo-split-item-meta">Quick question</span></a></li>
<li><a class="demo-split-item" data-split-open="" href="#"><strong>Team weekly</strong><span class="demo-split-item-meta">Agenda for Thursday</span></a></li>
</ul>
</aside>
<article class="demo-split-detail">
<header class="demo-split-detail-header">
<button class="button is-ghost as-icon demo-split-back" type="button" data-split-close="" aria-label="Back">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><polyline points="15 18 9 12 15 6"></polyline></svg>
</button>
<strong>Re: design review</strong>
</header>
<div class="demo-split-detail-body stack">
<p class="text-muted text--1"><strong>From:</strong> sarah@example.com · 2:14 PM</p>
<p>Thanks for pulling these together. I went through the flows and have a few comments on the onboarding path.</p>
</div>
</article>
</div>
</div>
</docs-resize>
<script>
(() => {
document.querySelectorAll('[data-split-open]').forEach(a => {
a.addEventListener('click', (e) => {
e.preventDefault();
a.closest('.docs-page-preview')?.classList.add('is-open');
});
});
document.querySelectorAll('[data-split-close]').forEach(btn => {
btn.addEventListener('click', (e) => {
e.preventDefault();
btn.closest('.docs-page-preview')?.classList.remove('is-open');
});
});
})();
</script>
The split pattern composes inside a dashboard shell. Two container-query
contexts do the work: the preview drives the outer dashboard's responsive;
main gets its own container-type so the split's
collapse threshold resolves against main's width, not the preview's.
From: sarah@example.com
Resize the preview to see both layers collapse: dashboard sidebar first, then the split to list-only.
<style>
.demo-dash-split { container-type: inline-size; }
.demo-dash-split-grid {
display: grid;
grid-template-columns: auto minmax(0, 1fr);
grid-template-rows: auto 1fr;
grid-template-areas:
"header header"
"sidebar main";
min-block-size: 26rem;
}
.demo-dash-split-grid > .navbar { grid-area: header; }
.demo-dash-split-grid > .sidebar {
grid-area: sidebar;
inline-size: auto;
--sidebar-width: 8rem;
}
.demo-dash-split-grid > main {
grid-area: main;
overflow: hidden;
container-type: inline-size;
position: relative;
}
@container (max-width: 42rem) {
.demo-dash-split-grid {
grid-template-columns: minmax(0, 1fr);
grid-template-areas:
"header"
"main";
}
}
.demo-dash-split-pane {
display: grid;
grid-template-columns: 12rem minmax(0, 1fr);
grid-template-areas: "list detail";
block-size: 100%;
position: relative;
}
.demo-dash-split-pane > .list {
grid-area: list;
border-inline-end: 1px solid var(--color-border);
overflow-y: auto;
padding: var(--space-sm);
}
.demo-dash-split-pane > .detail {
grid-area: detail;
overflow-y: auto;
background: var(--color-surface);
}
.demo-dash-split-pane .back { display: none; }
@container (max-width: 28rem) {
.demo-dash-split-pane {
grid-template-columns: minmax(0, 1fr);
grid-template-areas: "list";
}
.demo-dash-split-pane > .list { border-inline-end: none; }
.demo-dash-split-pane > .detail { display: none; }
.demo-dash-split-pane.is-open > .detail {
display: block;
position: absolute;
inset: 0;
z-index: 1;
}
.demo-dash-split-pane .back { display: inline-flex; }
}
</style>
<docs-resize>
<div class="demo-dash-split">
<div class="docs-page-preview demo-dash-split-grid">
<header class="navbar">
<div class="cluster is-between">
<div class="cluster is-tight">
<button class="button is-ghost as-icon cq-hide-above-md" type="button" aria-label="Menu" disabled="">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
<line x1="3" y1="6" x2="21" y2="6"></line>
<line x1="3" y1="12" x2="21" y2="12"></line>
<line x1="3" y1="18" x2="21" y2="18"></line>
</svg>
</button>
<strong>Inbox</strong>
</div>
</div>
</header>
<aside class="sidebar cq-hide-below-md">
<nav class="sidebar-nav" aria-label="Mail">
<a class="sidebar-link" href="#" aria-current="page">Inbox</a>
<a class="sidebar-link" href="#">Sent</a>
<a class="sidebar-link" href="#">Drafts</a>
<a class="sidebar-link" href="#">Archive</a>
</nav>
</aside>
<main>
<div class="demo-dash-split-pane is-open" id="split-in-dash">
<aside class="list" aria-label="Messages">
<ul class="demo-split-items">
<li><a class="demo-split-item" aria-current="page" data-split-open="" href="#"><strong>Sarah Chen</strong><span class="demo-split-item-meta">Re: design review</span></a></li>
<li><a class="demo-split-item" data-split-open="" href="#"><strong>Build system</strong><span class="demo-split-item-meta">Deploy succeeded</span></a></li>
<li><a class="demo-split-item" data-split-open="" href="#"><strong>Alex Park</strong><span class="demo-split-item-meta">Quick question</span></a></li>
</ul>
</aside>
<article class="detail">
<header class="demo-split-detail-header">
<button class="button is-ghost as-icon back" type="button" data-split-close="" aria-label="Back">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><polyline points="15 18 9 12 15 6"></polyline></svg>
</button>
<strong>Re: design review</strong>
</header>
<div class="demo-split-detail-body stack">
<p class="text-muted text--1"><strong>From:</strong> sarah@example.com</p>
<p>Resize the preview to see both layers collapse: dashboard sidebar first, then the split to list-only.</p>
</div>
</article>
</div>
</main>
</div>
</div>
</docs-resize>
<script>
(() => {
const pane = document.getElementById('split-in-dash');
if (!pane) return;
pane.addEventListener('click', (e) => {
if (e.target.closest('[data-split-open]')) {
e.preventDefault();
pane.classList.add('is-open');
} else if (e.target.closest('[data-split-close]')) {
e.preventDefault();
pane.classList.remove('is-open');
}
});
})();
</script>