Skip to content

Slide-In Mobile Menu

Tailwind CSS off-canvas mobile menu that slides in from the left with a backdrop, focus trap, Escape to close and an expandable submenu.

Live preview

Open in new tab

Code

An off-canvas mobile menu built with Tailwind CSS that slides in from the left over a blurred backdrop. The drawer has a search field, icon links, an expandable submenu, account links and a sign-in button, and it behaves like a proper modal with a focus trap, Escape to close and scroll locking. It fits online shops, blogs and any site that needs a compact navigation on phones.

How it works

The root .tsm-root contains the top bar with the data-tsm-open hamburger button and a fixed layer marked data-tsm-layer, which starts with hidden and data-open="false". The layer is a named Tailwind group (group/layer): the drawer #tsm-drawer sits at -translate-x-full and moves to translate-x-0 through group-data-[open=true]/layer:, while the backdrop fades from opacity-0 to opacity-100 the same way.

script.js initializes every .tsm-root on DOMContentLoaded. Opening removes hidden, forces a reflow so the slide transition runs, sets data-open="true", locks page scrolling on the html element and moves focus to the close button. A keydown listener closes on Escape and keeps Tab and Shift+Tab inside the drawer, and a focusin listener pulls stray focus back. Clicking the backdrop, the close button (both data-tsm-close) or any link closes the drawer; the layer is hidden again after TRANSITION_MS and focus returns to the hamburger. Buttons with data-tsm-sub toggle their submenu through aria-controls and aria-expanded.

Customization

  • w-[86%] max-w-xs on #tsm-drawer: make the drawer wider (max-w-sm) or narrower.
  • left-0, -translate-x-full and rounded-r-3xl on #tsm-drawer: switch to right-0, translate-x-full and rounded-l-3xl to slide in from the right.
  • bg-orange-600, aria-[current=page]:bg-orange-50 and focus-visible:outline-orange-600: replace orange with your brand color.
  • bg-stone-950/50 backdrop-blur-[2px] on the backdrop: adjust the dimming or remove the blur.
  • duration-300 on the drawer and backdrop: change the slide speed and update TRANSITION_MS in script.js to match.
  • Add lg:hidden to the data-tsm-open button and show an inline link list from lg up for a classic responsive header.
  • data-tsm-search: the demo form only prevents submission; send the query to your search page in its submit handler.

Accessibility

The drawer uses role="dialog", aria-modal="true" and aria-labelledby="tsm-drawer-title". The hamburger has aria-haspopup="dialog", aria-controls and an aria-expanded state, plus a visually hidden "Open menu" label; the cart link and close button have text labels too. The current page is marked with aria-current="page", the search form has role="search" and a hidden label, and focus is trapped while open and restored on close. Every control shows an orange focus outline, and transitions are turned off with motion-reduce:transition-none.

Usage notes

For several menus on one page, give each drawer, title and submenu unique id values.

Requirements

Add these to your page (already included in the download):

  • <script src="https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4"></script>

The Tailwind browser build is for prototyping. In production, compile Tailwind with its CLI or your bundler.

Slide-In Mobile Menu – Tailwind CSS mobile menus component screenshot
Slide-In Mobile Menu · Tailwind CSS Mobile Menus

More Mobile Menus

Full-Screen Overlay Mobile Menu – Pure CSS mobile menus preview

Full-Screen Overlay Mobile Menu

Hamburger button that opens a full-screen menu with a circular clip-path reveal, staggered serif links, focus trap and Escape to close, in pure CSS and vanilla JS.