Tailwind CSS Dropdown with details-menu Web Component
Stefan Zweifel
Posted on March 21, 2021
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>
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>
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>
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.
- github/clipboard-copy-element
- github/tab-container-element
- github/auto-complete-element
- github/details-dialog-element
- github/time-elements
Check them out. Maybe you can replace an existing component in your projects with them and reduce the size of your bundles.
Posted on March 21, 2021
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.
Related
October 28, 2024
September 4, 2024