skip to Main Content

I have the following design created by a Youtube tutorial about magic indicators:
enter image description here

But I want a smooth, not so steep curve as a continuation of the circle when selected instead of using element with small border-radius like in the original solution and I don’t know how to make it perfectly match the border of the circle. Code snippet is below. On the screenshot below I show with arrows the places where I make manual adjustments which don’t completely work. I’m looking for a CSS-only solution.
enter image description here

const indicator = document.querySelector("[data-indicator]")

document.addEventListener("click", e => {
  let anchor
  if (e.target.matches("a")) {
    anchor = e.target
  } else {
    anchor = e.target.closest("a")
  }
  if (anchor != null) {
    const allAnchors = [...document.querySelectorAll("a")]
    const index = allAnchors.indexOf(anchor)
    indicator.style.setProperty("--position", index)
    document.querySelectorAll("a").forEach(elem => {
      elem.classList.remove("active")
    })
    anchor.classList.add("active")
  }
})
*, *::before, *::after {
  box-sizing: border-box;
  font-family: Arial, Helvetica, sans-serif;
}

body {
  background-color: var(--background-color);
  color: white;
}

:root {
  --icon-size: 2rem;
  --indicator-spacing: calc(var(--icon-size) / 8);
  --border-radius: calc(var(--icon-size) / 4);
  --nav-item-padding: calc(var(--icon-size) / 2);
  --background-color: #333;
}

.navbar-container {
  background-color: white;
  border-radius: var(--border-radius);
  width: max-content;
  margin: 0 auto;
  margin-top: 10rem;
  padding: 0 calc(var(--nav-item-padding) * 1.5);
}

.list {
  display: flex;
  margin: 0;
  padding: 0;
  list-style: none;
}

.list a {
  color: #333;
  text-decoration: none;
  display: flex;
  flex-direction: column;
  align-items: center;
  padding: var(--nav-item-padding);
}

.list .text {
  font-size: .8em;
  opacity: 0;
  pointer-events: none;
  transition: 250ms ease-in-out;
  position: absolute;
  bottom: calc(.5 * var(--nav-item-padding));
  transform: translateY(50%);
}

.list .icon {
  position: relative;
  transition: 250ms ease-in-out;
}

.list .icon svg {
  fill: currentColor;
  width: var(--icon-size);
  height: var(--icon-size);
  display: block;
}

.list .active .text {
  pointer-events: all;
  opacity: 1;
  transform: translateY(0);
}

.list .active .icon {
  transform: translateY(calc(-50% - var(--nav-item-padding)));
}

/* .list .active::before {
  content: "";
  box-sizing: content-box;
  position: absolute;
  width: var(--border-radius);
  height: var(--border-radius);
  background-color: white;
  z-index: 1;
  top: calc(-1 * var(--indicator-spacing));
  left: calc(.2 * var(--indicator-spacing));
  transform: translateX(-100%);
  border-top-right-radius: 100%;
  border-width: calc(var(--indicator-spacing));
  border-color: var(--background-color);
  border-style: solid;
  border-bottom: none;
  border-left: none;
}

.list .active::after {
  content: "";
  box-sizing: content-box;
  position: absolute;
  width: var(--border-radius);
  height: var(--border-radius);
  background-color: white;
  z-index: 1;
  top: calc(-1 * var(--indicator-spacing));
  right: calc(.2 * var(--indicator-spacing));
  transform: translateX(100%);
  border-top-left-radius: 100%;
  border-width: calc(var(--indicator-spacing));
  border-color: var(--background-color);
  border-style: solid;
  border-bottom: none;
  border-right: none;
} */

.list {
  position: relative;
}

.indicator {
  position: absolute;
  left: calc(var(--position) * (var(--icon-size) + var(--nav-item-padding) * 2));
  transition: 250ms ease-in-out;
}

.indicator::after,
.indicator::before {
  content: "";
  position: absolute;
  border-radius: 100%;
}

.indicator::after {
  background-color: hsl(100, 100%, 50%);
  width: calc(var(--icon-size) * 2);
  height: calc(var(--icon-size) * 2);
  top: calc(-1 * var(--icon-size));
}
.indicator::before {
  background-color: var(--background-color);
  width: calc((var(--icon-size) + var(--indicator-spacing)) * 2);
  height: calc((var(--icon-size) + var(--indicator-spacing)) * 2);
  top: calc(-1 * var(--icon-size) - var(--indicator-spacing));
  left: calc(-1 * var(--indicator-spacing));
}

.corners::before {
  content: "";
  box-sizing: content-box;
  position: absolute;
  width: var(--border-radius);
  height: var(--border-radius);
  background-color: white;
  z-index: 1;
  top: calc(-1 * var(--indicator-spacing));
  left: calc(.2 * var(--indicator-spacing));
  transform: translateX(-100%);
  border-top-right-radius: 100%;
  border-width: calc(var(--indicator-spacing));
  border-color: var(--background-color);
  border-style: solid;
  border-bottom: none;
  border-left: none;
}

.corners::after {
  content: "";
  box-sizing: content-box;
  position: absolute;
  width: var(--border-radius);
  height: var(--border-radius);
  background-color: white;
  z-index: 1;
  top: calc(-1 * var(--indicator-spacing));
  left: calc(var(--icon-size) * 2 + -.2 * var(--indicator-spacing));
  border-top-left-radius: 100%;
  border-width: calc(var(--indicator-spacing));
  border-color: var(--background-color);
  border-style: solid;
  border-bottom: none;
  border-right: none;
}
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <link rel="stylesheet" href="styles.css">
  <script src="script.js" defer></script>
</head>
<body>
  <nav class="navbar-container">
    <ul class="list">
      <div style="--position: 0;" data-indicator class="indicator">
        <div class="corners"></div>
      </div>
      <li><a href="#" class="active">
        <div class="icon">
          <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" ><path d="M0 0h24v24H0V0z" fill="none"/><path d="M12 5.69l5 4.5V18h-2v-6H9v6H7v-7.81l5-4.5M12 3L2 12h3v8h6v-6h2v6h6v-8h3L12 3z"/>
          </svg>
        </div>
        <div class="text">Home</div>
      </a></li>
      <li><a href="#">
        <div class="icon">
          <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M0 0h24v24H0V0z" fill="none"/><path d="M12 5.9c1.16 0 2.1.94 2.1 2.1s-.94 2.1-2.1 2.1S9.9 9.16 9.9 8s.94-2.1 2.1-2.1m0 9c2.97 0 6.1 1.46 6.1 2.1v1.1H5.9V17c0-.64 3.13-2.1 6.1-2.1M12 4C9.79 4 8 5.79 8 8s1.79 4 4 4 4-1.79 4-4-1.79-4-4-4zm0 9c-2.67 0-8 1.34-8 4v3h16v-3c0-2.66-5.33-4-8-4z"/></svg>
        </div>
        <div class="text">Account</div>
      </a></li>
      <li><a href="#">
        <div class="icon">
          <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M0 0h24v24H0V0z" fill="none"/><path d="M20 2H4c-1.1 0-2 .9-2 2v18l4-4h14c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zm0 14H6l-2 2V4h16v12z"/></svg>
        </div>
        <div class="text">Messages</div>
      </a></li>
      <li><a href="#">
        <div class="icon">
          <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M0 0h24v24H0V0z" fill="none"/><path d="M14.12 4l1.83 2H20v12H4V6h4.05l1.83-2h4.24M15 2H9L7.17 4H4c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2h-3.17L15 2zm-3 7c1.65 0 3 1.35 3 3s-1.35 3-3 3-3-1.35-3-3 1.35-3 3-3m0-2c-2.76 0-5 2.24-5 5s2.24 5 5 5 5-2.24 5-5-2.24-5-5-5z"/></svg>
        </div>
        <div class="text">Photos</div>
      </a></li>
      <li><a href="#">
        <div class="icon">
          <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M0 0h24v24H0V0z" fill="none"/><path d="M19.43 12.98c.04-.32.07-.64.07-.98 0-.34-.03-.66-.07-.98l2.11-1.65c.19-.15.24-.42.12-.64l-2-3.46c-.09-.16-.26-.25-.44-.25-.06 0-.12.01-.17.03l-2.49 1c-.52-.4-1.08-.73-1.69-.98l-.38-2.65C14.46 2.18 14.25 2 14 2h-4c-.25 0-.46.18-.49.42l-.38 2.65c-.61.25-1.17.59-1.69.98l-2.49-1c-.06-.02-.12-.03-.18-.03-.17 0-.34.09-.43.25l-2 3.46c-.13.22-.07.49.12.64l2.11 1.65c-.04.32-.07.65-.07.98 0 .33.03.66.07.98l-2.11 1.65c-.19.15-.24.42-.12.64l2 3.46c.09.16.26.25.44.25.06 0 .12-.01.17-.03l2.49-1c.52.4 1.08.73 1.69.98l.38 2.65c.03.24.24.42.49.42h4c.25 0 .46-.18.49-.42l.38-2.65c.61-.25 1.17-.59 1.69-.98l2.49 1c.06.02.12.03.18.03.17 0 .34-.09.43-.25l2-3.46c.12-.22.07-.49-.12-.64l-2.11-1.65zm-1.98-1.71c.04.31.05.52.05.73 0 .21-.02.43-.05.73l-.14 1.13.89.7 1.08.84-.7 1.21-1.27-.51-1.04-.42-.9.68c-.43.32-.84.56-1.25.73l-1.06.43-.16 1.13-.2 1.35h-1.4l-.19-1.35-.16-1.13-1.06-.43c-.43-.18-.83-.41-1.23-.71l-.91-.7-1.06.43-1.27.51-.7-1.21 1.08-.84.89-.7-.14-1.13c-.03-.31-.05-.54-.05-.74s.02-.43.05-.73l.14-1.13-.89-.7-1.08-.84.7-1.21 1.27.51 1.04.42.9-.68c.43-.32.84-.56 1.25-.73l1.06-.43.16-1.13.2-1.35h1.39l.19 1.35.16 1.13 1.06.43c.43.18.83.41 1.23.71l.91.7 1.06-.43 1.27-.51.7 1.21-1.07.85-.89.7.14 1.13zM12 8c-2.21 0-4 1.79-4 4s1.79 4 4 4 4-1.79 4-4-1.79-4-4-4zm0 6c-1.1 0-2-.9-2-2s.9-2 2-2 2 .9 2 2-.9 2-2 2z"/></svg>
        </div>
        <div class="text">Settings</div>
      </a></li>
    </ul>
  </nav>
</body>
</html>

2

Answers


  1. Chosen as BEST ANSWER

    I'm also posting my own solution using clip-path as suggested by @Paulie_D since it has its own benefits and drawbacks compared to @Brett Donald's solution.

    Here the drawback is I need to hardcode the point where the clipped content (watch for classes .clip-wrapper and .clip) hits the circle (::before of .indicator). I had to also put some left to .clip-wrapper but that's tightly coupled with the point of intersection so I count it as one situation of hardcoding.

    I manually composed the curve using cubic bezier curve and respectively the mirrored one on the right, but that was relatively easy to do.

    The wrapper is necessary so I had the two colors when clipping - white for the clipped content and dark for the rest.

    const indicator = document.querySelector('[data-indicator]');
    
    document.addEventListener('click', e => {
        let anchor;
    
        if (e.target.matches("a")) {
            anchor = e.target;
        } else {
            anchor = e.target.closest("a");
        }
    
        if (anchor != null) {
            const allAnchors = [...document.querySelectorAll("a")];
            const index = allAnchors.indexOf(anchor);
            indicator.style.setProperty("--position", index);
    
            document.querySelectorAll("a").forEach(elem => {
                elem.classList.remove("active");
            });
            anchor.classList.add("active");
        }
    });
    *,
    *::after,
    ::before {
      box-sizing: border-box;
      font-family: Arial, Helvetica, sans-serif;
    }
    
    :root {
      --icon-size: 2rem;
      --nav-item-padding: calc(var(--icon-size) / 2);
      --background-color: #333;
      --border-radius: calc(var(--icon-size) / 4);
      --indicator-spacing: calc(var(--icon-size) / 8);
    }
    
    body {
      background-color: var(--background-color);
      color: white;
    }
    
    .navbar-container {
      background-color: white;
      border-radius: 0.5rem;
      width: max-content;
      margin: 0 auto;
      margin-top: 10rem;
      padding: 0 calc(var(--nav-item-padding) * 4); // * 1.5
    }
    
    .list {
      display: flex;
      margin: 0;
      padding: 0;
      list-style: none;
      text-decoration: none;
    }
    
    .list a {
      color: #333;
      text-decoration: none;
      display: flex;
      flex-direction: column;
      align-items: center;
      padding: var(--nav-item-padding);
    }
    
    .list .text {
      font-size: 0.8em;
      opacity: 0;
      pointer-events: none;
      transition: 500ms ease-in-out;
      position: absolute;
      bottom: calc(0.5 * var(--nav-item-padding));
      transform: translateY(50%);
    }
    
    .list .icon svg {
      fill: currentColor;
      width: var(--icon-size);
      height: var(--icon-size);
      display: block;
    }
    
    .list .active .text {
      pointer-events: all;
      opacity: 1;
      transform: translateY(0);
    }
    
    .list .icon {
      position: relative;
      transition: 250ms ease-in-out;
    }
    
    .list .active .icon {
      transform: translateY(calc(-50% - var(--nav-item-padding)));
    }
    
    /* .list .active::before {
      content: "";
      position: absolute;
      box-sizing: content-box;
      width: var(--border-radius);
      height: var(--border-radius);
      background-color: white;
      z-index: 1;
      top: calc(-1 * var(--indicator-spacing));
      left: calc(.2 * var(--indicator-spacing));
      transform: translateX(-100%); 
      border-top-right-radius: 100%;
      border-width: calc(var(--indicator-spacing));
      border-color: var(--background-color);
      border-style: solid;
      border-bottom: none;
      border-left: none;
    }
    
    .list .active::after {
      content: "";
      position: absolute;
      box-sizing: content-box;
      width: var(--border-radius);
      height: var(--border-radius);
      background-color: white;
      z-index: 1;
      top: calc(-1 * var(--indicator-spacing));
      right: calc(.2 * var(--indicator-spacing));
      transform: translateX(100%); 
      border-top-left-radius: 100%;
      border-width: calc(var(--indicator-spacing));
      border-color: var(--background-color);
      border-style: solid;
      border-bottom: none;
      border-right: none;
    } */
    
    .indicator {
      position: absolute;
      left: calc(
        var(--position) * (var(--icon-size) + var(--nav-item-padding) * 2)
      );
      transition: 250ms ease-in-out;
    }
    
    .indicator::before,
    .indicator::after {
      content: "";
      position: absolute;
      border-radius: 100%;
    }
    
    .indicator::after {
      background-color: hsl(100, 100%, 50%);
      width: calc(var(--icon-size) * 2);
      height: calc(var(--icon-size) * 2);
      top: calc(-1 * var(--icon-size));
      // left: 1px; // comment for other solutions
      /* left: calc(-.5 * var(--icon-size)); */
    }
    
    .indicator::before {
      background-color: var(--background-color);
      width: calc((var(--icon-size) + var(--indicator-spacing)) * 2);
      height: calc((var(--icon-size) + var(--indicator-spacing)) * 2);
      top: calc(-1 * var(--icon-size) - var(--indicator-spacing));
      left: calc(-1 * var(--indicator-spacing));
    }
    
    .list {
      position: relative;
    }
    
    // .corners::before {
    //   content: "";
    //   position: absolute;
    //   box-sizing: content-box;
    //   width: var(--border-radius);
    //   height: var(--border-radius);
    //   background-color: white;
    //   z-index: 1;
    //   top: calc(-1 * var(--indicator-spacing));
    //   left: calc(.2 * var(--indicator-spacing));
    //   transform: translateX(-100%);
    //   border-top-right-radius: 100%;
    //   border-width: calc(var(--indicator-spacing));
    //   border-color: var(--background-color);
    //   border-style: solid;
    //   border-bottom: none;
    //   border-left: none;
    // }
    
    // .corners::after {
    //   content: "";
    //   position: absolute;
    //   box-sizing: content-box;
    //   width: var(--border-radius);
    //   height: var(--border-radius);
    //   background-color: white;
    //   z-index: 1;
    //   top: calc(-1 * var(--indicator-spacing));
    //   left: calc(var(--icon-size) * 2 - .2 * var(--indicator-spacing));
    //   border-top-left-radius: 100%;
    //   border-width: calc(var(--indicator-spacing));
    //   border-color: var(--background-color);
    //   border-style: solid;
    //   border-bottom: none;
    //   border-right: none;
    // }
    
    // .corners::before {
    //   position: absolute;
    //   content: url("data:image/svg+xml,%3C%3Fxml version='1.0' encoding='utf-8'%3F%3E%3Csvg viewBox='0 0 261.3 100' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M 261.29 50.002 C 254.134 50.002 247.631 54.09 245.969 60.432 C 241.212 83.005 221.119 100.003 197.056 100.003 C 181.263 100.003 167.18 92.681 158.021 81.26 C 155.639 78.412 149.785 72.471 145.755 69.829 C 123.211 55.046 95.066 52.813 68.823 51.867 C 44.982 51.007 23.961 50.016 0 50.002 C 23.961 49.987 44.982 48.996 68.823 48.136 C 95.066 47.19 123.211 44.957 145.755 30.174 C 149.785 27.532 155.639 21.591 158.021 18.743 C 167.18 7.322 181.263 0 197.056 0 C 221.119 0 241.212 16.998 245.969 39.571 C 247.631 45.913 254.134 50.002 261.29 50.002 Z' style='stroke: rgb(0, 0, 0); stroke-width: 0px; fill: rgb(51, 51, 51);'/%3E%3C/svg%3E");
    //   height: calc((var(--icon-size) + var(--indicator-spacing)) * 2);
    //   aspect-ratio: 2613 / 1000;
    //   top: calc(-1 * var(--icon-size) - var(--indicator-spacing));
    //   left: calc(-1 * var(--indicator-spacing) - 105px);
    // }
    
    // .corners::after {
    //   position: absolute;
    //   content: url("data:image/svg+xml,%3C%3Fxml version='1.0' encoding='utf-8'%3F%3E%3Csvg viewBox='0 0 261.3 100' xmlns='http://www.w3.org/2000/svg'%3E%3Cdefs/%3E%3Cpath d='M 261.29 50.002 C 254.134 50.002 247.631 54.09 245.969 60.432 C 241.212 83.005 221.119 100.003 197.056 100.003 C 181.263 100.003 167.18 92.681 158.021 81.26 C 155.639 78.412 149.785 72.471 145.755 69.829 C 123.211 55.046 95.066 52.813 68.823 51.867 C 44.982 51.007 23.961 50.016 0 50.002 C 23.961 49.987 44.982 48.996 68.823 48.136 C 95.066 47.19 123.211 44.957 145.755 30.174 C 149.785 27.532 155.639 21.591 158.021 18.743 C 167.18 7.322 181.263 0 197.056 0 C 221.119 0 241.212 16.998 245.969 39.571 C 247.631 45.913 254.134 50.002 261.29 50.002 Z' style='stroke: rgb(0, 0, 0); stroke-width: 0px; fill: rgb(51, 51, 51); transform-box: fill-box; transform-origin: 50%25 50%25;' transform='matrix(-1, 0, 0, -1, -0.000009, -0.000001)'/%3E%3C/svg%3E");
    //   height: calc((var(--icon-size) + var(--indicator-spacing)) * 2);
    //   aspect-ratio: 2613 / 1000;
    //   top: calc(-1 * var(--icon-size) - var(--indicator-spacing));
    //   left: calc(-1 * var(--indicator-spacing) - 10px);
    // }
    
    .clip-wrapper {
      position: absolute;
      width: 64px;
      height: 64px;
      background-color: var(--background-color);
    }
    
    .clip {
      transform: skewY(
        0.001deg
      ); // https://stackoverflow.com/questions/51542919/clip-path-on-chrome-leaves-a-strange-line-on-the-edge
      z-index: 1;
      width: 64px;
      height: 64px;
      background: white;
    }
    
    .clip-wrapper.left {
      left: 8px;
      transform: translateX(-100%);
    }
    
    .clip-wrapper.left .clip {
      background: white;
      clip-path: path("M 0 0 C 25 0, 35 0, 64 26.9 L 64 64 L 0 64 L 0 0z");
    }
    
    .clip-wrapper.right {
      left: -8px;
      transform: translateX(100%);
    }
    
    .clip-wrapper.right .clip {
      clip-path: path("M 64 0 C 39 0, 29 0, 0 26.9 L 0 64 L 64 64 L 64 0z");
    }
    <html>
      <head>
        <link rel="stylesheet" href="css/style.css" />
        <script src="script.js" defer></script>
      </head>
    
      <body>
        <nav class="navbar-container">
          <ul class="list">
            <div style="--position: 0;" data-indicator class="indicator">
              <div class="clip-wrapper left">
                <div class="clip">
                </div>
              </div>
              <div class="clip-wrapper right">
                <div class="clip">
                </div>
              </div>
              <!-- <div class="corners"></div> -->
            </div>
            <li>
              <a href="#" class="active">
                <div class="icon">
                  <svg
                    xmlns="http://www.w3.org/2000/svg"
                    viewBox="0 0 24 24"
                    fill="#000000"
                  >
                    <path d="M0 0h24v24H0V0z" fill="none" />
                    <path
                      d="M12 5.69l5 4.5V18h-2v-6H9v6H7v-7.81l5-4.5M12 3L2 12h3v8h6v-6h2v6h6v-8h3L12 3z"
                    />
                  </svg>
                </div>
                <div class="text">Home</div>
              </a>
            </li>
            <li>
              <a href="#">
                <div class="icon">
                  <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="#000000"><path d="M0 0h24v24H0V0z" fill="none"/><path d="M12 5.9c1.16 0 2.1.94 2.1 2.1s-.94 2.1-2.1 2.1S9.9 9.16 9.9 8s.94-2.1 2.1-2.1m0 9c2.97 0 6.1 1.46 6.1 2.1v1.1H5.9V17c0-.64 3.13-2.1 6.1-2.1M12 4C9.79 4 8 5.79 8 8s1.79 4 4 4 4-1.79 4-4-1.79-4-4-4zm0 9c-2.67 0-8 1.34-8 4v3h16v-3c0-2.66-5.33-4-8-4z"/></svg>
                </div>
                <div class="text">Account</div>
              </a>
            </li>
            <li>
              <a href="#">
                <div class="icon">
                  <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="#000000"><path d="M0 0h24v24H0V0z" fill="none"/><path d="M20 2H4c-1.1 0-2 .9-2 2v18l4-4h14c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zm0 14H6l-2 2V4h16v12z"/></svg>
                </div>
                <div class="text">Message</div>
              </a>
            </li>
            <li>
              <a href="#">
                <div class="icon">
                  <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="#000000"><path d="M0 0h24v24H0V0z" fill="none"/><path d="M14.12 4l1.83 2H20v12H4V6h4.05l1.83-2h4.24M15 2H9L7.17 4H4c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2h-3.17L15 2zm-3 7c1.65 0 3 1.35 3 3s-1.35 3-3 3-3-1.35-3-3 1.35-3 3-3m0-2c-2.76 0-5 2.24-5 5s2.24 5 5 5 5-2.24 5-5-2.24-5-5-5z"/></svg>
                </div>
                <div class="text">Photos</div>
              </a>
            </li>
            <li>
              <a href="#">
                <div class="icon">
                  <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="#000000"><path d="M0 0h24v24H0V0z" fill="none"/><path d="M19.43 12.98c.04-.32.07-.64.07-.98 0-.34-.03-.66-.07-.98l2.11-1.65c.19-.15.24-.42.12-.64l-2-3.46c-.09-.16-.26-.25-.44-.25-.06 0-.12.01-.17.03l-2.49 1c-.52-.4-1.08-.73-1.69-.98l-.38-2.65C14.46 2.18 14.25 2 14 2h-4c-.25 0-.46.18-.49.42l-.38 2.65c-.61.25-1.17.59-1.69.98l-2.49-1c-.06-.02-.12-.03-.18-.03-.17 0-.34.09-.43.25l-2 3.46c-.13.22-.07.49.12.64l2.11 1.65c-.04.32-.07.65-.07.98 0 .33.03.66.07.98l-2.11 1.65c-.19.15-.24.42-.12.64l2 3.46c.09.16.26.25.44.25.06 0 .12-.01.17-.03l2.49-1c.52.4 1.08.73 1.69.98l.38 2.65c.03.24.24.42.49.42h4c.25 0 .46-.18.49-.42l.38-2.65c.61-.25 1.17-.59 1.69-.98l2.49 1c.06.02.12.03.18.03.17 0 .34-.09.43-.25l2-3.46c.12-.22.07-.49-.12-.64l-2.11-1.65zm-1.98-1.71c.04.31.05.52.05.73 0 .21-.02.43-.05.73l-.14 1.13.89.7 1.08.84-.7 1.21-1.27-.51-1.04-.42-.9.68c-.43.32-.84.56-1.25.73l-1.06.43-.16 1.13-.2 1.35h-1.4l-.19-1.35-.16-1.13-1.06-.43c-.43-.18-.83-.41-1.23-.71l-.91-.7-1.06.43-1.27.51-.7-1.21 1.08-.84.89-.7-.14-1.13c-.03-.31-.05-.54-.05-.74s.02-.43.05-.73l.14-1.13-.89-.7-1.08-.84.7-1.21 1.27.51 1.04.42.9-.68c.43-.32.84-.56 1.25-.73l1.06-.43.16-1.13.2-1.35h1.39l.19 1.35.16 1.13 1.06.43c.43.18.83.41 1.23.71l.91.7 1.06-.43 1.27-.51.7 1.21-1.07.85-.89.7.14 1.13zM12 8c-2.21 0-4 1.79-4 4s1.79 4 4 4 4-1.79 4-4-1.79-4-4-4zm0 6c-1.1 0-2-.9-2-2s.9-2 2-2 2 .9 2 2-.9 2-2 2z"/></svg>
                </div>
                <div class="text">Settings</div>
              </a>
            </li>
          </ul>
        </nav>
      </body>
    </html>


  2. I would create an SVG shape to serve as the background of the .indicator::before pseudo-element. I would start with a circle bisected by a line, then draw some paths in the corners, then join it all together into a single filled path.

    enter image description here

    You then need to encode the SVG for use in CSS. After setting it as the content of the .indicator::before pseudo-element, and deleting the .corners element which is no longer needed, the final result looks like this:

    enter image description here

    Snippet below to demonstrate.

    const indicator = document.querySelector("[data-indicator]")
    
    document.addEventListener("click", e => {
      let anchor
      if (e.target.matches("a")) {
        anchor = e.target
      } else {
        anchor = e.target.closest("a")
      }
      if (anchor != null) {
        const allAnchors = [...document.querySelectorAll("a")]
        const index = allAnchors.indexOf(anchor)
        indicator.style.setProperty("--position", index)
        document.querySelectorAll("a").forEach(elem => {
          elem.classList.remove("active")
        })
        anchor.classList.add("active")
      }
    })
    *, *::before, *::after {
      box-sizing: border-box;
      font-family: Arial, Helvetica, sans-serif;
    }
    
    :root {
      --icon-size: 2rem;
      --indicator-spacing: calc(var(--icon-size) / 8);
      --border-radius: calc(var(--icon-size) / 4);
      --nav-item-padding: calc(var(--icon-size) / 2);
      --background-color: #333;
    }
    
    body {
      background-color: var(--background-color);
      color: white;
    }
    
    .navbar-container {
      background-color: white;
      border-radius: var(--border-radius);
      width: max-content;
      margin: 0 auto;
      margin-top: 5rem;
      padding: 0 calc(var(--nav-item-padding) * 1.5);
    }
    
    .list {
      display: flex;
      margin: 0;
      padding: 0;
      list-style: none;
    }
    
    .list a {
      color: #333;
      text-decoration: none;
      display: flex;
      flex-direction: column;
      align-items: center;
      padding: var(--nav-item-padding);
    }
    
    .list .text {
      font-size: .8em;
      opacity: 0;
      pointer-events: none;
      transition: 250ms ease-in-out;
      position: absolute;
      bottom: calc(.5 * var(--nav-item-padding));
      transform: translateY(50%);
    }
    
    .list .icon {
      position: relative;
      transition: 250ms ease-in-out;
    }
    
    .list .icon svg {
      fill: currentColor;
      width: var(--icon-size);
      height: var(--icon-size);
      display: block;
    }
    
    .list .active .text {
      pointer-events: all;
      opacity: 1;
      transform: translateY(0);
    }
    
    .list .active .icon {
      transform: translateY(calc(-50% - var(--nav-item-padding)));
    }
    
    .list {
      position: relative;
    }
    
    .indicator {
      position: absolute;
      left: calc(var(--position) * (var(--icon-size) + var(--nav-item-padding) * 2));
      transition: 250ms ease-in-out;
    }
    
    .indicator::after, .indicator::before {
      content: "";
      position: absolute;
    }
    
    .indicator::after {
      background-color: hsl(100, 100%, 50%);
      width: calc(var(--icon-size) * 2);
      height: calc(var(--icon-size) * 2);
      top: calc(-1 * var(--icon-size));
      border-radius: 100%;
    }
    
    .indicator::before {
      content: url("data:image/svg+xml,%3C%3Fxml version='1.0' encoding='utf-8'%3F%3E%3Csvg viewBox='0 0 261.3 100' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M 261.29 50.002 C 254.134 50.002 247.631 54.09 245.969 60.432 C 241.212 83.005 221.119 100.003 197.056 100.003 C 181.263 100.003 167.18 92.681 158.021 81.26 C 155.639 78.412 149.785 72.471 145.755 69.829 C 123.211 55.046 95.066 52.813 68.823 51.867 C 44.982 51.007 23.961 50.016 0 50.002 C 23.961 49.987 44.982 48.996 68.823 48.136 C 95.066 47.19 123.211 44.957 145.755 30.174 C 149.785 27.532 155.639 21.591 158.021 18.743 C 167.18 7.322 181.263 0 197.056 0 C 221.119 0 241.212 16.998 245.969 39.571 C 247.631 45.913 254.134 50.002 261.29 50.002 Z' style='stroke: rgb(0, 0, 0); stroke-width: 0px; fill: rgb(51, 51, 51);'/%3E%3C/svg%3E");
      height: calc((var(--icon-size) + var(--indicator-spacing)) * 2);
      aspect-ratio: 2613 / 1000;
      top: calc(-1 * var(--icon-size) - var(--indicator-spacing));
      left: calc(-1 * var(--indicator-spacing) - 105px);
    }
    <nav class="navbar-container">
      <ul class="list">
        <div style="--position: 0;" data-indicator class="indicator">
        </div>
        <li><a href="#" class="active">
          <div class="icon">
            <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" ><path d="M0 0h24v24H0V0z" fill="none"/><path d="M12 5.69l5 4.5V18h-2v-6H9v6H7v-7.81l5-4.5M12 3L2 12h3v8h6v-6h2v6h6v-8h3L12 3z"/>
            </svg>
          </div>
          <div class="text">Home</div>
        </a></li>
        <li><a href="#">
          <div class="icon">
            <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M0 0h24v24H0V0z" fill="none"/><path d="M12 5.9c1.16 0 2.1.94 2.1 2.1s-.94 2.1-2.1 2.1S9.9 9.16 9.9 8s.94-2.1 2.1-2.1m0 9c2.97 0 6.1 1.46 6.1 2.1v1.1H5.9V17c0-.64 3.13-2.1 6.1-2.1M12 4C9.79 4 8 5.79 8 8s1.79 4 4 4 4-1.79 4-4-1.79-4-4-4zm0 9c-2.67 0-8 1.34-8 4v3h16v-3c0-2.66-5.33-4-8-4z"/></svg>
          </div>
          <div class="text">Account</div>
        </a></li>
        <li><a href="#">
          <div class="icon">
            <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M0 0h24v24H0V0z" fill="none"/><path d="M20 2H4c-1.1 0-2 .9-2 2v18l4-4h14c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zm0 14H6l-2 2V4h16v12z"/></svg>
          </div>
          <div class="text">Messages</div>
        </a></li>
        <li><a href="#">
          <div class="icon">
            <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M0 0h24v24H0V0z" fill="none"/><path d="M14.12 4l1.83 2H20v12H4V6h4.05l1.83-2h4.24M15 2H9L7.17 4H4c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2h-3.17L15 2zm-3 7c1.65 0 3 1.35 3 3s-1.35 3-3 3-3-1.35-3-3 1.35-3 3-3m0-2c-2.76 0-5 2.24-5 5s2.24 5 5 5 5-2.24 5-5-2.24-5-5-5z"/></svg>
          </div>
          <div class="text">Photos</div>
        </a></li>
        <li><a href="#">
          <div class="icon">
            <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M0 0h24v24H0V0z" fill="none"/><path d="M19.43 12.98c.04-.32.07-.64.07-.98 0-.34-.03-.66-.07-.98l2.11-1.65c.19-.15.24-.42.12-.64l-2-3.46c-.09-.16-.26-.25-.44-.25-.06 0-.12.01-.17.03l-2.49 1c-.52-.4-1.08-.73-1.69-.98l-.38-2.65C14.46 2.18 14.25 2 14 2h-4c-.25 0-.46.18-.49.42l-.38 2.65c-.61.25-1.17.59-1.69.98l-2.49-1c-.06-.02-.12-.03-.18-.03-.17 0-.34.09-.43.25l-2 3.46c-.13.22-.07.49.12.64l2.11 1.65c-.04.32-.07.65-.07.98 0 .33.03.66.07.98l-2.11 1.65c-.19.15-.24.42-.12.64l2 3.46c.09.16.26.25.44.25.06 0 .12-.01.17-.03l2.49-1c.52.4 1.08.73 1.69.98l.38 2.65c.03.24.24.42.49.42h4c.25 0 .46-.18.49-.42l.38-2.65c.61-.25 1.17-.59 1.69-.98l2.49 1c.06.02.12.03.18.03.17 0 .34-.09.43-.25l2-3.46c.12-.22.07-.49-.12-.64l-2.11-1.65zm-1.98-1.71c.04.31.05.52.05.73 0 .21-.02.43-.05.73l-.14 1.13.89.7 1.08.84-.7 1.21-1.27-.51-1.04-.42-.9.68c-.43.32-.84.56-1.25.73l-1.06.43-.16 1.13-.2 1.35h-1.4l-.19-1.35-.16-1.13-1.06-.43c-.43-.18-.83-.41-1.23-.71l-.91-.7-1.06.43-1.27.51-.7-1.21 1.08-.84.89-.7-.14-1.13c-.03-.31-.05-.54-.05-.74s.02-.43.05-.73l.14-1.13-.89-.7-1.08-.84.7-1.21 1.27.51 1.04.42.9-.68c.43-.32.84-.56 1.25-.73l1.06-.43.16-1.13.2-1.35h1.39l.19 1.35.16 1.13 1.06.43c.43.18.83.41 1.23.71l.91.7 1.06-.43 1.27-.51.7 1.21-1.07.85-.89.7.14 1.13zM12 8c-2.21 0-4 1.79-4 4s1.79 4 4 4 4-1.79 4-4-1.79-4-4-4zm0 6c-1.1 0-2-.9-2-2s.9-2 2-2 2 .9 2 2-.9 2-2 2z"/></svg>
          </div>
          <div class="text">Settings</div>
        </a></li>
      </ul>
    </nav>

    It’s probably possible take the path out of this SVG and use it as a clip-path instead, but the end result would be the same.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search