Navigation flyout avec CSS Grid

Utilisation de CSS Grid au lieu de position: absolute pour positionner les éléments en plusieurs colonnes

by oilvier

HTML

<ul role="list" class="navigation">
  <li class="navigation-item"><a href="#">Nos produits</a>
    <ul class="navigation-flyout navigation-flyout--1" style="--nav-items-count: 4;">
      <li class="navigation-item"><a href="#">Accès léger</a>
        <ul class="navigation-flyout navigation-flyout--2" style="--nav-items-count: 5;">
          <li class="navigation-item"><a href="#">Échelles</a></li>
          <li class="navigation-item"><a href="#">Marchands</a>
            <ul class="navigation-flyout navigation-flyout--3">
              <li><a href="#">Accessoires</a></li>
              <li><a href="#">Autres</a></li>
              <li><a href="#">Lien 1</a></li>
              <li><a href="#">Lien 2</a></li>
              <li><a href="#">Lien 3</a></li>
              <li><a href="#">Lien 4</a></li>
              <li><a href="#">Lien 5</a></li>
              <li><a href="#">Lien 6</a></li>
              <li><a href="#">Lien 7</a></li>
            </ul>
          </li>
          <li class="navigation-item"><a href="#">Plateforme</a></li>
          <li class="navigation-item"><a href="#">Échafaudage</a></li>
          <li class="navigation-item"><a href="#">Test</a></li>
        </ul>
      </li>
      <li class="navigation-item"><a href="#">Échaffaudage de façade</a></li>
      <li class="navigation-item"><a href="#">EPI Antichute</a></li>
      <li class="navigation-item"><a href="#">Modulaire circulation</a></li>
    </ul>
  </li>
  <li class="navigation-item"><a href="#">Nos services</a></li>
  <li class="navigation-item"><a href="#">Conseils</a></li>
  <li class="navigation-item"><a href="#">Contact</a></li>
</ul>

CSS

.navigation {
  position: relative;
  list-style: none;
  padding-left: 0;
  margin: 0;
  display: flex;
  gap: 10px;
}
  
.navigation ul {
  list-style: none;
  padding-left: 0;
  margin: 0;
}

.navigation-flyout {
  position: absolute;
  top: 100%;
  left: 0;
  right: 0;
  z-index: 2;
  background-color: beige;
  
  display: none;
}

.navigation-flyout--1 {
  grid-template-columns: 1fr 2fr;
}

/**
 * 1. We use `display: contents;` on the `li` to make its children use the parent `.navigation-flyout` as the grid container
 */
.navigation-flyout > li {
  position: relative;
  display: contents; /* [1] */
}

.navigation-flyout > li > a {
  grid-column: 1;
}

/**
 * 1. To keep the alignment of elements in our grid, `grid-row-end` value must be "number of visible items + 1"
 * Here, we need to add up the "number of `li` siblings" + "the `ul` of our flyout menu" + 1.
 * In order to do that math, we use a custom property populated on the template side to find the number of `li` siblings and we add 1 for the `ul` and 1 more for the trick to work.
 */
.navigation-flyout--2,
.navigation-flyout--3 {
  position: relative;
  top: auto;
  grid-column: 2;
  grid-row-start: 1;
  grid-row-end: calc(var(--nav-items-count) + 2); /* [1] */
  grid-template-columns: 1fr 1fr;
}

.navigation-flyout--2 {
  background-color: lightblue;
}

.navigation-flyout--3 {
  background-color: lightpink;
  grid-template-columns: 1fr;
}

.navigation-item:hover > .navigation-flyout {
  display: grid;
}