Hamburger Menu Using Details-Summary

A hamburger menu built using Details and Summary...

Menu

The CSS...

<style>
    
*, *:after, *:before {
  box-sizing: border-box;
  margin: 0;
}
* + * {
  margin-top: 1rem;
}

/* The mobile nav styles */

/* Enforce list semantics */
nav [role="list"] {
  list-style: none;
  padding-left: 1rem;
  margin: 0;
}

summary {
  border: 1px solid #ccc;
  border-radius: 4px;
  display: inline-block;
  height: 3rem !important;
  line-height: 1;
  list-style: none;
  padding: .5rem !important;
  position: relative;
  text-align: center;
  transition: .4s border-radius ease-out;
  width: 3rem;
  will-change: border-radius;
}
summary span {
  position: relative;
  top: .75rem;
  transition-delay: 0.2s;
  transition-duration: 0s;
}
summary span:before,
summary span:after {
  position: absolute;
  content: '';
}
summary span,
summary span:before,
summary span:after {
  background-color: #000;
  display: block;
  height: .375rem;
  width: 100%;
  will-change: background-color, margin, transform;
}
summary span:before {
  /* margin-top: -.75rem; */
  transform: translateY(-.75rem) rotate(0); 
}
summary span:after {
  /* margin-top: .75rem; */
  transform: translateY(.75rem) rotate(0);
}
summary x {
  bottom: -2.125rem;
  font-size: 1.2rem;
  font-weight: 700;
  line-height: 2em;
  width: 4rem;
  left: 50%;
  letter-spacing: .05em;
  position: absolute;
  text-transform: uppercase;
  transform: translate3d(-50%,0%,0);
}

details[open] > summary {
  border-radius: 100%;
}
details[open] > summary > span {
  background-color: transparent;
  transition-delay: 0.2s;
}
summary span:before {
  transition-delay: 0.2s, 0s;
  transition-duration: 0.2s;
  transition-property: margin, transform;
/*   transition-property: transform; */
}
details[open] > summary span:before {
  margin-top: 0;
  transform: rotate(45deg);
/*   transform: translateY(0) rotate(45deg); */
  transition-delay: 0s, 0.2s;
}
summary span:after {
  transition-delay: 0.2s, 0s;
  transition-duration: 0.2s;
  transition-property: margin, transform;
/*   transition-property: transform; */
}
details[open] > summary span:after {
  margin-top: 0;
  transform: rotate(-45deg);
/*   transform: translateY(0) rotate(-45deg); */
  transition-delay: 0s, 0.2s;
}

nav details {
  display: inline-block;
}
    
nav summary {
    width: auto !important;
    border: none;
    height: auto !important;
    }
    
nav summary:after {
    position: absolute;
    content: '▶';
    padding-left: 5px;
}
    
nav details[open] > summary:after {
    position: absolute;
    content: '▼';
    padding-left: 5px;
}
    
details[open] > summary x {
display: none;
}
        

@media (min-width: 40em) { /* 640px */

  /* Note: dropdowns may use position absolute,
      but was considered beyond scope in this simple demo. */

  nav > details > [role="list"] {
    /* display: flex; */
    gap: 1rem;
    margin-top: 0 !important;
    padding: 0;
  }
  nav > details > ul > li {
    margin-top: 0 !important;
  }
}
</style>

The HTML...

<details>
  <summary>
    <span></span>
    <x>Menu</x>
  </summary>
  <nav>
      <ul role=list>
        <li><a href="#">About us</a></li>
        <li>
          <details>
            <summary>Products</summary>
            <ul role=list>
              <li><a href="#">Product 1</a></li>
              <li><a href="#">Product 2</a></li>
              <li>
                <details>
                  <summary>Sub</summary>
                  <ul role=list>
                    <li><a href="#">Sub 1</a></li>
                    <li><a href="#">Sub 2</a></li>
                  </ul>
                </details>
              </li>
            </ul>
          </details>
        </li>
        <li><a href="#">Insights</a></li>
        <li><a href="#">Contact</a></li>
      </ul>
  </nav>
</details>


<script>
    console.clear();

// The mobile disclosure button only:
// Progressive enhancement, works without JS too.

const responsiveMenuButton = (document => {
  'use strict';

  const minViewportWidth = '40em'; // @ 640px

  const details = document.querySelector('nav > details');
  if (!details) return;

  const summary = details.querySelector('summary');
  if (!summary) return;

  const mediaQuery = window.matchMedia(`(min-width: ${minViewportWidth})`);
  let isSmallScreen = !mediaQuery.matches;

  const showMenu = _ => {

    // Edge case: Orientation may change viewport width without moving focus
    const isSummaryFocused = !!details.querySelector('summary:is(:focus)');

    details.open = true;
    summary.hidden = true;

    if (isSummaryFocused) {
      const firstLink = details.querySelector('a[href]');
      firstLink && firstLink.focus();
    }
  };

  const hideMenu = _ => {

    // Edge case: Orientation may change viewport width without moving focus
    const isMenuLinkFocused = !!details.querySelector('ul:is(:focus-within)');

    details.removeAttribute('open');
    summary.removeAttribute('hidden');

    isMenuLinkFocused && summary.focus();
  };

  const controlResponsiveMenu = event => {
    if (isSmallScreen && event.matches) {
      showMenu();
      isSmallScreen = true;
    }

    isSmallScreen && !event.matches && hideMenu();

    !isSmallScreen && event.matches && showMenu();

    if (!isSmallScreen && !event.matches) {
      hideMenu();
      isSmallScreen = false;
    }
  };

  mediaQuery.addListener(controlResponsiveMenu);
  controlResponsiveMenu(mediaQuery);
})(document);
</script>

The Javascript...

<script>
    console.clear();

// The mobile disclosure button only:
// Progressive enhancement, works without JS too.

const responsiveMenuButton = (document => {
  'use strict';

  const minViewportWidth = '40em'; // @ 640px

  const details = document.querySelector('nav > details');
  if (!details) return;

  const summary = details.querySelector('summary');
  if (!summary) return;

  const mediaQuery = window.matchMedia(`(min-width: ${minViewportWidth})`);
  let isSmallScreen = !mediaQuery.matches;

  const showMenu = _ => {

    // Edge case: Orientation may change viewport width without moving focus
    const isSummaryFocused = !!details.querySelector('summary:is(:focus)');

    details.open = true;
    summary.hidden = true;

    if (isSummaryFocused) {
      const firstLink = details.querySelector('a[href]');
      firstLink && firstLink.focus();
    }
  };

  const hideMenu = _ => {

    // Edge case: Orientation may change viewport width without moving focus
    const isMenuLinkFocused = !!details.querySelector('ul:is(:focus-within)');

    details.removeAttribute('open');
    summary.removeAttribute('hidden');

    isMenuLinkFocused && summary.focus();
  };

  const controlResponsiveMenu = event => {
    if (isSmallScreen && event.matches) {
      showMenu();
      isSmallScreen = true;
    }

    isSmallScreen && !event.matches && hideMenu();

    !isSmallScreen && event.matches && showMenu();

    if (!isSmallScreen && !event.matches) {
      hideMenu();
      isSmallScreen = false;
    }
  };

  mediaQuery.addListener(controlResponsiveMenu);
  controlResponsiveMenu(mediaQuery);
})(document);
</script>

From https://codepen.io/2kool2/pen/bGxvQaO