webdevpuneet.com

Off-Canvas Push Menu — Free HTML CSS JS Sliding Navigation Snippet

Off-Canvas Push Menu · Navigation · Plain HTML, CSS & JS · Live preview

CategoryNavigation

Copy & paste

Source Code

<div class="app" id="app">
  <nav class="off-canvas" id="offCanvas" aria-hidden="true">
    <div class="oc-header">
      <span class="oc-brand">Acme Inc.</span>
      <button class="oc-close" onclick="closeMenu()" aria-label="Close menu">&times;</button>
    </div>
    <a href="#" class="oc-link">Home</a>
    <a href="#" class="oc-link">Products</a>
    <a href="#" class="oc-link">Pricing</a>
    <a href="#" class="oc-link">About</a>
    <a href="#" class="oc-link">Contact</a>
  </nav>
  <div class="page-wrap" id="pageWrap">
    <header class="topbar">
      <button class="hamburger" id="hamburger" onclick="toggleMenu()" aria-label="Open menu" aria-expanded="false" aria-controls="offCanvas">
        <span></span><span></span><span></span>
      </button>
      <span class="brand">Acme Inc.</span>
    </header>
    <main class="content">
      <h1>Welcome back</h1>
      <p>This is the page content. When the menu opens, this entire wrapper slides sideways on the X axis instead of a drawer sliding over it — a push-style off-canvas layout.</p>
      <div class="card">Some page content block</div>
      <div class="card">Another content block</div>
    </main>
  </div>
  <div class="scrim" id="scrim" onclick="closeMenu()"></div>
</div>

Extend with AI

Take This Snippet Further With AI

The AI prompt for this one is for Patreon supporters ($3 a month, 7 days free), along with a site with no ads. Supporters enter the passcode once and it is remembered in this browser.

· Become a supporter

What's included

Features

True push effect — the whole page shifts via transform, the menu is not an overlay drawer
Single shared 0.3s transition keeps the sidebar and page perfectly in sync while animating
Hamburger-to-X animation driven by the aria-expanded attribute, not a separate class
Scrim overlay catches outside clicks to close the menu without extra event listener setup
overflow-x: hidden on the app wrapper prevents any horizontal scrollbar during the shift
aria-hidden and aria-expanded toggled in JS keep assistive tech in sync with the visual state
Sidebar width is controlled from two matching CSS values — easy to resize consistently
Any number of nav links can be added with no JS or layout changes required
Works with three independent close triggers: close button, scrim click, and hamburger toggle
No framework, no drawer library, no build step required

About this UI Snippet

Off-Canvas Push Menu — HTML, CSS & JavaScript Sliding Page Navigation

Screenshot of the Off-Canvas Push Menu snippet rendered live

Most mobile navigation drawers are overlays: a panel slides in on top of the page while the page itself stays put. A push-style off-canvas menu is different — the entire visible page shifts sideways on the X axis to reveal a hidden sidebar underneath it. It's the pattern used by older Facebook and many native-feeling mobile web apps because it makes the menu feel like a physical layer beneath the content rather than a modal floating above it.

This snippet builds that effect with plain HTML, CSS, and vanilla JavaScript. There are three sibling elements inside .app: the .off-canvas sidebar positioned absolutely at the left edge, the .page-wrap that contains the topbar and all page content, and a .scrim overlay used only to catch outside clicks. No layout library, no drawer component, no npm package.

How the push effect works

The trick is that .off-canvas starts at transform: translateX(-100%) — fully hidden off the left edge of the viewport — while .page-wrap sits on top of it at transform: translateX(0). When the menu opens, two things happen at once: the off-canvas panel animates to translateX(0) to slide into view, and .page-wrap gets a .shifted class that animates it to transform: translateX(240px) — the exact width of the sidebar. Because both elements share the same 0.3s ease transition, the page appears to be *pushed* to the right by the menu sliding in underneath it, rather than the menu sliding on top of the page. The parent .app has overflow-x: hidden so the page-wrap's rightward shift never creates a horizontal scrollbar.

How the hamburger-to-X animation works

The hamburger icon is three <span> bars inside a button. When aria-expanded="true" is set on the button, three sibling selectors kick in: the top bar translates down 7px and rotates 45deg, the middle bar fades to opacity: 0, and the bottom bar translates up 7px and rotates -45deg. Together the two remaining bars form an X. Driving this off the aria-expanded attribute rather than a separate class means the visual state and the accessibility state can never drift out of sync.

Closing the menu

Three things close the menu: clicking the close button inside the panel, clicking the semi-transparent scrim that covers the page while the menu is open, or clicking the hamburger again. All three call the same closeMenu() function, which resets the transforms, removes the .shifted and .visible classes, and flips aria-expanded and aria-hidden back.

Changing the sidebar width

The sidebar width is set in exactly two places that must match: .off-canvas { width: 240px } and .page-wrap.shifted { transform: translateX(240px) }. Change both values together — if they diverge, the page will either overshoot the sidebar's edge or leave a gap.

Step by step

How to Use

  1. 1
    Load the snippetClick "Off-Canvas Push Menu" in the sidebar Library tab to load the HTML, CSS, and JS panels and see the closed state in the preview.
  2. 2
    Open the menuClick the hamburger icon in the preview. Watch the page content shift right as the dark sidebar slides in from underneath it, and the hamburger morph into an X.
  3. 3
    Close it three waysClick the X inside the sidebar, click the dimmed scrim area, or click the hamburger again — all three should close the menu identically.
  4. 4
    Resize the sidebarIn the CSS panel, change both the width on .off-canvas and the translateX value on .page-wrap.shifted to the same new value, then reopen the menu to confirm the page shifts exactly that far.
  5. 5
    Add or edit linksIn the HTML panel, add more .oc-link anchors inside the nav — the flex layout and hover states apply automatically to any number of links.
  6. 6
    Export in your formatClick "HTML" for a standalone file, "JSX" for React, or "Tailwind" for a React + Tailwind version. "Copy all" copies everything to the clipboard.

Real-world uses

Common Use Cases

Mobile-first app navigation
Use the push menu as the primary navigation for a mobile web app where a floating overlay drawer would feel disconnected from the page underneath it.
Learn the transform-based push technique
Study how two sibling elements animate in lockstep with matching transform values and a shared transition to produce the illusion of one panel pushing another.
Prototype a native-feeling web app shell
Drop this into a prototype where you want the navigation to feel like a physical layer of the interface rather than a modal floating above the content.
Match your brand palette
Restyle the dark sidebar and topbar colors to your brand, then save the customized version under its own name for reuse across projects.
Practice accessible disclosure patterns
The aria-expanded and aria-hidden wiring here is a good reference for building your own accessible toggle-based disclosure widgets elsewhere on a site.
Convert to a React component
Use the JSX export as a base — replace the toggleMenu/closeMenu functions with a single isOpen state variable and derive both the sidebar and page-wrap classes from it.
Related: Radial Right-Click Menu
See the Radial Right-Click Menu for a related navigation pattern worth pairing with this one.

Got questions?

Frequently Asked Questions

It is a navigation pattern where opening the menu shifts the entire visible page sideways to reveal a hidden sidebar underneath it, instead of sliding a drawer on top of the page. Both the sidebar and the page move using CSS transforms so they appear physically connected.

A normal drawer is an overlay: it slides in above the page using a higher z-index while the page stays fixed in place and is often dimmed with a scrim. Here, the page itself moves via transform: translateX, so the menu feels like it lives beneath the content and pushes it aside rather than covering it.

Update the width value on .off-canvas and the translateX distance on .page-wrap.shifted to the same new number — for example 280px in both places. They must match exactly or the page will either overshoot the sidebar edge or leave a visible gap.

The outer .app container has overflow-x: hidden, which clips anything that extends past the viewport width during the transform animation, including the momentary overhang while the page-wrap is sliding.

The three bars are span elements targeted with nth-child selectors that key off the button's aria-expanded attribute. When it is true, the top and bottom bars rotate 45 and -45 degrees and translate toward the center, and the middle bar fades out, forming an X shape purely with CSS transitions.

Yes. Move .off-canvas to right: 0 instead of left: 0, change its initial transform to translateX(100%), and change .page-wrap.shifted to translateX(-240px) so the page shifts left instead of right.

It works at any viewport width, but the push effect is most natural on narrower screens where a permanent sidebar would not fit. On wide desktop layouts you may prefer a permanently visible sidebar instead of a toggled one.

Yes. Add a details/summary element or a small accordion pattern in place of an .oc-link anchor. The sidebar's fixed width and vertical layout accommodate nested content as long as you manage its own expand/collapse state separately from the outer menu.

Yes. While the Off-Canvas Push Menu snippet gives you one pattern to copy, the free Responsive Navbar Builder at webdevpuneet.com/navbar-builder/ designs a whole navigation bar: logo, links and dropdown menus, a mobile hamburger menu (dropdown panel, slide-in drawer or full screen) and a scroll effect (sticky, shrink, hide on scroll or transparent). It has six presets, a live preview that runs the real code, and exports HTML + CSS + vanilla JS, React, Tailwind, Vue 3 or Angular — or you can click Fork & Edit to keep editing it in My Code.