Building menus and buttons in Astro
This is how I built my navigation menu, dropdown, and button components in Astro.
Nav Bar Component
I created a Nav bar component for my site, in it I import Button and DropdownMenu components in addition to two logos, one for mobile and one for desktop.
---import Button from './subComponents/Button.astro';import DropdownMenu from './subComponents/DropdownMenu.astro';---
<div  class="navbar-container sticky top-0 z-10 mb-8 w-full bg-slate-900 py-2 pl-4 pr-2 sm:w-5/6 sm:rounded-b-lg">  <div class="button-container flex items-center justify-between gap-6">    <a href="/">      <img        src="../astroLogo.svg"        alt="Astro Logo"        class="w-30 hidden h-8 opacity-80 md:block"      />      <img        src="../astro-icon-light.svg"        alt="Astro Logo"        class="h-8 w-8 opacity-80 sm:block md:hidden"      />    </a>    <div class="md:text-md flex lg:text-lg">      <Button name="Home" link="/" />      <Button name="About" link="/about" />      <Button name="Blog" link="/posts/blog1" />      <DropdownMenu        name="Projects"        links={[          { name: 'Minimal Typography', url: '/designProject' },          { name: 'Old Flask Site', url: '/flaskSite' },        ]}      />    </div>  </div></div>Button Component
I created a button component with variable styling including the ability to add a caret icon when the button is being used to trigger a dropdown menu. The button component accepts several props including name, link, id, showCaret, and styles which are used to customize the button component when it’s called.
---interface Props {  name: string;  link?: string;  id?: string;  showCaret?: boolean;  styles?: string;}
const { name, link, id, showCaret = false, styles = '' } = Astro.props;---
<a  href={link}  id={id}  class=`mx-2 flex rounded-lg px-3 py-2 font-normal text-slate-200 hover:bg-slate-800 text-opacity-70 hover:text-opacity-100 transition-color duration-200 max-w-max ${styles}`>  <button class="flex">    {name}    {      showCaret && (        <svg          class="caret-icon ml-1 mt-1 h-5 w-5 lg:h-6 lg:w-6"          viewBox="0 0 24 24"          fill="none"          stroke="currentColor"          stroke-width="1.1">          <path            d="M8 9l5 5 5-5"            stroke-linecap="round"            stroke-linejoin="round"          />        </svg>      )    }  </button></a><style>  .dropdown-active {    @apply bg-slate-800;  }
  .caret-icon {    transition: transform 0.15s ease-in-out;    will-change: transform;  }  .dropdown-active .caret-icon {    transform: rotate(180deg);  }</style>Dropdown Menu Component
The DropdownMenu component uses the Button component to open a dropdown menu with links to project pages on my website.
---interface Link {  name: string;  url: string;}
interface Props {  name: string;  links: Link[];}
const { name = 'Dropdown', links = [] } = Astro.props;import Button from './Button.astro';---
<div class="relative">  <Button name={name} id="dropdown-button" showCaret={true} />  <div    id="dropdown"    class="absolute mt-2 hidden text-balance rounded-md bg-slate-800 p-[0.5px] sm:ml-2 md:w-40">    {      links.map((link) => (        <a          href={link.url}          class="m-1 block rounded-md px-2 py-2 text-sm text-slate-300/80 transition-colors duration-200 hover:bg-slate-700 hover:text-slate-200/95">          {link.name}        </a>      ))    }  </div></div>
<script>  const button = document.getElementById('dropdown-button');  const dropdown = document.getElementById('dropdown');
  button.addEventListener('click', (event) => {    dropdown.classList.toggle('hidden');    button.classList.toggle('dropdown-active');    event.stopPropagation();  });
  document.addEventListener('click', () => {    button.classList.remove('dropdown-active');    dropdown.classList.add('hidden');  });</script>