JSFiddle - React, Tailwind, and code Playground

by DannyEnglander

HTML

<nav class="main-nav" aria-label="Main Navigation" id="main-nav">

  <ul>
    <li><a href="#">Home</a></li>

    <li class="dropdown">

      <button type="button" class="dropdown__title" aria-controls="foo-dropdown">
        Foo
      </button>
			
      <ul class="dropdown__menu" id="sweets-dropdown">
        <li><a href="#">Donuts</a></li>
        <li><a href="#">Cupcakes</a></li>
        <li><a href="#">Chocolate</a></li>
        <li><a href="#">Bonbons</a></li>
      </ul>
    </li>

    <li class="dropdown">
    
      <button type="button" class="dropdown__title" aria-controls="bar-dropdown">
        Bar
      </button>

      <ul class="dropdown__menu" id="sweets-dropdown">
        <li><a href="#">Donuts</a></li>
        <li><a href="#">Cupcakes</a></li>
        <li><a href="#">Chocolate</a></li>
        <li><a href="#">Bonbons</a></li>
      </ul>
    </li>

    <li><a href="#">About</a></li>

  </ul>
</nav>

<main>

</main>

SCSS

// Learn about how this was made:
// @link https://moderncss.dev/css-only-accessible-dropdown-navigation-menu/

$color: #333;
$color-dark: scale-color($color, $lightness: -30%);
$transition: 280ms all 120ms ease-out;

* {
  box-sizing: border-box;
}

.dropdown__title {
  background-color: transparent;
  border: none;
  font-family: inherit;
}

nav {
  background-color: $color;
  padding: 0 1rem;

  > ul {

		
		  display: flex;
	justify-content: space-around;

    > li {
      margin: 0 0.5rem;

      a,
      .dropdown__title {
        text-decoration: none;
        text-align: center;
        display: inline-block;
        color: #fff;
        font-size: 1.125rem;

        &:focus {
          outline: none;
        }
      }

      > a,
      .dropdown__title {
        padding: 1rem 0.5rem;
        border-top: 3px solid transparent;
        transition: $transition;

        &:hover,
        &:focus {
          border-top-color: pink;
          color: pink;
        }
      }
    }
  }

  ul {
    list-style: none;
    margin: 0;
    padding: 0;

    li {
      padding: 0;
    }
  }
}

.dropdown {
  position: relative;

  .dropdown__title {
    display: inline-flex;
    align-items: center;

    &:after {
      content: "";
      border: 0.35rem solid transparent;
      border-top-color: rgba(#fff, 0.45);
      margin-left: 0.25em;
      transform: translateY(0.15em);
    }
  }

  .dropdown__menu {
    position: absolute;
    min-width: 15ch;
    left: 50%;
    top: calc(100% - 0.25rem);
    transition: $transition;
    transform: rotateX(-90deg) translateX(-50%);
    transform-origin: top center;
    visibility: hidden;
    opacity: 0.3;
    padding: 0.5em 0;
    background-color: #fff;
    border-radius: 4px;
    box-shadow: 0 0.15em 0.25em rgba(black, 0.25);

    a {
      color: $color-dark;
      display: block;
      padding: 0.5em;
      opacity: 0;
      transition: $transition;

      &:hover {
        background-color: rgba($color, 0.15);
      }

     ...

JavaScript

// Define the nav container
const box = document.getElementById("main-nav")

// Define all the items have submenus.
let dropDowns = Array.from(document.querySelectorAll(".dropdown"))

// Define all the items have buttons.
let Buttons = Array.from(document.querySelectorAll(".dropdown__title"))

// Click handler function for dropDowns.
const handleClick = (event) => {
  event.preventDefault();

  dropDowns.forEach((node) => {
    node.classList.remove("active")
  });

  event.currentTarget.classList.add("active")

};

dropDowns.forEach((node) => {
  node.addEventListener("click", handleClick)
});

// Click handler function for Buttons.
const buttonClick = (event) => {
  event.preventDefault();

  Buttons.forEach((node) => {
    node.setAttribute('aria-expanded', 'false')
  });


  event.currentTarget.setAttribute('aria-expanded', 'true')
};

Buttons.forEach((node) => {
  node.addEventListener("click", buttonClick)
});


// Close submenus if clicked off of.
document.addEventListener("click", (e) => {
  if (!box.contains(e.target)) {
    console.log("body clicked")
    dropDowns.forEach((node) => {
      node.classList.remove("active")
    });
  }
});