Files
tabler/docs/content/ui/layout/page-layouts.mdx

529 lines
22 KiB
Plaintext

---
title: Page layouts
summary: Learn how to build a sample version of the dashboard
description: Combine containers, navigation and page structure into a complete dashboard layout, step by step.
related: [/ui/layout/page-headers, /ui/layout/navbars]
---
import Example from '@components/Example.astro'
import Icon from '@ui/Icon.astro'
<Info>
Before you start with this section, make sure you have followed the [installation guideline](/ui/getting-started/installation).
</Info>
## Sample layout
To create a sample version of the dashboard, you can use the following code snippet. This code snippet will help you to create a dashboard layout with a header.
<Example>
<div class="page">
<header class="navbar navbar-expand-sm d-print-none">
<div class="container-xl">
<h1 class="navbar-brand navbar-brand-autodark pe-0 pe-md-3">
<a href="#">
<img src="/static/logo.svg" width="110" height="32" alt="Tabler" class="navbar-brand-image" />
</a>
</h1>
<div class="navbar-nav flex-row order-md-last">
<div class="nav-item">
<a href="#" class="nav-link d-flex lh-1 text-reset p-0">
<span class="avatar avatar-sm" style="background-image: url(/static/avatars/002m.jpg)"></span>
<div class="d-none d-xl-block ps-2">
<div>Paweł Kuna</div>
<div class="mt-1 small text-secondary">UI Designer</div>
</div>
</a>
</div>
</div>
</div>
</header>
<div class="page-wrapper">
<main class="page-body">
<div class="container-xl">
<div class="row row-deck row-cards">
<div class="col-4">
<div class="card">
<div class="card-body" style="height: 10rem"></div>
</div>
</div>
<div class="col-4">
<div class="card">
<div class="card-body" style="height: 10rem"></div>
</div>
</div>
<div class="col-4">
<div class="card">
<div class="card-body" style="height: 10rem"></div>
</div>
</div>
<div class="col-12">
<div class="card">
<div class="card-body" style="height: 10rem"></div>
</div>
</div>
</div>
</div>
</main>
</div>
</div>
</Example>
## Footer
Add a `footer` element with the `footer` class after `.page-wrapper` to close the page. `d-print-none` hides it when the page is printed, the same as the navbar. Use [`list-inline list-inline-dots`](/ui/components/list-group#inline-list) for the footer links.
<Example>
<footer class="footer footer-transparent d-print-none">
<div class="container-xl">
<div class="row text-center align-items-center flex-row-reverse">
<div class="col-lg-auto ms-lg-auto">
<ul class="list-inline list-inline-dots mb-0">
<li class="list-inline-item"><a href="#" class="link-secondary">Documentation</a></li>
<li class="list-inline-item"><a href="#" class="link-secondary">License</a></li>
<li class="list-inline-item"><a href="#" class="link-secondary">Source code</a></li>
</ul>
</div>
<div class="col-12 col-lg-auto mt-3 mt-lg-0">
<ul class="list-inline list-inline-dots mb-0">
<li class="list-inline-item">Copyright &copy; 2026 <a href="#" class="link-secondary">Your company</a> . All rights reserved.</li>
<li class="list-inline-item"><a href="#" class="link-secondary">v1.0.0</a></li>
</ul>
</div>
</div>
</div>
</footer>
</Example>
`footer-transparent` drops the footer's own background, so it blends with the page behind it. Remove the class for a footer with a solid background, for example to visually separate it from the page body.
## Sidebar layout
To create a sidebar layout, you can use the following code snippet. This code snippet will help you to create a sidebar layout with a header.
<Example>
<div class="page">
<aside class="navbar navbar-vertical navbar-expand-sm position-absolute">
<div class="container-fluid">
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#sidebar-menu" aria-controls="sidebar-menu" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<h1 class="navbar-brand navbar-brand-autodark">
<a href="#">
<img src="/static/logo.svg" width="110" height="32" alt="Tabler" class="navbar-brand-image" />
</a>
</h1>
<div class="collapse navbar-collapse" id="sidebar-menu">
<ul class="navbar-nav pt-lg-3">
<li class="nav-item">
<a class="nav-link" href="./">
<span class="nav-link-title">Home</span>
</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#">
<span class="nav-link-title">Link 1</span>
</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#">
<span class="nav-link-title">Link 2</span>
</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#">
<span class="nav-link-title">Link 3</span>
</a>
</li>
</ul>
</div>
</div>
</aside>
<div class="page-wrapper">
<div class="page-header d-print-none">
<div class="container-xl">
<div class="row g-2 align-items-center">
<div class="col">
<h2 class="page-title">Vertical layout</h2>
</div>
</div>
</div>
</div>
<main class="page-body">
<div class="container-xl">
<div class="row row-deck row-cards">
<div class="col-sm-6 col-lg-3">
<div class="card">
<div class="card-body" style="height: 10rem"></div>
</div>
</div>
<div class="col-sm-6 col-lg-3">
<div class="card">
<div class="card-body" style="height: 10rem"></div>
</div>
</div>
<div class="col-sm-6 col-lg-3">
<div class="card">
<div class="card-body" style="height: 10rem"></div>
</div>
</div>
<div class="col-sm-6 col-lg-3">
<div class="card">
<div class="card-body" style="height: 10rem"></div>
</div>
</div>
<div class="col-lg-6">
<div class="row row-cards">
<div class="col-12">
<div class="card">
<div class="card-body" style="height: 10rem"></div>
</div>
</div>
<div class="col-12">
<div class="card">
<div class="card-body" style="height: 10rem"></div>
</div>
</div>
</div>
</div>
<div class="col-lg-6">
<div class="card">
<div class="card-body" style="height: 10rem"></div>
</div>
</div>
<div class="col-12">
<div class="card">
<div class="card-body" style="height: 10rem"></div>
</div>
</div>
<div class="col-md-12 col-lg-8">
<div class="card">
<div class="card-body" style="height: 10rem"></div>
</div>
</div>
<div class="col-md-6 col-lg-4">
<div class="card">
<div class="card-body" style="height: 10rem"></div>
</div>
</div>
<div class="col-md-6 col-lg-4">
<div class="card">
<div class="card-body" style="height: 10rem"></div>
</div>
</div>
<div class="col-md-12 col-lg-8">
<div class="card">
<div class="card-body" style="height: 10rem"></div>
</div>
</div>
<div class="col-12">
<div class="card">
<div class="card-body" style="height: 10rem"></div>
</div>
</div>
</div>
</div>
</main>
</div>
</div>
</Example>
### Section labels
Group related menu items with a label. Add a `nav-section-title` list item before the first item of a group:
```html
<ul class="navbar-nav">
<li class="nav-section-title">Overview</li>
<li class="nav-item">...</li>
<li class="nav-item">...</li>
<li class="nav-section-title">Components</li>
<li class="nav-item">...</li>
</ul>
```
The label is aligned with the item text and muted, so it does not compete with the links. In the folded sidebar, the label text is replaced with a short separator line centered in the gap between the groups, and the label keeps its height, so the menu does not shift when the sidebar folds. Section labels render only in vertical navbars - a horizontal navbar hides them.
### Trailing entries
A `.navbar-side` wrapper with its own `.navbar-nav` after the menu is the sidebar's trailing group: a Customize link, a help link, anything that belongs at the end rather than in the menu. It stacks like the menu and never grows, and when the menu is shorter than the sidebar it is pinned to the bottom of the scroll area. In a horizontal navbar the same class lays the group out as a row on the end side.
```html
<nav class="collapse navbar-collapse" id="sidebar-menu">
<ul class="navbar-nav">...</ul>
<div class="navbar-side">
<ul class="navbar-nav">
<li class="nav-item">
<button type="button" class="nav-link" data-bs-toggle="offcanvas" data-bs-target="#offcanvas-settings">
<span class="nav-link-icon"><svg><!-- brush icon --></svg></span>
<span class="nav-link-title">Customize</span>
</button>
</li>
</ul>
</div>
</nav>
```
### Sidebar footer and user block
The vertical sidebar has three zones: the brand at the top, the nav as its own scroll area, and an optional `navbar-footer` pinned at the bottom. A common use is a user block with a drop-up menu:
```html
<aside class="navbar navbar-vertical navbar-expand-lg">
<div class="container-fluid">
<div class="navbar-brand">...</div>
<nav class="collapse navbar-collapse" id="sidebar-menu">...</nav>
<div class="navbar-footer">
<ul class="navbar-nav">
<li class="nav-item dropup">
<a href="#" class="nav-link" data-bs-toggle="dropdown" aria-label="Open user menu">
<span class="avatar avatar-sm" style="background-image: url(...)"></span>
<span class="nav-link-title">
Paweł Kuna
<div class="small text-secondary">UI Designer</div>
</span>
</a>
<div class="dropdown-menu">
<a class="dropdown-item" href="#">Profile</a>
<a class="dropdown-item" href="#">Sign out</a>
</div>
</li>
</ul>
</div>
</div>
</aside>
```
The nav scrolls independently, so the footer stays visible with long menus. In the folded sidebar, the name and role collapse like link titles. Below the navbar's breakpoint, the footer moves into the mobile top bar: only the avatar shows, and its menu drops down instead of up.
## Folded sidebar
Add `navbar-folded` to a vertical navbar to fold it into a narrow, icon-only rail. Link titles are hidden, icons are centered and submenus open as floating flyout menus next to the rail. The page content moves over automatically, so you get more space for your app.
The folded mode works only above the navbar's `navbar-expand-*` breakpoint. Below it, the sidebar keeps the standard mobile behavior with the toggler button.
Use `data-bs-auto-close="outside"` on submenu toggles in a folded sidebar, so an open flyout closes when the user clicks somewhere else.
<Example height="26rem">
<div class="page">
<aside class="navbar navbar-vertical navbar-expand-sm navbar-folded position-absolute">
<div class="container-fluid">
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#sidebar-folded" aria-controls="sidebar-folded" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<h1 class="navbar-brand navbar-brand-autodark">
<a href="#">
<img src="/static/logo-small.svg" width="32" height="32" alt="Tabler" class="navbar-brand-image" />
</a>
</h1>
<div class="collapse navbar-collapse" id="sidebar-folded">
<ul class="navbar-nav pt-lg-3">
<li class="nav-item active">
<a class="nav-link" href="#" aria-current="page">
<span class="nav-link-icon"><Icon name="home" /></span>
<span class="nav-link-title">Home</span>
</a>
</li>
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle" href="#" data-bs-toggle="dropdown" data-bs-auto-close="outside" role="button" aria-haspopup="true" aria-expanded="false">
<span class="nav-link-icon"><Icon name="chart-bar" /></span>
<span class="nav-link-title">Reports</span>
</a>
<div class="dropdown-menu">
<a class="dropdown-item" href="#">Overview</a>
<a class="dropdown-item" href="#">Sales</a>
<a class="dropdown-item" href="#">Traffic</a>
</div>
</li>
<li class="nav-item">
<a class="nav-link" href="#">
<span class="nav-link-icon"><Icon name="settings" /></span>
<span class="nav-link-title">Settings</span>
</a>
</li>
</ul>
</div>
</div>
</aside>
<div class="page-wrapper">
<main class="page-body">
<div class="container-xl">
<div class="row row-deck row-cards">
<div class="col-6">
<div class="card">
<div class="card-body" style="height: 10rem"></div>
</div>
</div>
<div class="col-6">
<div class="card">
<div class="card-body" style="height: 10rem"></div>
</div>
</div>
</div>
</div>
</main>
</div>
</div>
</Example>
Use a small, square logo in a folded sidebar. The brand area is only `4rem` wide, so a wide logo will be cropped.
A folded rail does not scroll. If your menu has more items than the screen can fit, use the hover variant below or keep the sidebar expanded.
### Unfold on hover
Use `navbar-folded-hover` instead of `navbar-folded` to get a folded rail that unfolds when the user hovers over it or moves keyboard focus into it. The expanded sidebar overlays the page content, so nothing moves around. This variant also restores scrolling of long menus while the sidebar is unfolded.
```html
<aside class="navbar navbar-vertical navbar-expand-lg navbar-folded-hover">...</aside>
```
### Pin button and saved state
To let users fold and unfold the sidebar at runtime, add a button with `data-bs-toggle="sidebar-folded"` anywhere on the page. A good place is the brand area: the logo stays on the start side and the pin button sits on the end side. The button is visible only while the sidebar is expanded. Unpinning folds the sidebar to the hover variant, so hovering the rail expands it again and shows the pin button:
```html
<div class="navbar-brand">
<a href="." aria-label="Tabler">
<svg class="navbar-brand-image"><!-- logo mark --></svg>
</a>
<button
type="button"
class="btn btn-action btn-sm"
data-bs-toggle="sidebar-folded"
aria-pressed="false"
aria-label="Pin sidebar"
>
<svg><!-- pin icon --></svg>
</button>
</div>
```
Use a small, square logo mark in the brand area, so the logo does not jump when the sidebar folds and unfolds.
The button toggles the `data-bs-sidebar="folded-hover"` attribute on the `<html>` element, which folds every vertical navbar on the page. Set `data-bs-sidebar="folded"` yourself for a static rail that does not expand on hover. The choice is saved to `localStorage` under the `tabler-sidebar` key. The `tabler-theme.js` script applies the saved state before the page renders, so there is no flash of the wrong layout on reload. You can also set the state with the `?sidebar=folded` URL parameter.
After each toggle, Tabler dispatches a `tabler:sidebar-folded` event on `document`. Use it to resize charts or maps after the page content changes width:
```js
document.addEventListener('tabler:sidebar-folded', (event) => {
console.log(event.detail.folded) // true or false
myChart.resize()
})
```
You can also add tooltips with the link title to icon-only links, so users see the name of each item on hover.
## Layout options
Beyond the sidebar, six more layout choices are available as attributes on `<html>`, applied before the page renders by [`tabler-theme.js`](/ui/getting-started/color-modes). Every one of them has a static route too, so you can hard-code a layout without any JavaScript.
### Container width
The page content runs at the container's normal width by default. Set `fluid` to let every container fill the viewport, or `boxed` to center the whole page in a frame:
```html
<!-- attribute route, switchable at runtime -->
<html data-bs-layout="fluid">
<html data-bs-layout="boxed">
<!-- class route, fixed in the markup -->
<body class="layout-fluid">
<body class="layout-boxed">
```
Use one route or the other, not both. The boxed frame is the containing block of a vertical navbar, so a sidebar stays inside the frame instead of pinning to the viewport - it scrolls with the frame rather than staying fixed.
### Sticky navbar
`data-bs-navbar="sticky"` keeps the top navbar at the top of the viewport while the page scrolls:
```html
<html data-bs-navbar="sticky">
```
Only the navbar that is a direct child of `.page` sticks. On the two-row navbar the menu row scrolls away underneath, so the sticky strip stays one row high. Without the attribute you can still wrap a navbar in your own `.sticky-top` element.
### Floating navigation
`data-bs-navbar-style="floating"` keeps a gap between the navigation and the edges around it, and gives it a border and rounded corners:
```html
<html data-bs-navbar-style="floating">
```
The attribute covers whichever navigation the page shows. The sidebar keeps its width, and the page next to it moves over by the same gap. On the two-row navbar both rows float. The navigation floats only from its `navbar-expand-*` breakpoint up. Without the attribute, add the [`navbar-floating`](/ui/layout/navbars#floating-navbar) class to a navbar in your markup:
```html
<header class="navbar navbar-expand-md navbar-floating">...</header>
```
### Floating offcanvas
`data-bs-offcanvas-style="floating"` does the same for every [offcanvas](/ui/components/offcanvas#floating-panel) on the page:
```html
<html data-bs-offcanvas-style="floating">
```
Without the attribute, add the `offcanvas-floating` class to a single panel.
### Navigation position
`data-bs-navbar-position` picks which navigation the page shows:
```html
<html data-bs-navbar-position="vertical">
```
This one needs the markup to cooperate. Render **both** navigations as direct children of `.page`, the vertical `aside` first:
```html
<div class="page">
<aside class="navbar navbar-vertical navbar-expand-lg">...</aside>
<header class="navbar navbar-expand-md">...</header>
<div class="page-wrapper">...</div>
</div>
```
The CSS then shows one and hides the other, and removes the sidebar's width from the page offset when the sidebar is hidden. Both rules only fire when the other navigation really is on the page, so a page with a single navbar keeps it whatever the stored value says. That is what makes the layout safe to switch at runtime: no stored choice can leave a visitor with no navigation.
The demo pages use this: every page renders both navigations, and the layout demos state their own variant with the attribute.
### Navigation theme
```html
<html data-bs-navbar-theme="dark">
<html data-bs-navbar-theme="primary">
```
The attribute colors whichever navigation the page shows - the vertical sidebar is a navbar too. `default` means "follow the page color mode", so the navigation is light on a light page and dark on a dark one. `dark` gives it the dark palette on a light page, exactly like putting `data-bs-theme="dark"` on the navbar in your markup. `primary` paints it in the primary color and keeps the dark text palette, the same look as `.bg-primary` with `data-bs-theme="dark"`:
```html
<header class="navbar navbar-expand-md" data-bs-theme="dark">...</header>
<header class="navbar navbar-expand-md bg-primary" data-bs-theme="dark">...</header>
```
A light navigation inside a dark page is not supported - a light-scoped subtree inside a dark page inherits the dark styles.
## Accessibility
A layout is what a screen reader user navigates by, so the landmarks matter more here than on any single component.
- Use the real elements for the regions. Tabler's own layout renders `header` for the top bar, `aside` for the vertical navbar, `main` for `.page-body` and `footer` for the footer. The classes are styling; the elements are what create the landmarks.
- `.page-body` should be a `main`, and there is exactly one per page. It is the target the skip link points at.
- Start the page with a skip link, so keyboard users can jump past the navigation. Tabler ships one:
```html
<a href="#content" class="visually-hidden-focusable skip-link">Skip to main content</a>
...
<main class="page-body">
```
`visually-hidden-focusable` keeps the link out of the way until it takes focus, and then shows it.
- Label each navigation region. A page with a sidebar and a top bar has two `nav` elements, and `aria-label="Primary"` on one of them is what tells them apart.
- The navbar toggler needs `aria-controls` pointing at the collapsing element and a name such as "Toggle navigation". Bootstrap keeps `aria-expanded` in sync.
- Keep the DOM order close to the reading order. A sidebar that appears on the right visually should still come where it belongs in the markup.
- `d-print-none` on the navbar and the footer removes them from print, which is what you want - but check that nothing users need only exists there.