DEV Community

Stefan Zweifel
Stefan Zweifel

Posted on • Originally published at stefanzweifel.dev on

Tailwind CSS Dropdown with details-menu Web Component

If you didn't know yet, GitHub maintains a growing list of open sourced Web Components.

One is love to use is details-menu-element. It adds a <details-menu> element which can be used in combination with <details> and <summary> to create dropdowns. The best thing: You don't have to write any additional JavaScript to make it work. Everything is bundled inside <details-menu>.

I use this in websites where I don't want to include a heavy JavaScript framework like Vue.js or React, but still need an accessible dropdown.

In this example on CodePen, I've adapted the Tailwind UI dropdown example to use <detail-menu>.

I've also sprinkled in some Alpine.js to close the dropdown if the user clicks anywhere outside of it.

Source Code#

The HTML without any Tailwind CSS classes or Alpine.js attributes looks like this.

<details open>
  <summary>
    <div>
        Options
        <!-- Heroicon name: solid/chevron-down -->
        <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
            <path fill-rule="evenodd" d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z" clip-rule="evenodd" />
        </svg>
    </div>
  </summary>
  <details-menu role="menu">
    <div role="none">
      <a href="#" role="menuitem">Account settings</a>
      <a href="#" role="menuitem">Support</a>
      <a href="#" role="menuitem">License</a>
      <form method="POST" action="#" role="none">
        <button type="submit" role="menuitem">
          Sign out
        </button>
      </form>
    </div>
  </details-menu>
</details>

Enter fullscreen mode Exit fullscreen mode

Next I've added the Alpine.js attributes. We init Alpine.js on the <details>-element and give it a $ref. We then listen to the @click.away event and remove the open attribute, to close/hide the dropdown.

<details
  x-data 
  x-ref="dropdown" 
  @click.away="$refs.dropdown.removeAttribute('open');" 
  open
  >
  <summary>
    <div>
        Options
        <!-- Heroicon name: solid/chevron-down -->
        <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
            <path fill-rule="evenodd" d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z" clip-rule="evenodd" />
        </svg>
    </div>
  </summary>
  <details-menu role="menu">
    <div role="none">
      <a href="#" role="menuitem">Account settings</a>
      <a href="#" role="menuitem">Support</a>
      <a href="#" role="menuitem">License</a>
      <form method="POST" action="#" role="none">
        <button type="submit" role="menuitem">
          Sign out
        </button>
      </form>
    </div>
  </details-menu>
</details>

Enter fullscreen mode Exit fullscreen mode

And here is the final component with its Tailwind CSS classes added.

<!-- https://tailwindui.com/components/application-ui/elements/dropdowns#component-f8a14da22f26a67757b19f2fe3ca00ed -->

<details open x-data x-ref="dropdown" @click.away="$refs.dropdown.removeAttribute('open');" class="relative inline-block text-left">

  <summary>
    <div class="inline-flex justify-center w-full rounded-md border border-gray-300 shadow-sm px-4 py-2 bg-white text-sm font-medium text-gray-700 hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-offset-gray-100 focus:ring-indigo-500">
      Options
      <!-- Heroicon name: solid/chevron-down -->
      <svg class="-mr-1 ml-2 h-5 w-5" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
        <path fill-rule="evenodd" d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z" clip-rule="evenodd" />
      </svg>
    </div>
  </summary>

  <details-menu role="menu" class="origin-top-right absolute right-0 mt-2 w-56 rounded-md shadow-lg bg-white ring-1 ring-black ring-opacity-5 focus:outline-none">

    <div class="py-1" role="none">
      <a href="#" class="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100 hover:text-gray-900" role="menuitem">Account settings</a>
      <a href="#" class="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100 hover:text-gray-900" role="menuitem">Support</a>
      <a href="#" class="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100 hover:text-gray-900" role="menuitem">License</a>
      <form method="POST" action="#" role="none">
        <button type="submit" class="block w-full text-left px-4 py-2 text-sm text-gray-700 hover:bg-gray-100 hover:text-gray-900" role="menuitem">
          Sign out
        </button>
      </form>
    </div>
  </details-menu>
</details>

Enter fullscreen mode Exit fullscreen mode

On the JavaScript side, you only have to import two libraries to make everything work.

  • import '@github/details-menu-element'
  • import 'alpinejs'

Outro#

Web Components haven't catched on in mainstream web development – or at least not in my bubble – but I see much potential in them.

Especially components like <details-menu> which are "headless" and don't come with any styles attached to them, have a bright future a head of them.

The team at Tailwind Labs for example is already working on Headless UI. A set of UI components which are accessible and unstyled and can be used in React or Vue.

I have the felling that the growing popularity of utility CSS frameworks will spill over to Web Components and we see Web Components used more in mainstream web development projects.


As mentioned in the beginning, GitHub open sourced quite a few Web Components they use on github.com. Here's a quick list of components I started using in some of my projects.

Check them out. Maybe you can replace an existing component in your projects with them and reduce the size of your bundles.

Top comments (0)