Layout Components (`layouts/layout.css`)
This file provides a comprehensive library of declarative, property-driven layout components that integrate deeply with the framework's design token system. The
This file provides a comprehensive library of declarative, property-driven layout components that integrate deeply with the framework’s design token system. These components use custom HTML elements for semantic, maintainable layouts.
Start with these three
Most pages can begin with layout-stack for vertical rhythm, layout-cluster for wrapping groups, and layout-grid for responsive columns.
The everyday layout toolkit
Resize the page to watch the grid and cluster adapt.
<layout-stack gap="var(--space-md)">
<div class="example-panel">
<layout-cluster gap="var(--space-sm)" justify="space-between">
<strong>Project activity</strong>
<button type="button">Export</button>
</layout-cluster>
</div>
<layout-grid min-item-size="10rem" gap="var(--space-sm)">
<div class="example-panel">
<strong>24</strong>
<br/>
<small>Open tasks</small>
</div>
<div class="example-panel">
<strong>8</strong>
<br/>
<small>In review</small>
</div>
<div class="example-panel">
<strong>17</strong>
<br/>
<small>Completed</small>
</div>
</layout-grid>
</layout-stack> Open tasks
In review
Completed
<layout-stack gap="var(--space-md)"> <layout-cluster gap="var(--space-sm)" justify="space-between"> <strong>Project activity</strong> <button type="button">Export</button> </layout-cluster>
<layout-grid min-item-size="10rem" gap="var(--space-sm)"> <section>Open tasks</section> <section>In review</section> <section>Completed</section> </layout-grid></layout-stack>The primary layouts support custom-element, class, and data-* hosts. For example, <layout-stack>, .layout-stack, and [data-layout-stack] share the same behavior. Stack children stretch to the available width by default; add .stack-intrinsic to a child that should keep its content width.
Philosophy
- Declarative & Semantic: Use
<layout-grid>instead of classes like.l-grid - Property-Driven: Core logic controlled by CSS Custom Properties (e.g.,
--l-gap) that can be overridden via HTML attributes - Token-Integrated: Default values pulled from global design tokens defined in
core/base.css - Composable & Context-Aware: Designed for nesting, with opt-in container-query hosts when descendants need them
Global Defaults
All layout components inherit these default CSS custom properties:
--l-gap: var(--space-md, 1.5rem);--l-breakpoint: var(--bp-sm, 30em);--l-padding: var(--space-md, 1.5rem);--l-gutters: 1.5rem; /* Side margins for centering layouts */--l-max-width: 65ch; /* Maximum width for readable content */These can be overridden globally or per-component.
Base Behaviors
All layout components ([class^="layout-"]) share these base styles:
display: block- Prevent child overflow with
min-width: 0on direct children
Layouts size from their children by default, including when nested inside flex and grid containers. Add container-query, data-container-query, or the .layout-container class when descendants use the named container helpers:
<layout-stack container-query> <div class="hide-on-narrow">Hidden below 30rem</div></layout-stack>This opts into container-type: inline-size and names the container layout-container. Override the name with --layout-container-name.
Grid-Based Layouts
<layout-grid>
A responsive grid that automatically fits columns based on minimum item size.
Auto-fitting grid
Each item stays at least 9rem wide, then the grid wraps.
<layout-grid min-item-size="9rem" gap="var(--space-sm)">
<div class="example-panel">Alpha</div>
<div class="example-panel">Beta</div>
<div class="example-panel">Gamma</div>
<div class="example-panel">Delta</div>
</layout-grid> CSS Properties:
--l-min-item-size: Minimum size for each item (default: 16rem)--l-gap: Gap between items
HTML Attributes:
min-item-size: Override minimum item sizegap: Override gap
Usage:
<layout-grid min-item-size="12rem" gap="1rem"> <div>Item 1</div> <div>Item 2</div> <div>Item 3</div></layout-grid><layout-split>
A two-column layout that splits at a breakpoint, stacking on smaller screens.
Split content and supporting detail
Resize the example below 30rem to see the two regions stack.
<layout-split fraction="2fr" gap="var(--space-sm)">
<section class="example-panel">
<strong>Article</strong>
<p>Main content keeps the larger fraction.</p>
</section>
<aside class="example-panel">
<strong>Aside</strong>
<p>Supporting context stacks when narrow.</p>
</aside>
</layout-split> Main content keeps the larger fraction.
CSS Properties:
--l-fraction: Ratio for first column (default: 1fr)--l-breakpoint: Container width to split at--l-gap: Gap between columns
HTML Attributes:
fraction: Override column ratiobreakpoint: Override breakpointgap: Override gapforce-stack: Force stacked layoutno-stack: Prevent stacking
Usage:
<layout-split fraction="2fr" breakpoint="40em"> <div>Content</div> <div>Sidebar</div></layout-split>Flexbox-Based Layouts
<layout-stack>
A vertical stack with consistent spacing.
Children stretch to the available width by default. Add .stack-intrinsic when a button or other child should keep its natural width.
Stack with an intrinsic action
<layout-stack gap="var(--space-sm)">
<h3 style="margin: 0">Invite your team</h3>
<p style="margin: 0">Send a reusable link or invite people individually.</p>
<button class="stack-intrinsic" type="button">Create invite link</button>
</layout-stack> Invite your team
Send a reusable link or invite people individually.
CSS Properties:
--l-gap: Gap between items--l-align: Alignment (default: stretch)
HTML Attributes:
gap: Override gapalign: Override alignment (stretch, center, start, end)
Usage:
<layout-stack gap="2rem" align="center"> <div>Item 1</div> <div>Item 2</div></layout-stack><layout-cluster>
Groups items that wrap onto new lines, useful for tags or buttons.
Wrapping action cluster
The cluster wraps buttons while preserving their intrinsic widths.
<layout-cluster gap="var(--space-sm)" justify="space-between">
<button type="button">Save draft</button>
<button type="button">Preview</button>
<button type="button">Publish changes</button>
</layout-cluster> CSS Properties:
--l-gap: Gap between items (default: var(—space-sm, 0.75rem))--l-justify: Justification (default: flex-start)--l-align: Alignment (default: center)
HTML Attributes:
gap: Override gapjustify: Override justificationalign: Override alignment
Usage:
<layout-cluster gap="0.5rem" justify="center"> <button>Tag 1</button> <button>Tag 2</button> <button>Tag 3</button></layout-cluster><layout-reel>
A horizontally scrolling container. Set an item size so the overflow is
intentional, keep the native scrollbar visible by default, and add tabindex="0"
when the reel itself should be keyboard-scrollable.
Visible scrollbar and themed track
Every card has a fixed track size, so the row reliably overflows. Drag the scrollbar, shift-wheel, swipe, or focus the reel and use the arrow keys.
<layout-reel gap="var(--space-sm)" item-size="13rem" scroll-padding="var(--space-sm)" tabindex="0" aria-label="Release highlights" style="--l-reel-scrollbar-track: var(--surface-subtle); --l-reel-scrollbar-thumb: var(--accent);">
<article class="example-panel">
<eyebrow>Foundation</eyebrow>
<strong>Semantic defaults</strong>
<p>Native elements inherit the same token system.</p>
</article>
<article class="example-panel">
<eyebrow>Components</eyebrow>
<strong>Three host forms</strong>
<p>Use custom tags, classes, or data attributes.</p>
</article>
<article class="example-panel">
<eyebrow>Layout</eyebrow>
<strong>Container aware</strong>
<p>Components adapt to the space they actually receive.</p>
</article>
<article class="example-panel">
<eyebrow>Platform</eyebrow>
<strong>Progressive first</strong>
<p>Modern features enhance a resilient baseline.</p>
</article>
<article class="example-panel">
<eyebrow>Theming</eyebrow>
<strong>Token controlled</strong>
<p>Change intent once instead of patching selectors.</p>
</article>
</layout-reel> Native elements inherit the same token system.
Use custom tags, classes, or data attributes.
Components adapt to the space they actually receive.
Modern features enhance a resilient baseline.
Change intent once instead of patching selectors.
Snapping reel with a hidden scrollbar
Start with the native element that already carries the right meaning.
Apply the custom tag, data attribute, or matching class.
Override the smallest semantic variable that expresses your intent.
Layer in snapping or modern platform behavior without losing the baseline.
CSS Properties:
--l-gap: Gap between items--l-reel-item-size: Shared flex basis for direct children--l-reel-scroll-padding: Logical inline scroll padding, especially useful with snapping--l-reel-scrollbar-thumb: Scrollbar thumb color--l-reel-scrollbar-thumb-hover: Hovered thumb color--l-reel-scrollbar-track: Scrollbar track color--l-reel-scrollbar-size: WebKit scrollbar track height--l-reel-scrollbar-size-thin: Track height for the thin variant
HTML Attributes:
gap: Override gapitem-size: Direct-child flex basis, such as14remorclamp(12rem, 75%, 20rem)scroll-padding: Logical inline scroll paddingscrollbar="auto|thin|hidden": Choose the native scrollbar presentationsnap: Opt into proximity snapping at each direct childno-scrollbar: Legacy alias forscrollbar="hidden"
Usage:
<layout-reel item-size="14rem" gap="1rem" scrollbar="thin" snap tabindex="0"> <div>Item 1</div> <div>Item 2</div> <div>Item 3</div></layout-reel><layout-switcher>
Switches from vertical stack to horizontal row when items have enough space.
Threshold-based switcher
The same two regions stack or sit side-by-side based on the container width.
<layout-switcher threshold="24rem" gap="var(--space-sm)">
<div class="example-panel">
<strong>Profile</strong>
<p>Account details and avatar.</p>
</div>
<div class="example-panel">
<strong>Notifications</strong>
<p>Email and push preferences.</p>
</div>
</layout-switcher> Account details and avatar.
Email and push preferences.
CSS Properties:
--l-threshold: Space threshold for switching (default: 30rem)--l-gap: Gap between items
HTML Attributes:
threshold: Override thresholdgap: Override gap
Usage:
<layout-switcher threshold="25rem"> <div>Title</div> <div>Description</div></layout-switcher>Wrapper & Centering Layouts
<layout-pad>
Adds consistent padding to content.
Logical padding controls
Change block and inline padding independently without writing directional rules.
<layout-pad padding-y="var(--space-lg)" padding-x="var(--space-md)" style="background: var(--surface-subtle)">
<div class="example-panel">
<strong>Padded content</strong>
<p>Block and inline space use the same token vocabulary.</p>
</div>
</layout-pad> Block and inline space use the same token vocabulary.
CSS Properties:
--l-padding: Padding value--l-padding-inline: Horizontal padding--l-padding-block: Vertical padding
HTML Attributes:
padding: Override paddingpadding-x: Override horizontal paddingpadding-y: Override vertical padding
Usage:
<layout-pad padding="2rem"> <p>Content with padding</p></layout-pad><layout-center>
Centers content horizontally with max-width for readability. The maximum width includes the component’s gutters, so it remains overflow-safe as a full-width child of layout-stack, grid, or flex layouts.
Readable centered measure
The max width controls line length while gutters protect the edges on small screens.
<layout-center max-width="34ch" gutters="var(--space-md)" and-text="true">
<h3>Readable by default</h3>
<p>Long-form content stays comfortable to scan instead of stretching across the entire preview.</p>
</layout-center> Readable by default
Long-form content stays comfortable to scan instead of stretching across the entire preview.
CSS Properties:
--l-max-width: Maximum width (default: 65ch)--l-gutters: Side padding (default: var(—space-md, 1.5rem))
HTML Attributes:
max-width: Override max widthgutters: Override guttersand-text: Also center text
Usage:
<layout-center max-width="50ch" and-text> <p>Centered content</p></layout-center><layout-inline-center>
Centers content horizontally without a max-width constraint, useful for full-width centering. Its gutters are included in its rendered width.
Full-width inline centering
Unlike layout-center, the content can use the full available width while its contents remain centered.
<layout-inline-center gutters="var(--space-md)" and-text="true" style="background: var(--surface-subtle)">
<div class="example-panel">
<strong>Centered action</strong>
<br/>
<button type="button">Continue</button>
</div>
</layout-inline-center> CSS Properties:
--l-gutters: Side padding (default: var(—space-md, 1.5rem))
HTML Attributes:
gutters: Override guttersand-text: Also center text
Usage:
<layout-inline-center gutters="2rem" and-text> <p>Centered content without width limit</p></layout-inline-center><layout-frame>
Creates responsive media containers with fixed aspect ratios.
Aspect-ratio frame
The frame reserves media space before the asset loads and keeps the ratio responsive.
<layout-frame ratio="4 / 3" style="background: linear-gradient(135deg, var(--accent), var(--secondary)); color: var(--text-on-accent)">
<div style="display:grid;place-items:center;block-size:100%">
<strong>4:3 media frame</strong>
</div>
</layout-frame> CSS Properties:
--l-aspect-ratio: Aspect ratio (default: 16 / 9)
HTML Attributes:
ratio: Override aspect ratio
Usage:
<layout-frame ratio="4/3"> <img src="image.jpg" alt="Framed image"></layout-frame>Complex Layout Patterns
<layout-sidebar>
Sidebar layout that stacks on small screens.
Content with a supporting sidebar
The main region grows to use the remaining space.
CSS Properties:
--l-breakpoint: Breakpoint for stacking--l-side-width: Sidebar width (default: 20rem)--l-content-min: Minimum content width (default: 50%)--l-gap: Gap between areas
HTML Attributes:
breakpoint: Override breakpointside-width: Override sidebar widthcontent-min-width: Override content min widthside: Sidebar position (“left” or “right”)gap: Override gap
Mark the supporting region with slot="aside", data-layout-sidebar-aside, or .layout-sidebar__aside. The data/class forms are useful in template systems that reserve or consume slot.
Usage:
<layout-sidebar side="right" side-width="15rem"> <main>Main content</main> <aside data-layout-sidebar-aside>Sidebar</aside></layout-sidebar><layout-page>
Standard page layout with header, main, and footer.
Page regions and slots
Header, main, and footer remain named landmarks while layout-page owns the vertical frame.
<layout-page min-height="18rem" gap="var(--space-sm)">
<header slot="header" class="example-panel">
<strong>Northstar</strong>
</header>
<main slot="main" class="example-panel">
<h3>Workspace</h3>
<p>Main content grows into the available frame.</p>
</main>
<footer slot="footer" class="example-panel">
<small>Updated just now</small>
</footer>
</layout-page> Workspace
Main content grows into the available frame.
CSS Properties:
--l-min-height: Minimum height (default: 100vh)--l-gap: Gap between sections
HTML Attributes:
min-height: Override min heightgap: Override gap
Usage:
<layout-page min-height="100vh"> <header slot="header">Header</header> <main slot="main">Main content</main> <footer slot="footer">Footer</footer></layout-page>Browser Support
- Modern browsers with CSS Grid and Flexbox support
- Container queries require recent browser versions
- Custom elements work in all modern browsers
- Fallbacks provided for older browsers where possible
Integration Notes
- Import this file into the components layer in
index.css - Add
container-query,data-container-query, or.layout-containeronly when descendants use named container-query helpers - Custom properties can be overridden globally in
core/theme.cssor per-component - Combine with other framework components for complete layouts
Related Files
core/base.css: Defines global design tokens used by these componentscore/theme.css: Theme-specific overridescomponents/: Other UI components that work well with these layouts