Sidenav
Materialize is a modern responsive CSS framework based on Material Design by Google.
1. Slide-out drawer
This is a slide out menu. You can add a dropdown to your sidebar by using our collapsible component. If you want to see a demo, our sidebar will use this on smaller screens. To use this in conjunction with a fullscreen navigation, you have to use two copies of the same UL.
Please note that the sidenav HTML should not be contained within the navbar HTML.
Toggle Sidenav<nav><!-- navbar content here --></nav>
<ul id="slide-out" class="sidenav">
<li>
<div class="user-view">
<div class="background">
<img src="images/office.jpg" />
</div>
<a href="#user"><img class="circle" src="images/yuna.jpg" /></a>
<a href="#name"><span class="white-text name">John Doe</span></a>
<a href="#email"><span class="white-text email">jdoe@example.com</span></a>
</div>
</li>
<li>
<a href="#!">
<i class="material-icons">cloud</i>
First Link With Icon
</a>
</li>
<li><a href="#!">Second Link</a></li>
<li><div class="divider"></div></li>
<li><a class="subheader">Subheader</a></li>
<li><a class="waves-effect" href="#!">Third Link With Waves</a></li>
</ul>
<a href="#" data-target="slide-out" class="sidenav-trigger">
<i class="material-icons">menu</i>
</a>2. Documentation drawer
A second variant matching this site's drawer: Kmaterialize branding, a search field, expandable sections, and an active page. This sample includes representative links from the docs menu.
Open documentation drawer in a full window
The preview docks the drawer at 560px and above; below that, the menu button opens it as an overlay. Use 993px instead to match the main site's desktop breakpoint. Search uses the same autocomplete and full page index as the website. Select a suggestion to open the page. In your own app, replace the imported config.pages with your route list. Links navigate to actual documentation pages. This example reads the same saved theme and palette as the site, using @material/material-color-utilities.
HTML — structure and navigation
<body class="drawer-example">
<!-- Drawer: logo, search, and grouped page links. Keep it outside the navbar. -->
<ul id="docs-example-menu" class="sidenav" aria-label="Documentation">
<li class="drawer-example-brand">
<a href="index.html" target="_top">
<img src="images/materialize.svg" alt="" />
Kmaterialize
</a>
</li>
<!-- Autocomplete uses the full documentation page index. -->
<li class="drawer-example-search">
<div class="input-field outlined">
<input
id="docs-example-search"
class="search-docs"
type="text"
placeholder=" "
autocomplete="off"
/>
<label for="docs-example-search">Search</label>
</div>
</li>
<li>
<a href="about.html" target="_top">
<i class="material-icons" aria-hidden="true">info</i>
About
</a>
</li>
<li>
<a href="getting-started.html" target="_top">
<i class="material-icons" aria-hidden="true">apps</i>
Getting started
</a>
</li>
<!-- Expandable navigation groups. The active section starts open. -->
<li>
<ul class="collapsible collapsible-accordion">
<li>
<button type="button" class="collapsible-header">
<i class="material-icons" aria-hidden="true">palette</i>
Styles
</button>
<div class="collapsible-body">
<ul>
<li><a href="color.html" target="_top">Color</a></li>
<li><a href="grid.html" target="_top">Grid</a></li>
<li><a href="themes.html" target="_top">Themes</a></li>
<li><a href="typography.html" target="_top">Typography</a></li>
</ul>
</div>
</li>
<li class="active">
<button type="button" class="collapsible-header">
<i class="material-icons" aria-hidden="true">add_circle</i>
Components
</button>
<div class="collapsible-body">
<ul>
<li><a href="cards.html" target="_top">Cards</a></li>
<li><a href="kanban.html" target="_top">Kanban</a></li>
<li><a href="navbar.html" target="_top">Navbar</a></li>
<li><a href="org-chart.html" target="_top">Org Chart</a></li>
<li class="active">
<a href="sidenav.html" target="_top" aria-current="page">Sidenav</a>
</li>
<li><a href="tabs.html" target="_top">Tabs</a></li>
</ul>
</div>
</li>
<li>
<button type="button" class="collapsible-header">
<i class="material-icons" aria-hidden="true">text_fields</i>
Forms
</button>
<div class="collapsible-body">
<ul>
<li><a href="autocomplete.html" target="_top">Autocomplete</a></li>
<li><a href="checkboxes.html" target="_top">Checkboxes</a></li>
<li><a href="select.html" target="_top">Select</a></li>
</ul>
</div>
</li>
</ul>
</li>
<li>
<a href="mobile.html" target="_top">
<i class="material-icons" aria-hidden="true">mobile_friendly</i>
Mobile
</a>
</li>
</ul>
<!-- Page area: offset beside the drawer on wide screens. -->
<div class="drawer-example-page">
<nav class="navbar">
<div class="nav-wrapper primary">
<!-- Visible on narrow screens; JavaScript updates aria-expanded. -->
<button
type="button"
id="docs-example-toggle"
class="btn text"
aria-controls="docs-example-menu"
aria-expanded="false"
aria-label="Open navigation"
>
<i class="material-icons" aria-hidden="true">menu</i>
</button>
<span>Documentation</span>
</div>
</nav>
<main class="drawer-example-content">
<span class="chip">Variant 2</span>
<h1>
A familiar place
<br />
to find your way.
</h1>
<p>
The docs drawer brings search, expandable sections, and the current page
together.
</p>
<p>
Search any documentation page, or expand Styles and Forms. The links open the
corresponding documentation pages.
</p>
<p class="drawer-example-note" role="status" id="docs-example-status">
The drawer stays open on wider screens.
</p>
</main>
</div>
<!-- Load the TypeScript below through your bundler. -->
<script type="module" src="/sidenav-docs-demo.ts"></script>
</body>SCSS — layout, colors, and responsive styles
// Load the icon font and Materialize before these example-specific styles.
@import 'material-icons/iconfont/material-icons.css';
@import 'kmaterialize/sass/materialize.scss';
// Base surface and drawer dimensions.
.drawer-example {
margin: 0;
background: var(--md-sys-color-background);
color: var(--md-sys-color-on-background);
}
.drawer-example .sidenav {
width: 300px;
}
.drawer-example-brand > a {
display: flex !important;
align-items: center;
gap: 16px;
height: 72px !important;
font-size: 22px !important;
}
.drawer-example-brand img {
width: 28px;
height: 28px;
}
// Use kmaterialize’s outlined field; only sidebar spacing is customized.
.drawer-example-search {
margin: 8px 16px 16px;
line-height: 1.25;
.input-field { margin: 0; }
}
.drawer-example [hidden] {
display: none !important;
}
.drawer-example .nav-wrapper {
display: flex;
align-items: center;
gap: 12px;
padding: 0 16px;
}
.drawer-example .nav-wrapper .btn {
color: var(--md-sys-color-on-primary);
}
// Content spacing and typography for the preview.
.drawer-example-content {
padding: 32px 24px;
}
.drawer-example-content h1 {
font-size: clamp(28px, 4vw, 52px);
line-height: 1.1;
letter-spacing: -0.035em;
}
.drawer-example-content p {
max-width: 48ch;
line-height: 1.6;
}
.drawer-example-note {
font-size: 13px;
color: var(--md-sys-color-on-surface-variant);
}
// JavaScript applies this class at 560px: dock the drawer and hide the overlay.
.drawer-example-docked .drawer-example-page {
margin-left: 300px;
}
.drawer-example-docked .sidenav-overlay,
.drawer-example-docked #docs-example-toggle {
display: none !important;
}TypeScript — initialization and interactions
import { Sidenav, Collapsible, Autocomplete } from 'kmaterialize';
import './sidenav-docs-demo.scss';
// This project supplies the page index. Use your own route list in another app.
import { config } from '../config.materialize';
import {
applyTheme,
argbFromHex,
themeFromSourceColor,
} from '@material/material-color-utilities';
// Use the same saved palette and theme as the documentation site.
const seed = localStorage.getItem('theme-primary-color') || '#FFD700';
const preference = localStorage.getItem('theme-mode');
const dark = preference
? preference === 'dark'
: matchMedia('(prefers-color-scheme: dark)').matches;
applyTheme(themeFromSourceColor(argbFromHex(seed)), {
target: document.documentElement,
dark,
});
document.documentElement.setAttribute('theme', dark ? 'dark' : 'light');
// Find the drawer and its controls after the HTML has loaded.
const menu = document.querySelector<HTMLElement>('#docs-example-menu')!;
const toggle = document.querySelector<HTMLButtonElement>('#docs-example-toggle')!;
const search = document.querySelector<HTMLInputElement>('#docs-example-search')!;
const wide = matchMedia('(min-width: 560px)');
// Initialize the drawer and keep the menu button state accessible.
const drawer = Sidenav.init(menu, {
draggable: false,
preventScrolling: false,
onOpenStart: () => toggle.setAttribute('aria-expanded', 'true'),
onCloseStart: () => toggle.setAttribute('aria-expanded', 'false'),
});
// Allow several navigation groups to stay open at once.
Collapsible.init(menu.querySelector<HTMLElement>('.collapsible')!, {
accordion: false,
});
// Wide screens dock the drawer; narrow screens use the menu button.
function resize() {
document.body.classList.toggle('drawer-example-docked', wide.matches);
if (wide.matches) drawer.open();
else drawer.close();
}
wide.addEventListener('change', resize);
resize();
// Toggle the mobile drawer; Escape closes it and restores button focus.
toggle.addEventListener('click', () =>
drawer.isOpen ? drawer.close() : drawer.open(),
);
document.addEventListener('keydown', (event) => {
if (event.key === 'Escape' && !wide.matches && drawer.isOpen) {
drawer.close();
toggle.focus();
}
});
// Match the documentation site's autocomplete and complete page index.
Autocomplete.init(search, {
minLength: 1,
data: config.pages.map((page) => ({
id: page.id,
text: page.name,
description: page.description,
url: page.url,
})),
onAutocomplete: (items) => {
if (items.length === 1) window.top!.location.href = items[0]['url'];
},
});3. Material 3 navigation rail
A compact rail inspired by the Material Design website: centered icons and labels, the website’s current colors, and circular motion and theme controls at the bottom. This example uses the Kmaterialize logo and Material Icons.
Open navigation rail in a full window
Choose a section to update the preview. The bottom controls pause the illustration and switch the example's light/dark theme. Its initial palette and theme match the website, and updates from the surrounding page are reflected automatically. Motion starts paused when the device requests reduced motion. The rail stays visible on phones and scrolls when the screen is too short.
The rail layout is included in kmaterialize and uses native links, so it needs no Sidenav.init(). Load Materialize and Material Icons. The SCSS below only styles the preview content. The theme control uses @material/material-color-utilities. Replace the demo hash links with your app's routes and preserve aria-current="page" on the active link.
HTML — structure and navigation
<body class="rail-example">
<!-- Fixed rail: section links above, utility buttons below. -->
<aside class="m3-rail" aria-label="Site navigation">
<nav class="m3-rail-nav" aria-label="Main sections">
<!-- Each link pairs an icon with a visible label. aria-current marks the active page. -->
<a href="#home" aria-current="page" data-view="Home">
<span class="m3-rail-icon"><img src="images/materialize.svg" alt="" /></span>
<span>Home</span>
</a>
<a href="#get-started" data-view="Get started">
<span class="m3-rail-icon">
<i class="material-icons" aria-hidden="true">apps</i>
</span>
<span>Get started</span>
</a>
<a href="#develop" data-view="Develop">
<span class="m3-rail-icon">
<i class="material-icons" aria-hidden="true">code</i>
</span>
<span>Develop</span>
</a>
<a href="#foundations" data-view="Foundations">
<span class="m3-rail-icon">
<i class="material-icons" aria-hidden="true">book</i>
</span>
<span>Foundations</span>
</a>
<a href="#styles" data-view="Styles">
<span class="m3-rail-icon">
<i class="material-icons" aria-hidden="true">palette</i>
</span>
<span>Styles</span>
</a>
<a href="#components" data-view="Components">
<span class="m3-rail-icon">
<i class="material-icons" aria-hidden="true">add_circle_outline</i>
</span>
<span>Components</span>
</a>
<a href="#blog" data-view="Blog">
<span class="m3-rail-icon">
<i class="material-icons" aria-hidden="true">stars</i>
</span>
<span>Blog</span>
</a>
</nav>
<!-- Bottom actions: pause motion and change the theme. -->
<div class="m3-rail-actions">
<button
type="button"
id="rail-motion"
aria-label="Pause motion"
aria-pressed="false"
>
<i class="material-icons" aria-hidden="true">pause</i>
</button>
<button type="button" id="rail-theme" aria-label="Switch to light theme">
<i class="material-icons" aria-hidden="true">light_mode</i>
</button>
</div>
</aside>
<!-- Demo content. Replace hash navigation with your application routes. -->
<main class="rail-example-content">
<p class="rail-example-eyebrow">MATERIAL DESIGN · VARIANT 3</p>
<h1 id="rail-view-title">Home</h1>
<p id="rail-view-description" aria-live="polite">
A place for the ideas, tools, and components that shape your next interface.
</p>
<!-- Decorative animation controlled by the pause button. -->
<div class="rail-example-art" aria-hidden="true">
<div></div>
<i class="material-icons">auto_awesome</i>
</div>
<p class="rail-example-hint">
Choose a section. Use the controls at the bottom of the rail to pause motion or
switch the theme.
</p>
</main>
<!-- Load the TypeScript below through your bundler. -->
<script type="module" src="/sidenav-rail-demo.ts"></script>
</body>SCSS — layout, colors, and responsive styles
@import 'material-icons/iconfont/material-icons.css';
@import 'kmaterialize/sass/materialize.scss';
.rail-example { margin: 0; background: var(--md-sys-color-background); color: var(--md-sys-color-on-background); }
.rail-example-content { margin-left: 104px; padding: 40px clamp(20px, 5vw, 64px); max-width: 1050px; }
.rail-example-content h1 { font-size: clamp(36px, 7vw, 80px); letter-spacing: -.05em; margin: 24px 0; }
.rail-example-content p { line-height: 1.6; max-width: 48ch; }
.rail-example-eyebrow { font-size: 10px; letter-spacing: .16em; color: var(--md-sys-color-on-surface-variant); }
.rail-example-hint { font-size: 13px; color: var(--md-sys-color-on-surface-variant); }
.rail-example-art {
display: grid; place-items: center; position: relative; isolation: isolate;
height: 240px; margin: 32px 0; overflow: hidden; border-radius: 36px;
background: var(--md-sys-color-secondary-container); color: var(--md-sys-color-on-secondary-container);
> div { position: absolute; width: 160px; height: 160px; background: var(--md-sys-color-tertiary-container); border-radius: 40%; animation: rail-orbit 18s linear infinite; z-index: -1; }
> i { font-size: 64px; }
}
.rail-motion-paused .rail-example-art > div { animation-play-state: paused; }
@keyframes rail-orbit { to { transform: rotate(360deg); } }
@media (max-width: 480px) {
.rail-example-content { margin-left: 88px; padding: 28px 20px; }
}
TypeScript — initialization and interactions
import {
applyTheme,
argbFromHex,
themeFromSourceColor,
} from '@material/material-color-utilities';
import './sidenav-rail-demo.scss';
// Read the website theme, falling back to the device preference.
const systemTheme = matchMedia('(prefers-color-scheme: dark)');
const savedDark = () => {
const preference = localStorage.getItem('theme-mode');
return preference ? preference === 'dark' : systemTheme.matches;
};
// Cache controls and initialize their state. Reduced motion starts paused.
const themeButton = document.querySelector<HTMLButtonElement>('#rail-theme')!;
const motionButton = document.querySelector<HTMLButtonElement>('#rail-motion')!;
let dark = savedDark();
let paused = matchMedia('(prefers-reduced-motion: reduce)').matches;
// Generate colors from the saved website palette and update the theme button.
function updateTheme() {
const seed = localStorage.getItem('theme-primary-color') || '#FFD700';
const theme = themeFromSourceColor(argbFromHex(seed));
applyTheme(theme, { target: document.documentElement, dark });
document.documentElement.setAttribute('theme', dark ? 'dark' : 'light');
themeButton.setAttribute('aria-label', `Switch to ${dark ? 'light' : 'dark'} theme`);
themeButton.querySelector('i')!.textContent = dark ? 'light_mode' : 'dark_mode';
}
// Keep the animation, icon, and accessible button state in sync.
function updateMotion() {
document.body.classList.toggle('rail-motion-paused', paused);
motionButton.setAttribute('aria-pressed', String(paused));
motionButton.setAttribute('aria-label', paused ? 'Resume motion' : 'Pause motion');
motionButton.querySelector('i')!.textContent = paused ? 'play_arrow' : 'pause';
}
// Connect the two utility buttons.
themeButton.addEventListener('click', () => {
dark = !dark;
updateTheme();
});
motionButton.addEventListener('click', () => {
paused = !paused;
updateMotion();
});
// Changes made in the surrounding docs page also reach this iframe.
window.addEventListener('storage', (event) => {
if (!event.key || ['theme-primary-color', 'theme-mode'].includes(event.key)) {
dark = savedDark();
updateTheme();
}
});
systemTheme.addEventListener('change', () => {
if (!localStorage.getItem('theme-mode')) {
dark = savedDark();
updateTheme();
}
});
updateTheme();
updateMotion();
// Demo page content. Real applications would render their own routes here.
const descriptions: Record<string, string> = {
Home: 'A place for the ideas, tools, and components that shape your next interface.',
'Get started':
'Start with the essentials and build a shared design language for your product.',
Develop: 'Turn design decisions into working interfaces with reusable components.',
Foundations:
'Create a clear hierarchy with accessible layouts, interaction, and motion.',
Styles: 'Make color, typography, and shape work together across your interface.',
Components:
'Explore the building blocks that help people navigate and get things done.',
Blog: 'Discover new ideas and stories from the world of interface design.',
};
const links = Array.from(
document.querySelectorAll<HTMLAnchorElement>('.m3-rail-nav a'),
);
// Update the active link and content when the URL hash changes.
function selectSection() {
const selected = links.find((link) => link.hash === location.hash) || links[0];
links.forEach((link) => {
if (link === selected) link.setAttribute('aria-current', 'page');
else link.removeAttribute('aria-current');
});
const name = selected.dataset.view!;
document.querySelector('#rail-view-title')!.textContent = name;
document.querySelector('#rail-view-description')!.textContent = descriptions[name];
}
window.addEventListener('hashchange', selectSection);
selectSection();Initialization
document.addEventListener('DOMContentLoaded', function () {
const elems = document.querySelectorAll('.sidenav');
const instances = Sidenav.init(elems, {
// specify options here
});
});
// Initialize collapsible (uncomment the lines below if you use the dropdown variation)
// const collapsibleElem = document.querySelector('.collapsible');
// const collapsibleInstance = Collapsible.init(collapsibleElem, {
// // specify options here
// });Options
| Name | Type | Default | Description |
|---|---|---|---|
| edge | String | 'left' | Side of screen on which Sidenav appears. |
| draggable | Boolean | true | Allow swipe gestures to open/close Sidenav. |
| dragTargetWidth | String | '10px' | Width of the area where you can start dragging. |
| inDuration | Number | 250 | Length in ms of enter transition. |
| outDuration | Number | 200 | Length in ms of exit transition. |
| onOpenStart | Function | null | Function called when sidenav starts entering. |
| onOpenEnd | Function | null | Function called when sidenav finishes entering. |
| onCloseStart | Function | null | Function called when sidenav starts exiting. |
| onCloseEnd | Function | null | Function called when sidenav finishes exiting. |
| preventScrolling | Boolean | true | Prevent page from scrolling while sidenav is open. |
Methods
All the methods are called on the plugin instance. You can get the plugin instance like this:
const instance = Sidenav.getInstance(elem);
.open();
Opens Sidenav.
instance.open();.close();
Closes Sidenav.
instance.close();.destroy();
Destroy plugin instance and teardown
instance.destroy();Properties
| Name | Type | Description |
|---|---|---|
| el | Element | The DOM element the plugin was initialized with. |
| options | Object | The options the instance was initialized with. |
| isOpen | Boolean | Describes open/close state of Sidenav. |
| isFixed | Boolean | Describes if sidenav is fixed. |
| isDragged | Boolean | Describes if Sidenav is being dragged. |
Close Trigger
Add the class
.sidenav-close to an element inside the sidenav and any click event on that element will cause the sidenav to close. This is
useful in Single Page Apps where the page does not refresh on link clicks.
<ul id="slide-out" class="sidenav">
<li><a class="sidenav-close" href="#!">Clicking this will close Sidenav</a></li>
</ul>
<a href="#" data-target="slide-out" class="sidenav-trigger">
<i class="material-icons">menu</i>
</a>Variations
Here are some useful variations and additional elements you can add to your sidebar.
Dropdown HTML Structure
Add collapsible menus to your sidebar.
<ul id="slide-out" class="sidenav">
<li><a href="#!">First Sidebar Link</a></li>
<li><a href="#!">Second Sidebar Link</a></li>
<li class="no-padding">
<ul class="collapsible collapsible-accordion">
<li>
<a class="collapsible-header">
Dropdown
<i class="material-icons">arrow_drop_down</i>
</a>
<div class="collapsible-body">
<ul>
<li><a href="#!">First</a></li>
<li><a href="#!">Second</a></li>
<li><a href="#!">Third</a></li>
<li><a href="#!">Fourth</a></li>
</ul>
</div>
</li>
</ul>
</li>
</ul>
<ul class="right hide-on-med-and-down">
<li><a href="#!">First Sidebar Link</a></li>
<li><a href="#!">Second Sidebar Link</a></li>
<li>
<a class="dropdown-trigger" href="#!" data-target="dropdown1">
Dropdown
<i class="material-icons right">arrow_drop_down</i>
</a>
</li>
<ul id="dropdown1" class="dropdown-content">
<li><a href="#!">First</a></li>
<li><a href="#!">Second</a></li>
<li><a href="#!">Third</a></li>
<li><a href="#!">Fourth</a></li>
</ul>
</ul>
<a href="#" data-target="slide-out" class="sidenav-trigger">
<i class="material-icons">menu</i>
</a>Fullscreen HTML Structure
If you want the menu to be accessible on all screensizes you just have to add a simple helper class
show-on-large to the
.sidenav-trigger.
<ul id="slide-out" class="sidenav">
<li><a href="#!">First Sidebar Link</a></li>
<li><a href="#!">Second Sidebar Link</a></li>
</ul>
<a href="#" data-target="slide-out" class="sidenav-trigger show-on-large">
<i class="material-icons">menu</i>
</a>Fixed HTML Structure
Add the class
sidenav-fixed to have the sidenav be fixed and open on large screens and hides to the regular functionality on smaller screens.
Our sidenav on the left is an example of this.
<ul id="slide-out" class="sidenav sidenav-fixed">
<li><a href="#!">First Sidebar Link</a></li>
<li><a href="#!">Second Sidebar Link</a></li>
</ul>
<a href="#" data-target="slide-out" class="sidenav-trigger">
<i class="material-icons">menu</i>
</a>If you are planning on using this you will have to offset your content by the width of the side menu. Place the padding on where the offset content will be, which in our case is in header, main and footer.
header,
main,
footer {
padding-left: 300px;
}
@media only screen and (max-width: 992px) {
header,
main,
footer {
padding-left: 0;
}
}