Encodera

Thanks for reading!

Donate to the site — we work hard for you.

How to make a dropdown menu

Short answer

details opens and closes by itself, and the list goes under the button with position: absolute:

<style>
  .dd { position: relative; display: inline-block; }
  .dd summary { list-style: none; cursor: pointer; padding: 6px 12px; background: royalblue; color: white; border-radius: 6px; }
  .dd ul { position: absolute; top: 100%; left: 0; z-index: 1; margin: 4px 0 0; padding: 6px 0; list-style: none; min-width: 140px; background: white; border: 1px solid gainsboro; border-radius: 6px; box-shadow: 0 6px 16px rgb(0 0 0 / 15%); }
  .dd a { display: block; padding: 6px 12px; color: inherit; text-decoration: none; }
  .dd a:hover { background: aliceblue; }
</style>
 
<details class="dd">
  <summary>Account ▾</summary>
  <ul><li><a href="https://example.com">Profile</a></li><li><a href="https://example.com">Orders</a></li><li><a href="https://example.com">Log out</a></li></ul>
</details>
<p>Content below the menu</p>

A popover menu

The popover attribute puts the menu in the top layer above everything, and closes it on an outside click and on Esc. A button connects to it with popovertarget:

<button popovertarget="menu">Sort by ▾</button>
<div id="menu" popover style="padding: 8px; border: 1px solid gainsboro; border-radius: 6px;">
  <p style="margin: 4px;">Price</p><p style="margin: 4px;">Rating</p><p style="margin: 4px;">Newest</p>
</div>

By default a popover sits in the middle of the screen. Anchor positioning (position-anchor) ties it to the button, and where that is not supported, a small script does.

Common mistake

A hover-only menu: phones have no hover, and the keyboard cannot open it. If the menu does open on hover, add :focus-within so Tab opens it too.

Functions used in this recipe

  • <details>, <summary> — A disclosure block without JavaScript: `<summary>` is the visible title, the rest is hidden until clicked.
  • position — How an element is positioned: in the normal flow or by offsets.
  • :has() — Matches an element that contains something matching the selector in brackets — the "parent selector".
  • popover — Turns an element into a layer that pops up over the page — without JavaScript.

Thanks for reading!

Donate to the site — we work hard for you.