0Pricing
Tailwind CSS Academy · Lesson

Collapsible Mobile Sidebar

Implement a slide-in drawer sidebar for mobile with a JavaScript toggle, overlay backdrop, and smooth translate transition.

Collapsible Mobile Sidebar is a free Tailwind CSS Academy lesson on CoddyKit — lesson 4 of 4. You can read the complete lesson below for free — then practise it hands-on in the browser with a built-in code editor and a 24/7 AI tutor. It is part of the Tailwind CSS Academy learning path, one of 4 lessons in the course, and your progress syncs across the web and the CoddyKit app.

What Is a Mobile Drawer Sidebar

A mobile drawer sidebar slides in from the left edge of the screen when a hamburger button is tapped, and slides out when dismissed. Unlike a desktop sidebar that is always visible, the mobile drawer is hidden off-screen by default using a CSS transform.

Three elements work together: the backdrop overlay that darkens the rest of the page, the sidebar panel that slides in, and the toggle button in the top bar that opens and closes the drawer.

<!-- Closed state: sidebar off-screen left -->
<aside id="drawer" class="fixed inset-y-0 left-0 w-72 bg-white z-50
                          -translate-x-full transition-transform duration-300">
  <!-- sidebar content -->
</aside>

<!-- Backdrop: hidden until drawer opens -->
<div id="backdrop" class="fixed inset-0 bg-black/50 z-40 hidden"></div>

The Open/Close JavaScript Toggle

The toggle works by removing the -translate-x-full class from the drawer and removing the hidden class from the backdrop. Closing reverses both operations.

A simple helper function openDrawer() and closeDrawer() keeps the logic clean. Add overflow-hidden to the body when the drawer is open to prevent scrolling the page content behind the overlay.

<script>
  const drawer = document.getElementById('drawer');
  const backdrop = document.getElementById('backdrop');

  function openDrawer() {
    drawer.classList.remove('-translate-x-full');
    backdrop.classList.remove('hidden');
    document.body.classList.add('overflow-hidden');
  }

  function closeDrawer() {
    drawer.classList.add('-translate-x-full');
    backdrop.classList.add('hidden');
    document.body.classList.remove('overflow-hidden');
  }

  // Clicking backdrop closes the drawer
  backdrop.addEventListener('click', closeDrawer);
</script>

Slide-In Transition With Tailwind

Tailwind's transition-transform duration-300 on the drawer panel creates a smooth slide animation when -translate-x-full is toggled. The duration-300 sets a 300ms ease transition, which feels snappy but not jarring.

You can customize the easing with ease-in-out and adjust the duration to duration-200 for a faster feel on simpler UIs. The key is that no custom CSS keyframes are needed — Tailwind's transition utilities handle it all.

<!-- Drawer with slide transition -->
<aside
  id="drawer"
  class="fixed inset-y-0 left-0 w-72 bg-gray-900 text-white z-50
         -translate-x-full transition-transform duration-300 ease-in-out
         flex flex-col">
  <!-- Header -->
  <div class="flex items-center justify-between px-5 py-4 border-b border-gray-800">
    <span class="font-bold text-white">Menu</span>
    <button onclick="closeDrawer()" class="text-gray-400 hover:text-white">
      <svg class="w-5 h-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
        <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"/>
      </svg>
    </button>
  </div>
</aside>

Backdrop Overlay Styling

The backdrop overlay dims the content behind the open drawer, visually communicating that the drawer is a focused panel requiring attention. Use fixed inset-0 bg-black/50 z-40 so it covers the full viewport at a 50% opacity black color.

The backdrop sits at z-40 (below the drawer's z-50) so the drawer renders on top. Adding a fade-in transition with transition-opacity duration-300 makes the overlay appear more naturally.

<div
  id="backdrop"
  class="fixed inset-0 bg-black/50 z-40 hidden
         transition-opacity duration-300">
</div>

Drawer Navigation Links

Inside the drawer, navigation links use the same pattern as the desktop sidebar but can use slightly larger text since this is a touch interface: text-base instead of text-sm for better readability on mobile screens.

Add tap-highlight-color: transparent behavior by using active:bg-gray-800 to give immediate visual feedback when a link is tapped. The list is scrollable using flex-1 overflow-y-auto.

<nav class="flex-1 overflow-y-auto px-4 py-6 space-y-1">
  <a href="/" onclick="closeDrawer()"
     class="flex items-center gap-3 px-3 py-3 rounded-lg text-base font-medium
            bg-gray-800 text-white">
    Home
  </a>
  <a href="/features" onclick="closeDrawer()"
     class="flex items-center gap-3 px-3 py-3 rounded-lg text-base font-medium
            text-gray-300 hover:bg-gray-800 hover:text-white
            active:bg-gray-800 transition-colors">
    Features
  </a>
  <a href="/pricing" onclick="closeDrawer()"
     class="flex items-center gap-3 px-3 py-3 rounded-lg text-base font-medium
            text-gray-300 hover:bg-gray-800 hover:text-white
            active:bg-gray-800 transition-colors">
    Pricing
  </a>
</nav>

Hamburger Button in Top Bar

On mobile, the hamburger button lives in the top bar that replaces the sidebar. Use lg:hidden on this top bar so it only shows on small screens where the sidebar is hidden.

Give the button a comfortable touch target of at least 44×44px by using p-2 with a 24px icon (w-6 h-6), which totals the 44px recommended by Apple's HIG and Google's Material Design guidelines.

<header class="lg:hidden flex items-center justify-between px-4 py-3 bg-white border-b border-gray-200">
  <span class="font-bold text-gray-900">AppDash</span>
  <button
    onclick="openDrawer()"
    aria-label="Open navigation"
    class="p-2 text-gray-600 hover:bg-gray-100 active:bg-gray-200 rounded-lg">
    <svg class="w-6 h-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
      <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16M4 18h16"/>
    </svg>
  </button>
</header>

Keyboard Escape to Close

Accessibility requires that overlay elements like drawers can be dismissed with the Escape key. Add a keydown listener to the document that checks for event.key === 'Escape' and calls closeDrawer().

This is a WCAG 2.1 requirement for all modal surfaces. Without Escape support, keyboard-only users are trapped inside the drawer.

<script>
  document.addEventListener('keydown', (event) => {
    if (event.key === 'Escape') {
      closeDrawer();
    }
  });
</script>

Right-Side Drawer Variant

Some UIs use a right-side drawer for filters, settings panels, or detail views rather than navigation. Replace left-0 with right-0 and change the closed transform from -translate-x-full to translate-x-full so the panel slides in from the right edge.

This is the same technique used for shopping cart sidebars, notification panels, and detail panes in single-page applications.

<!-- Right drawer -->
<aside
  id="right-drawer"
  class="fixed inset-y-0 right-0 w-80 bg-white shadow-xl z-50
         translate-x-full transition-transform duration-300 ease-in-out
         flex flex-col">
  <div class="flex items-center justify-between px-5 py-4 border-b border-gray-100">
    <h2 class="text-lg font-semibold text-gray-900">Filter Options</h2>
    <button onclick="closeRightDrawer()" class="p-1 text-gray-400 hover:text-gray-600 rounded">
      <svg class="w-5 h-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
        <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"/>
      </svg>
    </button>
  </div>
  <div class="flex-1 overflow-y-auto p-5">
    <!-- Filter controls go here -->
  </div>
</aside>

Swipe-to-Close Hint Styling

On touch devices, users often expect to be able to swipe the drawer closed. While actual swipe detection requires JavaScript (Touch Events API), you can provide a visual hint by adding a drag handle at the top of the drawer — a short horizontal bar that signals swipeability.

Style it as a small rounded rectangle: mx-auto mt-2 w-10 h-1 rounded-full bg-gray-300. This is a common UX convention from native mobile apps adapted to the web.

<aside class="fixed inset-y-0 left-0 w-72 bg-gray-900 z-50 flex flex-col -translate-x-full transition-transform duration-300">
  <!-- Drag handle -->
  <div class="flex justify-center pt-3">
    <div class="w-10 h-1 rounded-full bg-gray-600"></div>
  </div>
  <!-- Header -->
  <div class="flex items-center justify-between px-5 py-4 mt-1 border-b border-gray-800">
    <span class="font-bold text-white">Menu</span>
    <button onclick="closeDrawer()" class="text-gray-400 hover:text-white">
      &times;
    </button>
  </div>
</aside>

Focus Trap for Accessibility

When the drawer is open, keyboard focus should be trapped inside it. This prevents Tab key navigation from reaching content hidden behind the overlay, which confuses screen reader users.

While implementing a full focus trap requires JavaScript (cycling through focusable elements with Tab and Shift+Tab), always ensure the close button is the first focusable element inside the drawer and receives autofocus or is programmatically focused when the drawer opens.

<script>
  function openDrawer() {
    const drawer = document.getElementById('drawer');
    const backdrop = document.getElementById('backdrop');
    drawer.classList.remove('-translate-x-full');
    backdrop.classList.remove('hidden');
    document.body.classList.add('overflow-hidden');

    // Move focus into drawer for accessibility
    const closeBtn = drawer.querySelector('button[data-close]');
    if (closeBtn) closeBtn.focus();
  }
</script>

<!-- Close button receives focus first -->
<button
  data-close
  onclick="closeDrawer()"
  class="text-gray-400 hover:text-white focus:outline-none focus-visible:ring-2 focus-visible:ring-white rounded"
  aria-label="Close menu">
  <svg class="w-5 h-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
    <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"/>
  </svg>
</button>

Complete Drawer Layout Assembly

A complete mobile drawer integrates all the pieces: a top bar with the hamburger button, the off-screen drawer panel with a close button and navigation links, and a backdrop overlay. The three pieces are independent HTML elements that JavaScript connects together.

On desktop (lg: breakpoint), hide the top bar with lg:hidden and show the persistent sidebar instead. This single mobile/desktop split is the foundation of every modern dashboard layout.

<!-- Top bar (mobile only) -->
<header class="lg:hidden sticky top-0 bg-white border-b border-gray-200 z-30 flex items-center justify-between px-4 py-3">
  <span class="font-bold">AppDash</span>
  <button onclick="openDrawer()" aria-label="Open menu" class="p-2 text-gray-600 hover:bg-gray-100 rounded-lg">
    <svg class="w-6 h-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
      <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16M4 18h16"/>
    </svg>
  </button>
</header>

<!-- Backdrop -->
<div id="backdrop" class="hidden fixed inset-0 bg-black/50 z-40" onclick="closeDrawer()"></div>

<!-- Drawer panel -->
<aside id="drawer" class="fixed inset-y-0 left-0 w-72 bg-gray-900 text-white z-50 flex flex-col -translate-x-full transition-transform duration-300">
  <div class="flex items-center justify-between px-5 py-4 border-b border-gray-800">
    <span class="font-bold text-white">AppDash</span>
    <button data-close onclick="closeDrawer()" aria-label="Close menu" class="text-gray-400 hover:text-white">&times;</button>
  </div>
  <nav class="flex-1 overflow-y-auto px-4 py-6 space-y-1">
    <a href="/" class="block px-3 py-3 rounded-lg text-base font-medium bg-gray-800 text-white">Home</a>
    <a href="/features" class="block px-3 py-3 rounded-lg text-base font-medium text-gray-300 hover:bg-gray-800 hover:text-white">Features</a>
  </nav>
</aside>

Quick Check

Test your understanding of Tailwind CSS Mastery concepts from this lesson.

Lesson Recap

In this lesson you learned: the mobile drawer uses three elements — backdrop overlay, slide-in panel, and hamburger trigger; the slide animation toggles -translate-x-full with transition-transform duration-300; and accessibility requires Escape-key support, focus management into the drawer on open, and a clickable backdrop to close. Next up we style text inputs and textareas.

Frequently asked questions

Is the “Collapsible Mobile Sidebar” lesson free?

Yes — the full text of “Collapsible Mobile Sidebar” is free to read here on the web, and the Tailwind CSS Academy course includes 4 lessons in total. To practise it interactively (a built-in code editor and a 24/7 AI tutor) and unlock the rest of the Tailwind CSS Academy course, upgrade to CoddyKit PRO.

What will I learn in “Collapsible Mobile Sidebar”?

Implement a slide-in drawer sidebar for mobile with a JavaScript toggle, overlay backdrop, and smooth translate transition. You practise Tailwind CSS Academy with hands-on code you run directly in the browser, and a 24/7 AI tutor answers your questions as you work through the lesson.

Do I need any experience to start Tailwind CSS Academy?

No prior experience is required. Tailwind CSS Academy on CoddyKit is structured for beginners through advanced learners; this is — lesson 4 of 4, so you can start here or from the beginning and move at your own pace.

How long does the “Collapsible Mobile Sidebar” lesson take?

Most CoddyKit lessons take about 5–10 minutes. Each one is bite-sized and interactive, so you make steady progress and pick up exactly where you left off across the web and the app.

Can I write and run code in this Tailwind CSS Academy lesson?

Yes. Every Tailwind CSS Academy lesson includes a built-in code editor, so you write and run real code right in your browser and get instant AI feedback — no local setup required.

All lessons in this course

  1. Building a Responsive Navbar
  2. Sticky and Fixed Navigation
  3. Vertical Sidebar Layout
  4. Collapsible Mobile Sidebar
← Back to Tailwind CSS Academy