SolutionsCase StudiesAI ToolsResourcesAboutContact
Get Your Free AI Automation Audit Book a Free 30-Minute Strategy Call
Playground~/frontend Open editor

~/css/tailwind-dropdown-menus

6 Tailwind Dropdown Menus

3 Tailwind3 with JSPublished 2026-10-04Free · no sign-up

Six dropdowns in Tailwind: an action menu with arrow-key support, a navbar submenu on hover and focus, an account menu, a no-JavaScript popover menu, a mega menu panel and a split Save button.

01. Tailwind Action Menu

TailwindARIA

A menu button with a list of actions styled in Tailwind and wired with the ARIA menu pattern: arrow keys move between items, Escape closes and returns focus to the button, and the destructive Delete item is separated and coloured red.

View code
<div class="relative" id="twm">
  <button id="twm-b" aria-haspopup="true" aria-expanded="false" aria-controls="twm-m" class="inline-flex items-center gap-2 rounded-lg border border-slate-300 bg-white px-4 py-2.5 text-sm font-bold text-slate-900 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-blue-600">Options <span aria-hidden="true">&#9662;</span></button>
  <ul id="twm-m" role="menu" aria-labelledby="twm-b" hidden class="absolute left-0 mt-1.5 w-48 rounded-xl border border-slate-200 bg-white p-1.5 shadow-lg">
    <li role="none"><button role="menuitem" class="w-full rounded-lg px-2.5 py-2 text-left text-sm text-slate-700 hover:bg-slate-100 focus:bg-slate-100 focus:outline-none">Rename</button></li>
    <li role="none"><button role="menuitem" class="w-full rounded-lg px-2.5 py-2 text-left text-sm text-slate-700 hover:bg-slate-100 focus:bg-slate-100 focus:outline-none">Move to folder</button></li>
    <li role="none" class="mt-1 border-t border-slate-100 pt-1"><button role="menuitem" class="w-full rounded-lg px-2.5 py-2 text-left text-sm text-red-700 hover:bg-red-50 focus:bg-red-50 focus:outline-none">Delete</button></li>
  </ul>
</div>
const tb = document.getElementById('twm-b'), tm = document.getElementById('twm-m'), ti = [...tm.querySelectorAll('[role=menuitem]')];
function set(o) { tm.hidden = !o; tb.setAttribute('aria-expanded', String(o)); if (o) ti[0].focus(); }
tb.addEventListener('click', () => set(tm.hidden));
tm.addEventListener('keydown', e => {
  const i = ti.indexOf(document.activeElement);
  if (e.key === 'ArrowDown') { e.preventDefault(); ti[(i + 1) % ti.length].focus(); }
  if (e.key === 'ArrowUp') { e.preventDefault(); ti[(i - 1 + ti.length) % ti.length].focus(); }
  if (e.key === 'Escape') { set(false); tb.focus(); }
});
ti.forEach(x => x.addEventListener('click', () => { set(false); tb.focus(); }));
document.addEventListener('click', e => { if (!e.target.closest('#twm')) { tm.hidden = true; tb.setAttribute('aria-expanded', 'false'); } });

02. Tailwind Nav Dropdown

TailwindNavigation

A navbar item that reveals a submenu on hover and whenever anything inside has focus, using group-hover: and group-focus-within: with invisible and opacity utilities. A small transition delay keeps it open while the pointer moves into the menu.

View code
<nav aria-label="Main" class="rounded-xl bg-slate-900 p-1.5">
  <ul class="flex gap-1 text-sm font-semibold">
    <li><a href="#" class="block rounded-lg px-3.5 py-2 text-slate-200 hover:bg-slate-800">Home</a></li>
    <li class="group relative"><a href="#" aria-haspopup="true" class="block rounded-lg px-3.5 py-2 text-slate-200 hover:bg-slate-800">Products &#9662;</a>
      <ul class="invisible absolute left-0 top-full mt-2 w-44 rounded-xl border border-slate-700 bg-slate-950 p-1.5 opacity-0 transition delay-100 group-hover:visible group-hover:opacity-100 group-hover:delay-0 group-focus-within:visible group-focus-within:opacity-100 before:absolute before:inset-x-0 before:-top-2 before:h-2">
        <li><a href="#" class="block rounded-lg px-3 py-2 font-medium text-slate-300 hover:bg-slate-800 hover:text-white focus:bg-slate-800 focus:outline-none">Storefront</a></li>
        <li><a href="#" class="block rounded-lg px-3 py-2 font-medium text-slate-300 hover:bg-slate-800 hover:text-white focus:bg-slate-800 focus:outline-none">Payments</a></li>
        <li><a href="#" class="block rounded-lg px-3 py-2 font-medium text-slate-300 hover:bg-slate-800 hover:text-white focus:bg-slate-800 focus:outline-none">Shipping</a></li>
      </ul>
    </li>
    <li><a href="#" class="block rounded-lg px-3.5 py-2 text-slate-200 hover:bg-slate-800">Pricing</a></li>
  </ul>
</nav>

03. Tailwind Account Dropdown

TailwindAvatar

An avatar button that opens the account panel with name, email, links and a sign-out action. The panel is toggled with aria-expanded, closes on Escape and outside click, and the avatar's accessible name includes the person's name.

View code
<div class="relative flex w-64 justify-end" id="twac">
  <button aria-expanded="false" aria-controls="twac-p" aria-label="Account menu for Leo Park" class="grid h-10 w-10 place-items-center rounded-full bg-gradient-to-br from-emerald-400 to-cyan-500 text-sm font-extrabold text-white ring-2 ring-white focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-blue-600">LP</button>
  <div id="twac-p" class="absolute right-0 top-12 w-56 rounded-xl border border-slate-200 bg-white p-2 text-sm shadow-xl">
    <p class="px-2 pb-2 pt-1"><b class="block text-slate-900">Leo Park</b><span class="text-slate-500">leo@harbor.app</span></p>
    <a href="#" class="block rounded-lg px-2 py-2 text-slate-700 hover:bg-slate-100">Profile</a>
    <a href="#" class="block rounded-lg px-2 py-2 text-slate-700 hover:bg-slate-100">Billing</a>
    <hr class="my-1.5 border-slate-100">
    <button class="w-full rounded-lg px-2 py-2 text-left text-red-700 hover:bg-red-50">Sign out</button>
  </div>
</div>
const ab = document.querySelector('#twac > button'), ap = document.getElementById('twac-p');
function set(o) { ap.hidden = !o; ab.setAttribute('aria-expanded', String(o)); }
ab.addEventListener('click', () => set(ap.hidden));
document.addEventListener('keydown', e => { if (e.key === 'Escape' && !ap.hidden) { set(false); ab.focus(); } });
document.addEventListener('click', e => { if (!e.target.closest('#twac')) set(false); });
set(true);

04. Tailwind Popover Dropdown

TailwindPopover

A share dropdown using the native popover attribute for opening, light dismiss and Escape, styled only with Tailwind classes. It needs no JavaScript at all, which makes it a good default for simple menus of links.

View code
<div>
  <button popovertarget="twpop-m" class="rounded-lg border border-slate-700 bg-slate-900 px-4 py-2.5 text-sm font-bold text-white focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-sky-400">Export</button>
  <div id="twpop-m" popover class="m-auto w-48 rounded-xl border border-slate-700 bg-slate-950 p-1.5 text-sm backdrop:bg-black/40">
    <a href="#" class="block rounded-lg px-3 py-2 text-slate-200 hover:bg-slate-800 focus:bg-slate-800 focus:outline-none">CSV file</a>
    <a href="#" class="block rounded-lg px-3 py-2 text-slate-200 hover:bg-slate-800 focus:bg-slate-800 focus:outline-none">Excel workbook</a>
    <a href="#" class="block rounded-lg px-3 py-2 text-slate-200 hover:bg-slate-800 focus:bg-slate-800 focus:outline-none">PDF report</a>
  </div>
</div>

05. Tailwind Mega Menu

TailwindLayout

A wide dropdown panel with two link columns and a promotional tile, toggled by a disclosure button. grid-cols-1 on phones grows to three columns from the sm breakpoint, and the panel uses ring and shadow utilities for depth.

View code
<nav aria-label="Main" class="w-full max-w-xl">
  <button aria-expanded="true" aria-controls="twmega" onclick="var o=this.getAttribute('aria-expanded')!=='true';this.setAttribute('aria-expanded',o);document.getElementById('twmega').hidden=!o" class="rounded-lg bg-slate-900 px-4 py-2.5 text-sm font-bold text-white focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-blue-600">Shop</button>
  <div id="twmega" class="mt-2.5 grid grid-cols-1 gap-5 rounded-2xl bg-white p-5 shadow-xl ring-1 ring-slate-200 sm:grid-cols-3">
    <div><h3 class="mb-2 text-xs font-bold uppercase tracking-wider text-slate-500">Women</h3><ul class="space-y-1.5 text-sm"><li><a href="#" class="text-slate-900 hover:text-blue-700">Dresses</a></li><li><a href="#" class="text-slate-900 hover:text-blue-700">Knitwear</a></li><li><a href="#" class="text-slate-900 hover:text-blue-700">Shoes</a></li></ul></div>
    <div><h3 class="mb-2 text-xs font-bold uppercase tracking-wider text-slate-500">Men</h3><ul class="space-y-1.5 text-sm"><li><a href="#" class="text-slate-900 hover:text-blue-700">Shirts</a></li><li><a href="#" class="text-slate-900 hover:text-blue-700">Outerwear</a></li><li><a href="#" class="text-slate-900 hover:text-blue-700">Boots</a></li></ul></div>
    <a href="#" class="flex flex-col justify-end rounded-xl bg-gradient-to-br from-amber-200 to-orange-400 p-4 text-sm text-orange-950"><b>Winter edit</b><span>Layers for cold mornings</span></a>
  </div>
</nav>

06. Tailwind Split Button

TailwindButton

A Save button with a joined arrow button that opens extra save options. Both halves sit in one wrapper with overflow-hidden and rounded corners, so they read as a single control, and each half is its own button with a descriptive accessible name.

View code
<div class="relative" id="twsp">
  <div class="inline-flex overflow-hidden rounded-lg">
    <button class="bg-emerald-400 px-4 py-2.5 text-sm font-bold text-emerald-950 focus-visible:outline focus-visible:outline-2 focus-visible:-outline-offset-4 focus-visible:outline-emerald-950">Save</button>
    <button aria-label="More save options" aria-expanded="false" aria-controls="twsp-m" class="border-l border-emerald-950/25 bg-emerald-400 px-3 text-emerald-950 focus-visible:outline focus-visible:outline-2 focus-visible:-outline-offset-4 focus-visible:outline-emerald-950">&#9662;</button>
  </div>
  <ul id="twsp-m" hidden class="absolute left-0 mt-1.5 w-52 rounded-xl border border-slate-700 bg-slate-950 p-1.5 text-sm">
    <li><button class="w-full rounded-lg px-3 py-2 text-left text-slate-200 hover:bg-slate-800 focus:bg-slate-800 focus:outline-none">Save as template</button></li>
    <li><button class="w-full rounded-lg px-3 py-2 text-left text-slate-200 hover:bg-slate-800 focus:bg-slate-800 focus:outline-none">Save and close</button></li>
  </ul>
</div>
const sa = document.querySelector('[aria-controls=twsp-m]'), smn = document.getElementById('twsp-m');
sa.addEventListener('click', () => { const o = smn.hidden; smn.hidden = !o; sa.setAttribute('aria-expanded', String(o)); if (o) smn.querySelector('button').focus(); });
document.addEventListener('keydown', e => { if (e.key === 'Escape' && !smn.hidden) { smn.hidden = true; sa.setAttribute('aria-expanded', 'false'); sa.focus(); } });

About these Tailwind Dropdown Menus

Hover and focus together

The nav dropdown uses group-hover:visible and group-focus-within:visible, so keyboard users can open it by tabbing in. An invisible before: bridge stops it closing in the gap below the link.

Visibility, not display

Using invisible and opacity utilities lets the menu fade, and invisible elements cannot be focused, so hidden links stay out of the Tab order.

Popover without script

The popover attribute handles opening, closing on Escape and outside clicks. Tailwind's backdrop: variant can style its backdrop.

Menus for actions

The ARIA menu pattern suits application actions; for site navigation a simple disclosure with links, like the mega menu, is easier to use.

Frequently asked questions

Which demos use JavaScript?

The action menu, account menu and split button. The nav dropdown and popover are pure HTML and Tailwind.

How do I position menus near their button?

Use a relative wrapper with an absolutely positioned list, or CSS anchor positioning for popovers.

Is there a CSS version?

Yes, see the CSS dropdown menus collection.