JSFiddle - React, Tailwind, and code Playground

by Alexandru Gatea

HTML

<div class="form-container" id="form">
  <div id="test1"> <span class="anchor anchor-right"></span></div>
  <div id="test2"> <span class="anchor anchor-right"></span></div>
  <div id="test3"> <span class="anchor anchor-right"></span></div>
  <div id="test4"> <span class="anchor anchor-right"></span></div>
  <div id="test5"> <span class="anchor anchor-right"></span></div>
  <div id="test6"> <span class="anchor anchor-left"></span></div>
</div>

SCSS

.form-container {
  display: grid;
  grid-template-columns: 250px 100px;
  grid-row-gap: 10px;
  grid-column-gap: 150px;
  grid-template-areas:
    "test1 test6"
    "test2 test6"
    "test3 test6"
    "test4 test6"
    "test5 test6";
  width: 500px;
  align-items: center;

  div {
    width: 100%;
    padding: 2rem;
    box-shadow: inset 0 0 0 2px #ccc;
    background: #fff;
    position: relative;
    z-index: 1;
    border-radius: 100px;

    .anchor {
      display: block;
      width: 25px;
      height: 25px;
      border-radius: 25px;
      top: 50%;
      background: transparent;
      position: absolute;


      &.anchor-right {
        right: 0;
        transform: translate(50%, -50%);
      }

      &.anchor-left {
        left: 0;
        transform: translate(-50%, -50%);
      }
    }
  }

}

svg path {
  stroke-width: 2px;
  stroke: #ccc;
  fill: transparent;
}

#test1 {
  grid-area: test1
}

#test2 {
  grid-area: test2
}

#test3 {
  grid-area: test3
}

#test4 {
  grid-area: test4
}

#test5 {
  grid-area: test5
}

#test6 {
  grid-area: test6;
}

JavaScript

let test1 = document.querySelector("#test1 .anchor");
let test2 = document.querySelector("#test2 .anchor");
let test3 = document.querySelector("#test3 .anchor");
let test4 = document.querySelector("#test4 .anchor");
let test5 = document.querySelector("#test5 .anchor");
let test6 = document.querySelector("#test6 .anchor");

let svg = null;

const formContainer = document.getElementById("form");
const gap = parseInt(window
											.getComputedStyle(formContainer)
											.getPropertyValue("grid-column-gap")
										);
										
const gapControlOffset = 3.5;
createCurve(test1, test6);
createCurve(test2, test6);
createCurve(test3, test6);
createCurve(test4, test6);
createCurve(test5, test6); 


function createCurve(startElement, endElement) {

  if (!svg) {
    svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
    svg.setAttribute("width", "100%");
    svg.setAttribute("height", "100%");
    svg.style.position = "absolute";
    svg.style.top = "0";
    svg.style.left = "0";
    formContainer.appendChild(svg);
  }
	
  // Get the positions of the start and end elements
  const startRect = startElement.getBoundingClientRect();
  const endRect = endElement.getBoundingClientRect();
  
  // Calculate the control points for the Bezier curve
  const startX = startRect.left + startRect.width / 2;
  const startY = startRect.top + startRect.height / 2;
  const endX = endRect.left + endRect.width / 2;
  const endY = endRect.top + endRect.height / 2;
  const control1X = startX + gap / gapControlOffset;
  const control1Y = startY;
  const control2X = endX - gap / gapControlOffset;
  const control2Y = endY;

  // Create the SVG path element
  const path = document.createElementNS("http://www.w3.org/2000/svg", "path");
  path.setAttribute("d", `M${startX},${startY} C${control1X},${control1Y} ${control2X},${control2Y} ${endX},${endY}`);
 svg.appendChild(path);
}