Skip to content

Tailwind Dropdown

Dropdown menus reveal a set of options relative to a trigger button, commonly used for user menus, filters, and action lists. This lesson builds a complete, accessible Tailwind dropdown.

What Makes Up a Tailwind Dropdown?

A dropdown pairs a relative trigger button with an absolute positioned menu panel, exactly the relative/absolute pattern from the Position lesson, toggled open and closed by a small amount of JavaScript.

Beyond visual positioning, a fully accessible dropdown supports keyboard navigation between menu items using arrow keys, and closes when focus or a click moves outside of it.

<div class="relative inline-block">
  <button class="rounded-lg border px-4 py-2">Options ▾</button>
  <div class="absolute right-0 mt-2 w-48 rounded-lg border bg-white p-1 shadow-lg">
    <a href="/profile" class="block rounded-md px-3 py-2 text-sm hover:bg-slate-100">Profile</a>
    <a href="/settings" class="block rounded-md px-3 py-2 text-sm hover:bg-slate-100">Settings</a>
  </div>
</div>

The menu panel is absolutely positioned relative to its relative parent, offset below the trigger with mt-2.

Dropdown Structure Syntax

<div class="relative inline-block">
  <button aria-haspopup="true" aria-expanded="false">Trigger</button>
  <div role="menu" class="absolute ...">
    <a role="menuitem" href="...">Option</a>
  </div>
</div>
  • relative on the wrapper establishes the positioning context for the menu.
  • absolute on the menu panel positions it relative to the trigger, not the page.
  • aria-haspopup and aria-expanded communicate the trigger's popup state.
  • role="menu" and role="menuitem" describe the dropdown's structure to assistive technology.

Dropdown Cheatsheet

Utility combinations for building a complete dropdown menu.

Piece Classes Purpose
Wrapper relative inline-block Positioning context for the menu
Trigger button rounded-lg border px-4 py-2 Opens/closes the dropdown
Menu panel absolute right-0 mt-2 w-48 rounded-lg border bg-white shadow-lg The floating options list
Menu item block px-3 py-2 text-sm hover:bg-slate-100 An individual clickable option
Divider my-1 border-t Separates groups of menu items
Right-aligned menu right-0 Menu aligns to the trigger's right edge
Left-aligned menu left-0 Menu aligns to the trigger's left edge
High z-index z-20 Keeps the menu above surrounding content

Toggling the Dropdown With JavaScript

A minimal dropdown toggles a hidden class on click and closes automatically when the user clicks anywhere outside of it.

<script>
  const trigger = document.getElementById("menu-trigger");
  const menu = document.getElementById("menu-panel");

  trigger.addEventListener("click", () => {
    const isOpen = !menu.classList.contains("hidden");
    menu.classList.toggle("hidden");
    trigger.setAttribute("aria-expanded", String(!isOpen));
  });

  document.addEventListener("click", (e) => {
    if (!trigger.contains(e.target) && !menu.contains(e.target)) {
      menu.classList.add("hidden");
      trigger.setAttribute("aria-expanded", "false");
    }
  });
</script>

Accessible Dropdown Menus

Dropdown triggers and panels need proper ARIA roles and keyboard support so users relying on assistive technology or a keyboard alone can operate them just as easily as with a mouse.

  • Use aria-haspopup="true" and aria-expanded on the trigger button.
  • Support arrow key navigation between menu items when using role="menu"/role="menuitem".
  • Close the menu on Escape and return focus to the trigger button.

Common Mistakes

  • Forgetting relative on the wrapper, causing the absolutely positioned menu to place itself relative to the wrong ancestor.
  • Not closing the dropdown when the user clicks outside of it, leaving it stuck open.
  • Missing aria-expanded and aria-haspopup, leaving screen reader users unaware the trigger opens a menu.
  • Using a low z-index that lets nearby content render above the open dropdown panel.
  • Not supporting keyboard arrow key navigation, which is expected behavior for role="menu" components.

Key Takeaways

  • A dropdown pairs a relative trigger wrapper with an absolute positioned menu panel.
  • The menu should close on outside click and Escape, not only via the trigger button.
  • aria-haspopup and aria-expanded communicate the dropdown's state to assistive technology.
  • Menu alignment (left-0 vs right-0) should match the trigger's position on the page.
  • Dividers and icons improve scannability for longer dropdown menus.

Pro Tip

If your framework has an accessible primitive library available (Headless UI, Radix, Reka UI), use it for dropdown behavior and keyboard handling, then apply your own Tailwind classes for the visual design, it saves significant accessibility testing effort.