Cory Rylan

My name is , Google Developer Expert, Speaker, Software Developer. Building Design Systems and Web Components.

Follow @coryrylan
HTML

Dropdown Menus with HTML Popovers and CSS Anchor Positioning

Cory Rylan

- 4 minutes

Building nested dropdown menus has traditionally required JavaScript for managing visibility, positioning, and keyboard navigation. With new browser APIs, we can now create fully functional nested dropdown menus using only HTML and CSS. In this post, we'll explore how to combine the HTML Popover API, CSS Anchor Positioning, and entry animations to build accessible nested menus.

An example of HTML Popover APIs combined with CSS Anchor Positioning
An example of HTML Popover APIs combined with CSS Anchor Positioning

The HTML Popover API

The Popover API provides built-in support for showing and hiding content without JavaScript. By adding the popover attribute to an element and using popovertarget on a button, the browser handles the show/hide logic automatically.

<button popovertarget="menu-1">Open Menu</button>
<menu id="menu-1" popover>
  <button>Menu Item 1</button>
  <button>Menu Item 2</button>
  <button>Menu Item 3</button>
</menu>

Clicking the button will toggle the menu visibility. The browser also handles closing the popover when clicking outside of it or pressing the Escape key.

Nesting Popovers

The Popover API supports nesting popovers by using popovertarget within a popover itself. This creates a parent-child relationship where child popovers stay open as long as the parent is open.

<button id="anchor" popovertarget="menu-1">Open Menu</button>
<menu id="menu-1" popover>
  <button>Menu Item</button>
  <button>Menu Item</button>
  <button popovertarget="menu-2">Submenu</button>
  <button>Menu Item</button>
</menu>
<menu id="menu-2" popover>
  <button>Submenu Item</button>
  <button popovertarget="menu-3">Nested Submenu</button>
  <button>Submenu Item</button>
</menu>
<menu id="menu-3" popover>
  <button>Nested Item</button>
  <button>Nested Item</button>
  <button>Nested Item</button>
</menu>

When a menu item with a popovertarget is clicked, the corresponding submenu opens. The browser manages the popover stack, keeping parent menus open while child menus are visible.

CSS Anchor Positioning

The CSS Anchor Positioning API allows us to position elements relative to other elements without JavaScript. For popover menus, we can use implicit anchors with position-anchor: auto to automatically anchor the popover to its triggering button.

[popover] {
  position-anchor: auto;
  position-area: bottom span-right;
  margin: 0;
  padding: 0;
  border: 1px solid #ccc;
  border-radius: 4px;
}

The position-anchor: auto property creates an implicit anchor relationship between the popover and its triggering element. The position-area property defines where the popover should appear relative to the anchor.

For nested submenus, we want them to appear to the right of the parent menu item instead of below:

[popover] ~ [popover] {
  position-area: right span-bottom;
}

This CSS sibling selector targets any popover that follows another popover and positions it to the right.

Entry Animations

To add polish to our menus, we can animate them using @starting-style and transition-behavior: allow-discrete. The @starting-style at-rule defines the initial state before an element is rendered, which is essential for animating elements that transition from display: none.

[popover] {
  transition-timing-function: ease;
  transition-property: opacity, overlay, transform;
  transition-duration: 300ms;
  transition-behavior: allow-discrete;
}

[popover]:popover-open {
  display: flex;
  flex-direction: column;
  opacity: 1;
  transform: translateY(0);
}

@starting-style {
  [popover]:popover-open {
    opacity: 0;
    transform: translateY(-10%);
  }
}

The transition-behavior: allow-discrete property enables transitioning discrete properties like display and overlay. Combined with @starting-style, this creates a smooth fade-in and slide effect when menus open.

Highlighting Active Parent Items

When a submenu is open, it's helpful to highlight the parent menu item that triggered it. We can achieve this with the CSS :has() selector:

[popover]:has(+ [popover]:popover-open) > button[popovertarget] {
  background: oklch(0 0 0 / 0.15);
}

This selector finds any popover that is immediately followed by an open popover, then highlights the button with a popovertarget inside it.

Complete Implementation

Here's the complete CSS for our nested dropdown menus:

[popover] {
  background: transparent;
  flex-direction: column;
  position-anchor: auto;
  position-area: bottom span-right;
  padding: 0;
  margin: 0;
  border: 1px solid #ccc;
  border-radius: 4px;
  transition-timing-function: ease;
  transition-property: opacity, overlay, transform;
  transition-duration: 300ms;
  transition-behavior: allow-discrete;
}

[popover] ~ [popover] {
  position-area: right span-bottom;
}

[popover]:popover-open {
  display: flex;
  opacity: 1;
  transform: translateY(0);
}

@starting-style {
  [popover]:popover-open {
    opacity: 0;
    transform: translateY(-10%);
  }
}

[popover]:has(+ [popover]:popover-open) > button[popovertarget] {
  background: oklch(0 0 0 / 0.15);
}

[popover] button {
  background: oklch(0 0 0 / 0.05);
  border-radius: 0;
  border: 0;
  padding: 8px;
  min-width: 100px;
  text-align: left;
  font-size: 14px;
  display: inline-flex;
  align-items: center;
  gap: 12px;
}

[popover] button:hover {
  background: oklch(0 0 0 / 0.15);
}

[popover] button[popovertarget]::after {
  content: '▶';
  display: inline-block;
  margin-left: auto;
  font-size: 0.8em;
  color: #484848;
}

Conclusion

The combination of the Popover API, CSS Anchor Positioning, and entry animations enables creating nested dropdown menus without JavaScript. The browser handles click-outside behavior, escape key handling, and the popover stacking context automatically. Check out the full working demo below!

View Demo Code   
Bluesky Facebook LinkedIn Email
 

No spam. Short occasional updates on Web Development articles, videos, and new courses in your inbox.

Related Posts

HTML

Styling HTML Form Validation with CSS

Learn how to style HTML form validation messages with CSS.

Read Article
HTML

Getting Started with HTML Form Events

Learn the basics of how HTML Form events and Form Control events work in modern browsers.

Read Article
JavaScript

Use JavaScript Date Objects with the HTML5 Date Picker

Learn how to easily use both the native HTML5 datepicker and JavaScript Date objects together.

Read Article