Components Dropdown

Modern Popover API NEW

A <ul> or <menu> with the popover attribute is styled like a dropdown and opens next to the button that toggles it, with no JS:

<button popovertarget="actions">Actions</button>
<ul id="actions" popover>
  <li><a href="#">Edit</a></li>
  <li><a href="#">Duplicate</a></li>
</ul>

How it works

/anyblades/blades/refs/heads/main/src/blades/components/_dropdown-popover.css :

:is(ul, menu)[popover] {
  min-width: fit-content;
  margin: auto;
  padding: 0;
  border: var(--pico-border-width) solid var(--pico-dropdown-border-color);
  border-radius: var(--pico-border-radius);
  background-color: var(--pico-dropdown-background-color);
  box-shadow: var(--pico-dropdown-box-shadow);
  color: var(--pico-dropdown-color);
  white-space: nowrap;
  opacity: 0;
  transition:
    opacity var(--pico-transition),
    overlay var(--pico-transition) allow-discrete,
    display var(--pico-transition) allow-discrete;

  /* Beats `nav ul` / `aside ul`, which would otherwise show the closed menu */
  &:not(:popover-open) {
    display: none;
  }
  &:popover-open {
    display: block;
    opacity: 1;
  }

  li {
    display: block;
    margin-bottom: 0;
    padding: calc(var(--pico-form-element-spacing-vertical) * 0.5)
      var(--pico-form-element-spacing-horizontal);
    list-style: none;

    &:first-of-type {
      margin-top: calc(var(--pico-form-element-spacing-vertical) * 0.5);
    }
    &:last-of-type {
      margin-bottom: calc(var(--pico-form-element-spacing-vertical) * 0.5);
    }
    &:has(label):hover {
      background-color: var(--pico-dropdown-hover-background-color);
    }
  }
  li a {
    display: block;
    margin: calc(var(--pico-form-element-spacing-vertical) * -0.5)
      calc(var(--pico-form-element-spacing-horizontal) * -1);
    padding: calc(var(--pico-form-element-spacing-vertical) * 0.5)
      var(--pico-form-element-spacing-horizontal);
    overflow: hidden;
    border-radius: 0;
    color: var(--pico-dropdown-color);
    text-decoration: none;
    text-overflow: ellipsis;

    &:is(:hover, :focus, :active, :focus-visible, [aria-current]:not([aria-current="false"])) {
      background-color: var(--pico-dropdown-hover-background-color);
    }
  }
  li label {
    width: 100%;
  }
}
/* Beats Pico's `nav ul:first-of-type` / `:last-of-type` margins whatever the load order (soft win via :not) */
nav :is(ul, menu)[popover]:not(.does-not-exist) {
  margin: auto;
}
@starting-style {
  :is(ul, menu)[popover]:popover-open {
    opacity: 0;
  }
}

/* Next to the button that opened it (its implicit anchor) */
@supports (position-area: bottom) {
  :is(ul, menu)[popover],
  nav :is(ul, menu)[popover]:not(.does-not-exist) {
    inset: auto;
    margin: 0;
    margin-top: var(--pico-outline-width);
    position-area: bottom span-right;
    position-try-fallbacks: flip-block, flip-inline;

    &:dir(rtl) {
      position-area: bottom span-left;
    }
  }
}

Classic <details> version

Create dropdown menus and custom selects with minimal and semantic HTML, without JavaScript.

Dropdowns are built with <details class="dropdown"> as a wrapper and <summary> and <ul> as direct children. Unless they are in a Nav, dropdowns are width: 100%; by default.

Dropdowns are not available in the class‑less version.

For style consistency with the form elements, dropdowns are styled like a select by default.

<!-- Dropdown -->
<details class="dropdown">
  <summary>Dropdown</summary>
  <ul>
    <li><a href="#">Solid</a></li>
    <li><a href="#">Liquid</a></li>
    <li><a href="#">Gas</a></li>
    <li><a href="#">Plasma</a></li>
  </ul>
</details>

<!-- Select -->
<select name="select" aria-label="Select" required>
  <option selected disabled value="">Select</option>
  <option>Solid</option>
  <option>Liquid</option>
  <option>Gas</option>
  <option>Plasma</option>false
</select>

Dropdowns with checkboxes and radios#

Dropdowns can be used as custom selects with <input type="radio"> or <input type="checkbox">.

<!-- Radios -->
<details class="dropdown">
  <summary>
    Select a phase of matter...
  </summary>
  <ul>
    <li>
      <label>
        <input type="radio" name="phase" value="solid" />
        Solid
      </label>
    </li>
    <li>
      <label>
        <input type="radio" name="phase" value="liquid" />
        Liquid
      </label>
    </li>
    <li>
      <label>
        <input type="radio" name="phase" value="gas" />
        Gas
      </label>
    </li>
    <li>
      <label>
        <input type="radio" name="phase" value="plasma" />
        Plasma
      </label>
    </li>
  </ul>
</details>

<!-- Checkboxes -->
<details class="dropdown">
  <summary>
    Select phases of matter...
  </summary>
  <ul>
    <li>
      <label>
        <input type="checkbox" name="solid" />
        Solid
      </label>
    </li>
    <li>
      <label>
        <input type="checkbox" name="liquid" />
        Liquid
      </label>
    </li>
    <li>
      <label>
        <input type="checkbox" name="gas" />
        Gas
      </label>
    </li>
    <li>
      <label>
        <input type="checkbox" name="plasma" />
        Plasma
      </label>
    </li>
  </ul>
</details>

Pico does not include JavaScript code. You will probably need some JavaScript to interact with these custom dropdowns.

Button variants#

<summary role="button"> transforms the dropdown into a button.

<details class="dropdown">
  <summary role="button">
    Dropdown as a button
  </summary>
  <ul>
    ...
  </ul>
</details>

Like regular buttons, they come with .secondary, .contrast, and .outline (not available in the class-less version).

<!-- Primary -->
<details class="dropdown">
  <summary role="button">
    Primary
  </summary>
  <ul>
    ...
  </ul>
</details>

<!-- Secondary -->
<details class="dropdown">
  <summary role="button" class="secondary">
    Secondary
  </summary>
  <ul>
    ...
  </ul>
</details>

<!-- Contrast -->
<details class="dropdown">
  <summary role="button" class="contrast">
    Contrast
  </summary>
  <ul>
    ...
  </ul>
</details>

<!-- Primary outline -->
<details class="dropdown">
  <summary role="button" class="outline">
    Primary outline
  </summary>
  <ul>
    ...
  </ul>
</details>

<!-- Secondary outline -->
<details class="dropdown">
  <summary role="button" class="outline secondary">
    Secondary outline
  </summary>
  <ul>
    ...
  </ul>
</details>

<!-- Contrast outline -->
<details class="dropdown">
  <summary role="button" class="outline contrast">
    Contrast outline
  </summary>
  <ul>
    ...
  </ul>
</details>

Validation states#

Just like any form elements, validation states are provided with aria-invalid.

<details class="dropdown">
  <summary aria-invalid="false">
    Valid phase of matter: Solid
  </summary>
  <ul>
    ...
  </ul>
</details>

<details class="dropdown">
  <summary aria-invalid="true">
    Debated classification: Plasma
  </summary>
  <ul>
    ...
  </ul>
</details>

Usage with <nav>#

You can use dropdowns inside Nav.

To change the alignment of the submenu, simply use <ul dir="rtl">.

<nav>
  <ul>
    <li><strong>Acme Corp</strong></li>
  </ul>
  <ul>
    <li><a href="#" class="secondary">Services</a></li>
    <li>
      <details class="dropdown">
        <summary>
          Account
        </summary>
        <ul dir="rtl">
          <li><a href="#">Profile</a></li>
          <li><a href="#">Settings</a></li>
          <li><a href="#">Security</a></li>
          <li><a href="#">Logout</a></li>
        </ul>
      </details>
    </li>
  </ul>
</nav>

Edit this page on GitHub