<div class="features">
  <svg class="features__svg" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" height="33">
    <defs>
      <pattern id="pattern" width="82" height="33" patternUnits="userSpaceOnUse">
        <line y1="22.5" x2="82" y2="22.5" fill="none" stroke="#738fc8" />
        <line y1="16.5" x2="82" y2="16.5" fill="none" stroke="#738fc8" />
        <line y1="10.5" x2="82" y2="10.5" fill="none" stroke="#738fc8" />
        <polyline points="28 30.5 13.34 30.5 5.34 22.5" fill="none" stroke="#738fc8" />
        <polyline points="48 2.5 34.94 2.5 26.94 10.5" fill="none" stroke="#738fc8" />
        <circle cx="28" cy="30.5" r="2" fill="#738fc8" />
        <circle cx="48" cy="2.5" r="2" fill="#738fc8" />
      </pattern>
    </defs>

    <rect width="100%" height="100%" fill="url(#pattern)" />
  </svg>

  <div class="features__main"></div>
  <ul class="features__list">
    <li class="features__item">Lorem ipsum dolor sit amet consectetur adipisicing elit</li>
    <li class="features__item">Odit reprehenderit nostrum culpa distinctio sapiente</li>
    <li class="features__item">Possimus quis odit, atque ea mollitia id dolores minima cumque quaerat</li>
    <li class="features__item">Molestiae nisi rem sit minima perferendis nemo</li>
    <li class="features__item">Veritatis molestiae libero recusandae labore earum deleniti </li>
    <li class="features__item">arum fugiat placeat quidem! Ratione quas fuga</li>
    <li class="features__item">Lorem ipsum dolor sit amet consectetur adipisicing elit</li>
    <li class="features__item">Odit reprehenderit nostrum culpa distinctio sapiente</li>
  </ul>
</div>
* {
  box-sizing: border-box;
}

.features {
  position: relative;
  z-index: 0;
}

.features__svg {
  transform-origin: 0 50%;
  position: absolute;
  pointer-events: none;
  width: 0;
  transition: width 350ms;
  z-index: -1;
}

.features__main {
  position: absolute;
  top: 50%;
  left: 50%;
  width: 25%;
  transform: translate(-50%, -50%);
  background-color: #cc07;
}

.features__main::before {
  content: '';
  display: block;
  width: 100%;
  padding-bottom: 100%;
}

.features__list {
  display: flex;
  flex-wrap: wrap;
  list-style: none;
  margin: 0;
  padding: 0;
  cursor: default;
}

.features__item {
  width: 25%;
  background-color: #ccc7;
  padding: 15px;
  margin-top: 15px;
  margin-bottom: 15px;
}

.features__item:first-child,
.features__item:last-child {
  margin-left: 37.5%;
  margin-right: 37.5%;
}

.features__item:nth-child(2),
.features__item:nth-child(6) {
  margin-left: 50px;
  margin-right: calc(25% - 50px);
}

.features__item:nth-child(3),
.features__item:nth-child(7) {
  margin-right: 50px;
  margin-left: calc(25% - 50px);
}

.features__item:nth-child(4) {
  margin-left: 0;
  margin-right: 25%;
}

.features__item:nth-child(5) {
  margin-left: 25%;
  margin-right: 0;
}
const featuresEl = document.querySelector('.features');
const featuresSvgEl = featuresEl.querySelector('.features__svg');
const centerEl = featuresEl.querySelector('.features__main');
const svgHeight = 33;
let currentOverEl = null;

function getDistAngle(r1, r2) {
  const dx = (r1.x + r1.width * 0.5) - (r2.x + r2.width * 0.5);
  const dy = (r1.y + r1.height * 0.5) - (r2.y + r2.height * 0.5);
  const dist = Math.hypot(dx, dy);
  const angle = 180 * Math.atan2(dy, dx) / Math.PI;
  
  return { dist, angle };
}

featuresEl.addEventListener('mouseover', (event) => {
  const target = event.target;
  const item = target.closest('.features__item');
  if (!item || item === currentOverEl) return;
  
  currentOverEl = item;
  showItemJoin();
});

featuresEl.addEventListener('mouseout', (event) => {
  const target = event.target;
  const item = target.closest('.features__item');
  if (!item) return;
  
  currentOverEl = null;
  hideItemJoin();
});

function showItemJoin() {
  const parrentRect = featuresEl.getBoundingClientRect();
  const itemRect = currentOverEl.getBoundingClientRect();
  const centerRect = centerEl.getBoundingClientRect();

  const { dist, angle } = getDistAngle(itemRect, centerRect);
  
  const offsetX = itemRect.x + itemRect.width * 0.5 - parrentRect.x;
  const offsetY = itemRect.y + itemRect.height * 0.5 - parrentRect.y - svgHeight * 0.5;
  
  featuresSvgEl.style.width = `${dist}px`;
  featuresSvgEl.style.transform = `translate(${offsetX}px, ${offsetY}px) rotate(${angle - 180}deg)`;
}

function hideItemJoin() {
  featuresSvgEl.style.width = `0px`;
}

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

This Pen doesn't use any external JavaScript resources.