Simple, customizable dropdown menu utility for Astro with only vanilla JS.
I personally use UI libraries like ShadCN for web applications, but Astro was mainly designed for static sites. If I say "It's the best framework for building static sites like landing pages, portfolios, documentation pages, etc..." it's not just an opinion.
Initially, I used the Dropdown component that UI libraries provide, but most of the time I use it on headers so it gets loaded at the very beginning of the fetch. And believe me, it's more than 300KB, so I decided to build my own component just for my personal use with only vanilla JS.
Feel free to use it, modify it, report issues, or just complain about it.
Note
The objective of this component is to reduce bundle size to improve the performance of Astro applications.
pnpm add @yarso/astro-dropdownJust add a <button> element as a child of the <Dropdown> component to set the trigger. Then add a <div> element also as a child of the <Dropdown> with a child <ul> element to set the content.
import { Dropdown } from "@yarso/astro-dropdown"<Dropdown>
<!-- Trigger element -->
<button>
🚀
</button>
<!-- Content element -->
<div>
<ul>
<li>Item 1</li>
<li>Item 2</li>
<li>Item 3</li>
</ul>
</div>
</Dropdown>Warning
It's important to note that the content element must be a direct child of the dropdown element and have a <ul> element as a child. You can add any other elements inside the <ul> to create your dropdown menu.
You're free to style the <button> and <ul> elements as you wish using whatever attributes you want and whatever frameworks you prefer (e.g., Tailwind, Bootstrap, vanilla CSS, etc.).
Note
I suggest you set a fixed width for the <ul> element to avoid the content splitting in the middle of the dropdown.
The <Dropdown> component has some optional custom attributes that you can use to customize its behavior:
container-gap: This attribute sets the gap in pixels between the trigger element and the content element (default is4).content-origin: This attribute sets the origin of the content element (default isbottom-right).content-z-index: This attribute sets the z-index of the content element (default is30).
import { Dropdown } from "@yarso/astro-dropdown"<Dropdown
container-gap={8}
content-origin="top-left"
content-z-index={10}
>
<!-- Trigger element -->
<button>
🚀
</button>
<!-- Content element -->
<div>
<ul>
<li>Item 1</li>
<li>Item 2</li>
<li>Item 3</li>
</ul>
</div>
</Dropdown>- Zero dependencies: Pure vanilla JavaScript
- Zero configuration: No setup needed
- Fully customizable: Total control over child elements
- Lightweight: ~6KB in production
- Multiple dropdowns: Now supported automatically!
<Dropdown>
<button class="rounded-xs bg-green-700 text-white hover:bg-green-500 transition-colors duration-200 py-1 px-3">
🚀
</button>
<div>
<ul class="bg-zinc-800 border-[1px] border-zinc-600 w-[200px]">
<li class="w-full">
href="/test-a"
class="hover:underline underline-offset-2 w-full block"
>Link to test A</a>
</li>
<li class="w-full">
href="/test-b"
class="hover:underline underline-offset-2 w-full block"
>Link to test B</a>
</li>
</ul>
</div>
</Dropdown>This is a custom implementation designed to suit specific requirements, so yeah, there are plenty of areas for improvement.
If you don't set a fixed width for the <ul> element, the content might split awkwardly in the middle of the dropdown. You might be able to make it work using block elements as children of the <ul>, but it's easier to just set a fixed width.
For now, I don't allow custom animations, but I'll implement a fix once I get time. I honestly don't need it, but it could be useful for some people. Feel free to fork and create a PR if you want to add it—I'll be grateful for that.
MIT License.