- Page
- Overview
- Examples
- Layout Anatomy
- Basic Layout
- Sidebar
- Separate Desktop and Mobile Sidebar
- Banner, Header, and Subheader
- Aside
- Custom Background
- Custom Column Widths
- Content Gutters
- Content Width
- Sidebar Placement
- Contained Footer
- Disable Sticky Sections
- Collapsible Desktop Sidebar
- Utility Classes
- Skip to Content
- Slots
- Properties
- Methods
- Custom Properties
- Parts
- Dependencies
Page
<sl-page> | SlPage
Pages scaffold an entire application layout with header, sidebar, aside, main content, and footer regions. Use them to structure full pages with minimal markup and responsive behavior built in.
Overview
The page component provides a full application layout with banner, header, subheader, sidebar, main content, aside, and footer regions. Sidebar collapses into a drawer on mobile viewports automatically.
Main Content
This example uses every available slot. The default slot accepts multiple elements.
<style> #overview-page::part(sidebar) { background: white; } #overview-page::part(main) { background: white; } #overview-page::part(aside) { background: white; } </style> <sl-page id="overview-page" style="--sidebar-width: 220px; --aside-width: 180px;"> <span slot="skip-to-content">Skip to main content</span> <div slot="banner" style="background: var(--sl-color-orange-100); text-align: center; font-size: var(--hds-font-size-body-sm); padding: var(--hds-space-1x) var(--hds-space-5x);"> 🔔 System maintenance scheduled this weekend. </div> <sl-app-header-hds slot="header"> <sl-icon-button slot="app-switcher" name="bars" data-toggle-sidebar label="Toggle sidebar"></sl-icon-button> <span slot="app-name">My Application</span> <sl-icon-button slot="action" name="bell" label="Notifications"></sl-icon-button> <sl-avatar slot="avatar" initials="JD" label="Jane Doe"></sl-avatar> <div slot="subheader-left">Home / Dashboard / Overview</div> </sl-app-header-hds> <div slot="mobile-sidebar-header">Menu</div> <div slot="mobile-sidebar-footer" style="font-size: var(--hds-font-size-body-sm); color: var(--sl-color-neutral-500);">v1.0.0</div> <div slot="sidebar"> <div style="font-weight: var(--hds-font-application-weight-strong); margin-bottom: var(--hds-space-2x);">Menu</div> <div>Dashboard</div> <div>Accounts</div> <div>Reports</div> <div>Settings</div> </div> <main> <h2>Main Content</h2> <p>This example uses every available slot. The default slot accepts multiple elements.</p> </main> <div slot="aside"> <strong>On this page</strong> <div>Overview</div> <div>Details</div> </div> <div slot="footer" style="background: var(--sl-color-neutral-100); text-align: center;"> © 2026 My Company </div> </sl-page>
Examples
Layout Anatomy
Each slot is colour-coded so you can see exactly which region it occupies in the layout.
<style> #layout-anatomy::part(banner) { background: var(--hds-color-yellow-100); } #layout-anatomy::part(header) { background: var(--hds-color-lavender-100); } #layout-anatomy::part(subheader) { background: var(--hds-color-blue-100); } #layout-anatomy::part(sidebar) { background: var(--hds-color-pink-100); } #layout-anatomy::part(main) { background-color: var(--hds-color-fuschia-100); } #layout-anatomy::part(aside) { background: var(--hds-color-pink-100); } #layout-anatomy::part(footer) { background: var(--hds-color-lavender-100); } </style> <sl-page id="layout-anatomy" style="--sidebar-width: 200px; --aside-width: 160px;"> <div slot="banner" style="text-align: center; padding: var(--hds-space-1x) var(--hds-space-5x);"> banner </div> <div slot="header" style="text-align: center; padding: var(--hds-space-5x);"> header </div> <div slot="subheader" style="text-align: center; padding: var(--hds-space-2x) var(--hds-space-5x);"> subheader </div> <div slot="sidebar" style="padding: var(--hds-space-4x);"> sidebar </div> <div style="padding: var(--hds-space-4x);"> main </div> <div slot="aside" style="padding: var(--hds-space-4x);"> aside </div> <div slot="footer" style="text-align: center; padding: var(--hds-space-2x) var(--hds-space-5x);"> footer </div> </sl-page>
Basic Layout
Content placed in the default slot appears in the main content area.
Main content goes in the default slot.
<style> #basic-layout::part(banner) { background: var(--hds-color-yellow-100); } #basic-layout::part(header) { background: var(--hds-color-lavender-100); } #basic-layout::part(subheader) { background: var(--hds-color-blue-100); } #basic-layout::part(sidebar) { background: var(--hds-color-pink-100); } #basic-layout::part(main) { background-color: var(--hds-color-fuschia-100); } #basic-layout::part(aside) { background: var(--hds-color-pink-100); } #basic-layout::part(footer) { background: var(--hds-color-lavender-100); } </style> <sl-page id="basic-layout"> <div slot="header" style="height: 64px; display: flex; align-items: center; padding: 0 var(--hds-space-5x); background: var(--sl-color-neutral-900); color: white; font-weight: var(--hds-font-application-weight-strong);"> My Application </div> <div> <p>Main content goes in the default slot.</p> </div> </sl-page>
Sidebar
Use the sidebar slot to add a sidebar. On desktop it appears inline; on mobile it moves
into a drawer. Add data-toggle-sidebar to any element to toggle the drawer — the element
is automatically hidden on desktop.
Dashboard
Welcome to the dashboard. Resize below 990 px to see the sidebar collapse into a drawer.
<style> #sidebar-example::part(banner) { background: var(--hds-color-yellow-100); } #sidebar-example::part(header) { background: var(--hds-color-lavender-100); } #sidebar-example::part(subheader) { background: var(--hds-color-blue-100); } #sidebar-example::part(sidebar) { background: var(--hds-color-pink-100); } #sidebar-example::part(main) { background-color: var(--hds-color-fuschia-100); } #sidebar-example::part(aside) { background: var(--hds-color-pink-100); } #sidebar-example::part(footer) { background: var(--hds-color-lavender-100); } </style> <sl-page id="sidebar-example" style="--sidebar-width: 220px;"> <sl-app-header-hds slot="header" hide-subheader> <sl-icon-button slot="app-switcher" name="bars" data-toggle-sidebar label="Toggle sidebar"></sl-icon-button> <span slot="app-name">App with Sidebar</span> </sl-app-header-hds> <div slot="sidebar"> <div style="font-weight: var(--hds-font-application-weight-strong); margin-bottom: var(--hds-space-2x);">Menu</div> <div>Dashboard</div> <div>Accounts</div> <div>Reports</div> <div>Settings</div> </div> <div> <h2>Dashboard</h2> <p>Welcome to the dashboard. Resize below <strong>990 px</strong> to see the sidebar collapse into a drawer.</p> </div> </sl-page>
Separate Desktop and Mobile Sidebar
Use desktop-sidebar and mobile-sidebar to provide different content for each
viewport. The mobile-sidebar-header and mobile-sidebar-footer slots map to
the drawer’s header and footer.
Dashboard
Resize below 990 px to see the simplified mobile sidebar.
<style> #separate-sidebars::part(banner) { background: var(--hds-color-yellow-100); } #separate-sidebars::part(header) { background: var(--hds-color-lavender-100); } #separate-sidebars::part(subheader) { background: var(--hds-color-blue-100); } #separate-sidebars::part(sidebar) { background: var(--hds-color-pink-100); } #separate-sidebars::part(main) { background-color: var(--hds-color-fuschia-100); } #separate-sidebars::part(aside) { background: var(--hds-color-pink-100); } #separate-sidebars::part(footer) { background: var(--hds-color-lavender-100); } </style> <sl-page id="separate-sidebars" style="--sidebar-width: 220px;"> <sl-app-header-hds slot="header" hide-subheader> <sl-icon-button slot="app-switcher" name="bars" data-toggle-sidebar label="Toggle sidebar"></sl-icon-button> <span slot="app-name">Separate Sidebar</span> </sl-app-header-hds> <div slot="desktop-sidebar"> <div style="font-weight: var(--hds-font-application-weight-strong); margin-bottom: var(--hds-space-2x);">Desktop Sidebar</div> <div>Dashboard</div> <div>Analytics</div> <div>Reports</div> <div>Admin Panel</div> </div> <div slot="mobile-sidebar-header">Sidebar</div> <div slot="mobile-sidebar"> <div>Dashboard</div> <div>Reports</div> <div>Settings</div> </div> <div slot="mobile-sidebar-footer" style="font-size: var(--hds-font-size-body-sm); color: var(--sl-color-neutral-500);">Logged in as Admin</div> <div> <h2>Dashboard</h2> <p>Resize below <strong>990 px</strong> to see the simplified mobile sidebar.</p> </div> </sl-page>
Banner, Header, and Subheader
The banner slot sits above the header (announcements), the subheader sits
below (breadcrumbs). Both are sticky by default.
Page content with banner and breadcrumb subheader above.
<style> #banner-header-subheader::part(banner) { background: var(--hds-color-yellow-100); } #banner-header-subheader::part(header) { background: var(--hds-color-lavender-100); } #banner-header-subheader::part(subheader) { background: var(--hds-color-blue-100); } #banner-header-subheader::part(sidebar) { background: var(--hds-color-pink-100); } #banner-header-subheader::part(main) { background-color: var(--hds-color-fuschia-100); } #banner-header-subheader::part(aside) { background: var(--hds-color-pink-100); } #banner-header-subheader::part(footer) { background: var(--hds-color-lavender-100); } </style> <sl-page id="banner-header-subheader"> <div slot="banner" style="background: var(--sl-color-orange-100); text-align: center; padding: var(--hds-space-1x) var(--hds-space-5x);"> đź”” System maintenance scheduled for this weekend. </div> <div slot="header" style="height: 64px; display: flex; align-items: center; padding: 0 var(--hds-space-5x); background: var(--sl-color-neutral-900); color: white; font-weight: var(--hds-font-application-weight-strong);"> My Application </div> <div slot="subheader" style="padding: var(--hds-space-2x) var(--hds-space-5x); background: var(--sl-color-neutral-50); border-bottom: 1px solid var(--sl-color-neutral-200);"> Home / Dashboard / Overview </div> <div><p>Page content with banner and breadcrumb subheader above.</p></div> </sl-page>
Aside
The aside slot places supplementary content on the right. Control its width with
--aside-width. On mobile it is automatically hidden.
Components
Resize below 990 px to see the aside disappear on mobile.
<style> #aside-example::part(banner) { background: var(--hds-color-yellow-100); } #aside-example::part(header) { background: var(--hds-color-lavender-100); } #aside-example::part(subheader) { background: var(--hds-color-blue-100); } #aside-example::part(sidebar) { background: var(--hds-color-pink-100); } #aside-example::part(main) { background-color: var(--hds-color-fuschia-100); } #aside-example::part(aside) { background: var(--hds-color-pink-100); } #aside-example::part(footer) { background: var(--hds-color-lavender-100); } </style> <sl-page id="aside-example" style="--sidebar-width: 200px; --aside-width: 200px;"> <sl-app-header-hds slot="header" hide-subheader> <sl-icon-button slot="app-switcher" name="bars" data-toggle-sidebar label="Toggle sidebar"></sl-icon-button> <span slot="app-name">Documentation</span> </sl-app-header-hds> <div slot="sidebar"> <div>Getting Started</div> <div>Components</div> <div>Tokens</div> </div> <div> <h2>Components</h2> <p>Resize below <strong>990 px</strong> to see the aside disappear on mobile.</p> </div> <div slot="aside"> <strong>On this page</strong> <div>Overview</div> <div>Properties</div> <div>Slots</div> </div> </sl-page>
Custom Background
Use ::part() selectors to apply custom background colours to any page region. The default
background is --hds-color-bg-surface. Override ::part(base) to change the
page background.
Default background
White base background
<style> #custom-bg-white::part(base) { background: white; } </style> <div style="display: flex; gap: var(--hds-space-4x);"> <div style="flex: 1;"> <p><strong>Default background</strong></p> <sl-page id="custom-bg-default" style="--sidebar-width: 180px;"> <div slot="header" style="height: 48px; display: flex; align-items: center; padding: 0 var(--hds-space-4x); font-weight: var(--hds-font-application-weight-strong);"> My App </div> <div slot="sidebar" style="padding: var(--hds-space-4x);"> <div>Dashboard</div> <div>Reports</div> </div> <div style="padding: var(--hds-space-4x);">Main Content</div> </sl-page> </div> <div style="flex: 1;"> <p><strong>White base background</strong></p> <sl-page id="custom-bg-white" style="--sidebar-width: 180px;"> <div slot="header" style="height: 48px; display: flex; align-items: center; padding: 0 var(--hds-space-4x); font-weight: var(--hds-font-application-weight-strong);"> My App </div> <div slot="sidebar" style="padding: var(--hds-space-4x);"> <div>Dashboard</div> <div>Reports</div> </div> <div style="padding: var(--hds-space-4x);">Main Content</div> </sl-page> </div> </div>
Custom Column Widths
Use --sidebar-width, --main-width, and --aside-width to control
column sizing. Any valid CSS grid track value works. Empty columns collapse automatically.
<style> #custom-widths::part(banner) { background: var(--hds-color-yellow-100); } #custom-widths::part(header) { background: var(--hds-color-lavender-100); } #custom-widths::part(subheader) { background: var(--hds-color-blue-100); } #custom-widths::part(sidebar) { background: var(--hds-color-pink-100); } #custom-widths::part(main) { background-color: var(--hds-color-fuschia-100); } #custom-widths::part(aside) { background: var(--hds-color-pink-100); } #custom-widths::part(footer) { background: var(--hds-color-lavender-100); } </style> <sl-page id="custom-widths" style="--sidebar-width: 250px; --aside-width: 250px;"> <div slot="header" style="height: 64px; display: flex; align-items: center; padding: 0 var(--hds-space-5x); background: var(--sl-color-neutral-900); color: white; font-weight: var(--hds-font-application-weight-strong);"> Custom Widths </div> <div slot="sidebar" style="background: var(--sl-color-neutral-50);">250 px sidebar</div> <div>Flexible main content (1fr by default)</div> <div slot="aside" style="background: var(--sl-color-neutral-50);">250 px aside</div> </sl-page>
Content Gutters
The page insets the content area — the sidebar, main, and aside as a group — with responsive side
gutters that grow from 16 px to 64 px as the viewport widens (the banner and header stay full-width; a
contained footer insets with the content, while a full footer stays
full-width). Override the --content-gutter custom property to change them — set
0 to sit flush with the page edges, a single value for a constant gutter, or wrap it in
media queries for your own responsive ramp.
Gutters
Resize the preview to watch the gutters grow from 16 px to 64 px. Because the gutters are transparent, they show the page background on either side of the content.
<style> #main-gutters::part(header) { background: var(--hds-color-lavender-100); } #main-gutters::part(main) { background-color: var(--hds-color-fuschia-100); } </style> <sl-page id="main-gutters"> <div slot="header" style="height: 56px; display: flex; align-items: center; padding: 0 var(--hds-space-5x); background: var(--sl-color-neutral-900); color: white; font-weight: var(--hds-font-application-weight-strong);"> default — responsive gutters </div> <div> <h2>Gutters</h2> <p>Resize the preview to watch the gutters grow from 16 px to 64 px. Because the gutters are transparent, they show the page background on either side of the content.</p> </div> </sl-page>
Set --content-gutter: 0 for edge-to-edge content:
Flush
Setting --content-gutter: 0 makes the content fill the column edge-to-edge, with
no gutters.
<style> #main-no-gutter::part(header) { background: var(--hds-color-lavender-100); } #main-no-gutter::part(main) { background-color: var(--hds-color-fuschia-100); } #main-no-gutter { --content-gutter: 0; } </style> <sl-page id="main-no-gutter"> <div slot="header" style="height: 56px; display: flex; align-items: center; padding: 0 var(--hds-space-5x); background: var(--sl-color-neutral-900); color: white; font-weight: var(--hds-font-application-weight-strong);"> --content-gutter: 0 </div> <div> <h2>Flush</h2> <p>Setting <code>--content-gutter: 0</code> makes the content fill the column edge-to-edge, with no gutters.</p> </div> </sl-page>
Or drive your own responsive gutter with media queries:
Custom gutters
This page overrides --content-gutter with its own media query — 8 px below 990 px
and 48 px at and above. Resize the preview to see it switch.
<style> #main-custom-gutter::part(header) { background: var(--hds-color-lavender-100); } #main-custom-gutter::part(main) { background-color: var(--hds-color-fuschia-100); } /* 8px gutter on small screens, 48px from 990px up */ #main-custom-gutter { --content-gutter: 8px; } @media (min-width: 990px) { #main-custom-gutter { --content-gutter: 48px; } } </style> <sl-page id="main-custom-gutter"> <div slot="header" style="height: 56px; display: flex; align-items: center; padding: 0 var(--hds-space-5x); background: var(--sl-color-neutral-900); color: white; font-weight: var(--hds-font-application-weight-strong);"> --content-gutter: 8px → 48px </div> <div> <h2>Custom gutters</h2> <p>This page overrides <code>--content-gutter</code> with its own media query — 8 px below 990 px and 48 px at and above. Resize the preview to see it switch.</p> </div> </sl-page>
Content Width
The page scaffolds the layout regions; capping and positioning the content area is up to you. Style
the body part directly — it wraps the sidebar, main, and aside as a group — with a
max-width and margin-inline: auto to cap and centre the whole content
region. Styling the part (rather than your slotted markup) sizes the actual container box, so any
::part(body) background tracks the content too. Set
--content-gutter: 0 first so the container spans the full width, then cap and position it
with max-width and margin-inline. To cap only the main reading column
instead, style ::part(main) the same way.
Capped & centered
The content is capped at max-width: 720px and centered with
margin-inline: auto, keeping long-form text readable on wide screens.
<style> #main-capped::part(header) { background: var(--hds-color-lavender-100); } #main-capped::part(body) { background-color: var(--hds-color-fuschia-100); max-width: 720px; margin-inline: auto; --content-gutter: 0; } </style> <sl-page id="main-capped"> <div slot="header" style="height: 56px; display: flex; align-items: center; padding: 0 var(--hds-space-5x); background: var(--sl-color-neutral-900); color: white; font-weight: var(--hds-font-application-weight-strong);"> ::part(body) capped at 720px </div> <div> <h2>Capped & centered</h2> <p>The content is capped at <code>max-width: 720px</code> and centered with <code>margin-inline: auto</code>, keeping long-form text readable on wide screens.</p> </div> </sl-page>
margin-inline also controls the alignment of a capped column — use
margin-inline-end: auto to align it to the start, or
margin-inline-start: auto to align it to the end (centred is shown above):
Aligned to the start
With margin-inline-end: auto the capped content hugs the start (left) edge of the
page, leaving the empty space on the right.
Aligned to the end
With margin-inline-start: auto the capped content hugs the end (right) edge of
the page, leaving the empty space on the left.
<style> #main-align-start::part(header), #main-align-end::part(header) { background: var(--hds-color-lavender-100); } #main-align-start::part(body) { background-color: var(--hds-color-fuschia-100); max-width: 720px; margin-inline-end: auto; --content-gutter: 0; } #main-align-end::part(body) { background-color: var(--hds-color-fuschia-100); max-width: 720px; margin-inline-start: auto; --content-gutter: 0; } </style> <sl-page id="main-align-start"> <div slot="header" style="height: 56px; display: flex; align-items: center; padding: 0 var(--hds-space-5x); background: var(--sl-color-neutral-900); color: white; font-weight: var(--hds-font-application-weight-strong);"> ::part(body) aligned to the start </div> <div> <h2>Aligned to the start</h2> <p>With <code>margin-inline-end: auto</code> the capped content hugs the start (left) edge of the page, leaving the empty space on the right.</p> </div> </sl-page> <sl-page id="main-align-end"> <div slot="header" style="height: 56px; display: flex; align-items: center; padding: 0 var(--hds-space-5x); background: var(--sl-color-neutral-900); color: white; font-weight: var(--hds-font-application-weight-strong);"> ::part(body) aligned to the end </div> <div> <h2>Aligned to the end</h2> <p>With <code>margin-inline-start: auto</code> the capped content hugs the end (right) edge of the page, leaving the empty space on the left.</p> </div> </sl-page>
Sidebar Placement
Set sidebar-placement="end" to make the mobile drawer slide in from the right.
On mobile, the sidebar drawer slides in from the right side.
<style> #sidebar-placement::part(banner) { background: var(--hds-color-yellow-100); } #sidebar-placement::part(header) { background: var(--hds-color-lavender-100); } #sidebar-placement::part(subheader) { background: var(--hds-color-blue-100); } #sidebar-placement::part(sidebar) { background: var(--hds-color-pink-100); } #sidebar-placement::part(main) { background-color: var(--hds-color-fuschia-100); } #sidebar-placement::part(aside) { background: var(--hds-color-pink-100); } #sidebar-placement::part(footer) { background: var(--hds-color-lavender-100); } </style> <sl-page id="sidebar-placement" sidebar-placement="end" style="--sidebar-width: 200px;"> <sl-app-header-hds slot="header" hide-subheader> <sl-icon-button slot="app-switcher" name="bars" data-toggle-sidebar label="Toggle sidebar"></sl-icon-button> <span slot="app-name">End Placement</span> </sl-app-header-hds> <div slot="sidebar"> <div>Sidebar Item 1</div> <div>Sidebar Item 2</div> </div> <div><p>On mobile, the sidebar drawer slides in from the right side.</p></div> </sl-page>
Contained Footer
Set footer-placement="contained" to keep the footer within the main column. Sidebar and
aside extend full height — ideal for dashboard layouts.
Dashboard
The sidebar and aside extend to the bottom, while the footer is contained in the main column.
<style> #contained-footer::part(banner) { background: var(--hds-color-yellow-100); } #contained-footer::part(header) { background: var(--hds-color-lavender-100); } #contained-footer::part(subheader) { background: var(--hds-color-blue-100); } #contained-footer::part(sidebar) { background: var(--hds-color-pink-100); } #contained-footer::part(main) { background-color: var(--hds-color-fuschia-100); } #contained-footer::part(aside) { background: var(--hds-color-pink-100); } #contained-footer::part(footer) { background: var(--hds-color-lavender-100); } </style> <sl-page id="contained-footer" footer-placement="contained" style="--sidebar-width: 220px; --aside-width: 200px;"> <sl-app-header-hds slot="header" hide-subheader> <sl-icon-button slot="app-switcher" name="bars" data-toggle-sidebar label="Toggle sidebar"></sl-icon-button> <span slot="app-name">Dashboard App</span> </sl-app-header-hds> <div slot="sidebar"> <div>Dashboard</div> <div>Accounts</div> <div>Reports</div> </div> <div> <h2>Dashboard</h2> <p>The sidebar and aside extend to the bottom, while the footer is contained in the main column.</p> </div> <div slot="aside"> <strong>Activity</strong> <div>Recent item 1</div> <div>Recent item 2</div> </div> <div slot="footer" style="text-align: center;">© 2026 My Company</div> </sl-page>
Disable Sticky Sections
Use disable-sticky with a space-separated list of section names (banner,
header, subheader, sidebar, aside) to make them
scroll with the page.
The header and aside scroll with the content. The banner and sidebar remain sticky.
<style> #disable-sticky::part(banner) { background: var(--hds-color-yellow-100); } #disable-sticky::part(header) { background: var(--hds-color-lavender-100); } #disable-sticky::part(subheader) { background: var(--hds-color-blue-100); } #disable-sticky::part(sidebar) { background: var(--hds-color-pink-100); } #disable-sticky::part(main) { background-color: var(--hds-color-fuschia-100); } #disable-sticky::part(aside) { background: var(--hds-color-pink-100); } #disable-sticky::part(footer) { background: var(--hds-color-lavender-100); } </style> <sl-page id="disable-sticky" disable-sticky="header aside" style="--sidebar-width: 200px; --aside-width: 180px;"> <div slot="banner" style="background: var(--sl-color-warning-100); text-align: center; padding: var(--hds-space-1x) var(--hds-space-5x);"> Sticky banner — remains visible </div> <sl-app-header-hds slot="header" hide-subheader> <sl-icon-button slot="app-switcher" name="bars" data-toggle-sidebar label="Toggle sidebar"></sl-icon-button> <span slot="app-name">Non-sticky header — scrolls away</span> </sl-app-header-hds> <div slot="sidebar"> <div>Sidebar Item 1</div> <div>Sidebar Item 2</div> <div>Sidebar Item 3</div> <div>Sidebar Item 4</div> <div>Sidebar Item 5</div> <div>Sidebar Item 6</div> <div>Sidebar Item 7</div> <div>Sidebar Item 8</div> <div>Sidebar Item 9</div> <div>Sidebar Item 10</div> <div>Sidebar Item 11</div> <div>Sidebar Item 12</div> <div>Sidebar Item 13</div> <div>Sidebar Item 14</div> <div>Sidebar Item 15</div> <div>Sidebar Item 16</div> </div> <div> <p>The header and aside scroll with the content. The banner and sidebar remain sticky.</p> <div style="height: 600px;"></div> </div> <div slot="aside"> <div style="font-weight: var(--hds-font-application-weight-strong);">Non-sticky Aside</div> <div>Scrolls away</div> </div> <div slot="footer" style="background: var(--sl-color-neutral-200); text-align: center; padding: var(--hds-space-4x); font-weight: var(--hds-font-application-weight-strong);"> Footer </div> </sl-page>
Collapsible Desktop Sidebar
Combine --sidebar-width with a toggle button to build a collapsible sidebar.
Click the sidebar hamburger to collapse/expand.
<style> #collapsible-page::part(banner) { background: var(--hds-color-yellow-100); } #collapsible-page::part(header) { background: var(--hds-color-lavender-100); } #collapsible-page::part(subheader) { background: var(--hds-color-blue-100); } #collapsible-page::part(sidebar) { background: var(--hds-color-pink-100); } #collapsible-page::part(main) { background-color: var(--hds-color-fuschia-100); } #collapsible-page::part(aside) { background: var(--hds-color-pink-100); } #collapsible-page::part(footer) { background: var(--hds-color-lavender-100); } </style> <sl-page id="collapsible-page" style="--sidebar-width: 220px;"> <sl-app-header-hds slot="header" hide-subheader> <sl-icon-button slot="app-switcher" name="bars" data-toggle-sidebar label="Toggle sidebar"></sl-icon-button> <span slot="app-name">My Application</span> </sl-app-header-hds> <div slot="sidebar"> <div style="display: flex; align-items: center; gap: var(--hds-space-2x); margin-bottom: var(--hds-space-2x);"> <sl-icon-button name="bars" label="Collapse sidebar" onclick=" const page = document.getElementById('collapsible-page'); const collapsed = page.style.getPropertyValue('--sidebar-width') === '3rem'; page.style.setProperty('--sidebar-width', collapsed ? '' : '3rem'); this.closest('[slot=sidebar]').querySelector('.sidebar-links').style.display = collapsed ? '' : 'none'; this.closest('[slot=sidebar]').querySelector('.sidebar-title').style.display = collapsed ? '' : 'none'; " ></sl-icon-button> <strong class="sidebar-title">Menu</strong> </div> <div class="sidebar-links"> <div>Dashboard</div> <div>Reports</div> <div>Settings</div> </div> </div> <div><p>Click the sidebar hamburger to collapse/expand.</p></div> </sl-page>
Utility Classes
The page injects two global utility classes:
.sl-desktop-only— hidden whenview="mobile".sl-mobile-only— hidden whenview="desktop"
You are on desktop.
You are on mobile.
Resize below 990 px to toggle.
<style> #utility-classes::part(banner) { background: var(--hds-color-yellow-100); } #utility-classes::part(header) { background: var(--hds-color-lavender-100); } #utility-classes::part(subheader) { background: var(--hds-color-blue-100); } #utility-classes::part(sidebar) { background: var(--hds-color-pink-100); } #utility-classes::part(main) { background-color: var(--hds-color-fuschia-100); } #utility-classes::part(aside) { background: var(--hds-color-pink-100); } #utility-classes::part(footer) { background: var(--hds-color-lavender-100); } </style> <sl-page id="utility-classes" style="--sidebar-width: 220px;"> <sl-app-header-hds slot="header" hide-subheader> <sl-icon-button slot="app-switcher" name="bars" data-toggle-sidebar label="Toggle sidebar"></sl-icon-button> <span slot="app-name"> <span class="sl-desktop-only">My Application — Desktop</span> <span class="sl-mobile-only">App</span> </span> </sl-app-header-hds> <div slot="sidebar"> <div>Dashboard</div> <div>Reports</div> <div>Settings</div> </div> <div> <p class="sl-desktop-only">You are on <strong>desktop</strong>.</p> <p class="sl-mobile-only">You are on <strong>mobile</strong>.</p> <p>Resize below <strong>990 px</strong> to toggle.</p> </div> </sl-page>
Skip to Content
A visually-hidden “Skip to content” link is included automatically for keyboard accessibility. Press
Tab to reveal it. Customise the label with the skip-to-content slot.
Press Tab to reveal the skip link in the top-left corner.
<style> #skip-to-content::part(banner) { background: var(--hds-color-yellow-100); } #skip-to-content::part(header) { background: var(--hds-color-lavender-100); } #skip-to-content::part(subheader) { background: var(--hds-color-blue-100); } #skip-to-content::part(sidebar) { background: var(--hds-color-pink-100); } #skip-to-content::part(main) { background-color: var(--hds-color-fuschia-100); } #skip-to-content::part(aside) { background: var(--hds-color-pink-100); } #skip-to-content::part(footer) { background: var(--hds-color-lavender-100); } </style> <sl-page id="skip-to-content" style="--sidebar-width: 200px;"> <span slot="skip-to-content">Skip to main content</span> <div slot="header" style="height: 64px; display: flex; align-items: center; padding: 0 var(--hds-space-5x); background: var(--sl-color-neutral-900); color: white; font-weight: var(--hds-font-application-weight-strong);"> Accessible Page </div> <div slot="sidebar"> <div>Sidebar Item 1</div> <div>Sidebar Item 2</div> </div> <div><p>Press <kbd>Tab</kbd> to reveal the skip link in the top-left corner.</p></div> </sl-page>
[component-metadata:sl-page]
Slots
| Name | Description |
|---|---|
| (default) | The page’s main content. |
banner
|
The banner displayed above the header. |
header
|
The header displayed at the top of the page. |
subheader
|
A subheader below the header, suitable for breadcrumbs. |
sidebar
|
Sidebar content displayed on the left side (desktop) or in a drawer (mobile). |
desktop-sidebar
|
Desktop-only sidebar. Overrides sidebar on desktop viewports. |
mobile-sidebar
|
Mobile-only sidebar. Overrides sidebar in the drawer. |
mobile-sidebar-header
|
Header displayed in the mobile sidebar drawer. |
mobile-sidebar-footer
|
Footer displayed in the mobile sidebar drawer. |
aside
|
Content on the right side of the page (e.g. table of contents). |
skip-to-content
|
Override the default “Skip to content” link. |
footer
|
Footer displayed below all content. |
Learn more about using slots.
Properties
| Name | Description | Reflects | Type | Default |
|---|---|---|---|---|
view
|
The current view mode. When the page width is greater than the mobile breakpoint, it is “desktop”; otherwise “mobile”. This controls whether sidebar appears inline or in a drawer. |
|
'mobile' | 'desktop'
|
'desktop'
|
sidebarOpen
sidebar-open
|
Whether the mobile sidebar drawer is open. |
|
boolean
|
false
|
sidebarPlacement
sidebar-placement
|
Where to place the sidebar drawer on mobile viewports. |
|
'start' | 'end'
|
'start'
|
footerPlacement
footer-placement
|
Controls whether the footer spans full width or is contained within the main column. |
|
'full' | 'contained'
|
'full'
|
updateAsideAndMenuHeights
|
Updates the aside and sidebar heights to prevent awkward gaps when scrolling. Sets –body-height on aside and sidebar based on visible viewport pixels of the body area. | - | - | |
updateComplete |
A read-only promise that resolves when the component has finished updating. |
Learn more about attributes and properties.
Methods
| Name | Description | Arguments |
|---|---|---|
showSidebar() |
Shows the mobile sidebar drawer. | - |
hideSidebar() |
Hides the mobile sidebar drawer. | - |
toggleSidebar() |
Toggles the mobile sidebar drawer. | - |
Learn more about methods.
Custom Properties
| Name | Description | Default |
|---|---|---|
--sidebar-width |
The width of the desktop sidebar section. | 300px |
--main-width |
The width of the main section’s column. Defaults to 1fr so it fills the space
between the sidebar and aside.
|
1fr |
--content-gutter |
The responsive side gutter that insets the content area (the sidebar, main, and aside as a
group), growing from --hds-space-container-xs to
--hds-space-container-xl as the viewport widens.
|
var(–hds-space-container-xs, 16px) |
--aside-width |
The width of the aside section. | 300px |
--banner-height |
The height of the banner. This gets calculated when the page initializes. If the height is known, you can set it here to prevent shifting when the page loads. | 0px |
--header-height |
The height of the header. This gets calculated when the page initializes. If the height is known, you can set it here to prevent shifting when the page loads. | 0px |
--subheader-height |
The height of the subheader. This gets calculated when the page initializes. If the height is known, you can set it here to prevent shifting when the page loads. | 0px |
Learn more about customizing CSS custom properties.
Parts
| Name | Description |
|---|---|
base |
The component’s base wrapper. |
banner |
The banner area. |
header |
The header area. |
subheader |
The subheader area. |
body |
Wrapper around sidebar, main, and aside. |
sidebar |
The left-hand sidebar container. |
main |
The main content area. |
aside |
The right-hand aside container. |
skip-link |
The “skip to main content” link. |
footer |
The page footer. |
drawer__base |
Drawer base wrapper. |
drawer__overlay |
Drawer overlay. |
drawer__panel |
Drawer panel. |
drawer__header |
Drawer header. |
drawer__header-actions |
Drawer header actions. |
drawer__title |
Drawer title. |
drawer__close-button |
Drawer close button. |
drawer__close-button__base |
Drawer close button base. |
drawer__body |
Drawer body. |
drawer__footer |
Drawer footer. |
Learn more about customizing CSS parts.
Dependencies
This component automatically imports the following dependencies.
<sl-drawer><sl-icon><sl-icon-button>