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")
});
}
});