<header>
  <nav>
    <ul class="nav">
      <li>
        <a href="#0">Dropdown Menu <svg class="icon" viewBox="0 0 320 512" width="100" title="caret-down">
            <path d="M31.3 192h257.3c17.8 0 26.7 21.5 14.1 34.1L174.1 354.8c-7.8 7.8-20.5 7.8-28.3 0L17.2 226.1C4.6 213.5 13.5 192 31.3 192z" />
          </svg></a>
        <ul class="submenu">
          <li><a href="#0">One</a></li>
          <li><a href="#0">Two</a></li>
          <li><a href="#0">Three</a></li>
        </ul>
      </li>
      <li>
        <a href="#0">Dropdown Menu <svg class="icon" viewBox="0 0 320 512" width="100">
            <path d="M31.3 192h257.3c17.8 0 26.7 21.5 14.1 34.1L174.1 354.8c-7.8 7.8-20.5 7.8-28.3 0L17.2 226.1C4.6 213.5 13.5 192 31.3 192z" />
          </svg></a>
        <ul class="submenu">
          <li><a href="#0">One</a></li>
          <li><a href="#0">Two</a></li>
          <li><a href="#0">Three <svg class="icon icon-rotate" viewBox="0 0 320 512" width="100">
                <path d="M31.3 192h257.3c17.8 0 26.7 21.5 14.1 34.1L174.1 354.8c-7.8 7.8-20.5 7.8-28.3 0L17.2 226.1C4.6 213.5 13.5 192 31.3 192z" />
              </svg></a>
            <ul class="submenu submenu-2">
              <li><a href="#0">One</a></li>
              <li><a href="#0">Two</a></li>
            </ul>
          </li>
          <li><a href="#0">Four</a></li>
          <li><a href="#0">Five</a></li>
        </ul>
      </li>
    </ul>
  </nav>
</header>
header {
  background: oklch(35% 3% 265deg);
  border-bottom: 1px solid oklch(44% 3% 265deg);
}

.nav {
  display: flex;
  justify-content: center;
  gap: 2rem;
  
  li {
    /* Submenus are absolutely positioned within */
    position: relative;
    
    /* for mouse users this works, but list items themselves aren't focusable, hence the next line */
    &:hover > a,
    
    /* if any link inside is hovered or has focus, then keep that menu open! */
    &:has(a:hover, a:focus) {
      /* Ya know what actually, it would be fun to set like `--dropdown-open: true` here and move the styles out to a style query instead of nesting this deeply. */
      
      > a + .submenu {        
        /* block-size fallback for browsers that can't animate to auto */
        block-size: auto;
        block-size: calc-size(auto, size);
        
        border: 2px solid oklch(38% 3% 265deg);
        
        /* this isn't really needed unless the menu isn't in the DOM at all to start or you intentionally wanted to `display: none` the submenus, which is generally a bad plan. */ 
        /*
        @starting-style {
          block-size: 0;
        }
        */
      }
    }
    
    > a {
      display: flex;
      gap: 0.25rem;
      padding: 1rem 0.5rem;
      color: #cfd8dc;
      
      &:hover,
      &:focus {
        color: white;
        /* May want to do nicer focus styles that don't look cut off. */
        
        svg {
          opacity: 1;
          translate: 0 1px;
        }
      }
    }
  }
}

.submenu {
  /* For anything at "opens and closes" `ease-in-out` is decently nice */
  transition: 0.15s ease-in-out;
  
  /* If we were animating from `display: none;` then `transition-behavior` would help make that work, but we don't need it here. Actually, for menus like this, `display: none` is bad news as it means we wouldn't be able to tab through the items properly. */
  /* transition-behavior: allow-discrete; */
  
  /* Funny how you need to replicate this in @starting-style if you need that because of `display: none` */
  block-size: 0;
  
  /* Might as well let it grow wider if it needs to */
  inline-size: max-content;
  min-inline-size: 100%;
  
  position: absolute;
  top: calc(100% - 5px);
  left: 15px;
  background: oklch(24% 3% 265deg);
  border-radius: 12px;
  
  /* You can clip directionally with `clip` which is awesome. This could be a bug though, if a sub-sub menu went lower vertically down than the sub menu. */
  overflow-y: clip;
  overflow-x: visible;
  
  li {
    a {
      display: flex;
      color: white;
      padding: 0.6rem 1.2rem;
      
      &:hover,
      &:focus {
        background: oklch(45% 3% 265deg);
      }
    }
    
    /* gotta do this cause can't fully clip overflow */
    &:first-child a {
      /* 12px border radius - 2px border = 10px */
      border-top-left-radius: 10px;
      border-top-right-radius: 10px;
    }
    &:last-child a {
      border-bottom-left-radius: 10px;
      border-bottom-right-radius: 10px;
    }
  }
}

.icon {
  /* push right arrow to right */
  margin-inline-start: auto;
  
  fill: white;
  opacity: 0.5;
  inline-size: 0.7em;
  transition: 0.1s ease-in-out;
  
  &.icon-rotate {
    rotate: -90deg;
  }
}

/* Sub-sub menus */
.submenu-2 {
  top: -5px;
  left: 80%;
  z-index: 1;
}

@layer foundation {
  body {
    font: 100% system-ui, sans-serif;
    background: oklch(18% 3% 268deg);
    margin: 0;
    overflow: clip;
  }
  ul {
    list-style: none;
    padding: 0;
    margin: 0;
  a {
    text-decoration: none;
  }
  * {
    box-sizing: border-box;
  }
}
// This was useful in some testing I was doing.

// setInterval(() => {
//   console.log(document.activeElement);
// }, 250);

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

This Pen doesn't use any external JavaScript resources.