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

* {
	margin: 0;
}


.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;
	position: relative;
	padding: 20px;

	svg {
		width: 100%;
		height: 100%;
		position: absolute;
		top: 0;
		left: 0;
		
		path {
			fill: transparent;
			
			&.simple {
				stroke-width: 1px;
				stroke: #ccc;
			}
			
			&.animated {
				stroke-width: 4px;
				stroke: red;
				stroke-dasharray: 1000 1000;
				stroke-dashoffset: 1000;
				
				&.grow {
					animation: pathGrow 2s ease-in-out forwards;
				}
			}
		}
	}
  div {
    width: 100%;
    padding: 10px;
		height: 60px;
    box-shadow:inset 0 0 0 2px #ccc;
    background: #fff;
    position: relative;
    z-index: 1;

    .anchor {
      display: block;
      width: 0px;
      height: 0px;
      top: 50%;
      position: absolute;


      &.anchor-right {
        right: 0;
      }

      &.anchor-left {
        left: 0;
      }
    }
  }

}


#test1 {
  grid-area: test1
}

#test2 {
  grid-area: test2
}

#test3 {
  grid-area: test3
}

#test4 {
  grid-area: test4
}

#test5 {
  grid-area: test5
}

#test6 {
  grid-area: test6;
}

@keyframes pathGrow {
	to {
		stroke-dashoffset: 0;
	}
}

JavaScript

// Collect all elements that need to be connected 
let test1 = document.querySelector("#test1");
let test2 = document.querySelector("#test2");
let test3 = document.querySelector("#test3");
let test4 = document.querySelector("#test4");
let test5 = document.querySelector("#test5");
let test6 = document.querySelector("#test6");

// get the element that needs to contain the SVG
const svgParent = document.getElementById("form");

// Define the SVG element
let svg = null;

// Get the grid-gap-column value, from css
const gap = parseInt(window
											.getComputedStyle(svgParent)
											.getPropertyValue("grid-column-gap")
										);
// set a constant value that will be used to adjust the bezier curve						
const gapControlOffset = 3.5;

// add a small straight line before the start and end of the bezier curve
let offset = gap / 10;


// Connect elements
connectElements(test1, test6);
connectElements(test2, test6);
connectElements(test3, test6);
connectElements(test4, test6);
connectElements(test5, test6); 

function connectElements(startElement, endElement) {
	// Create the SCG tag only if it doesn't exist and place all path inside a single SVG element
	if (!svg) {
		svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
		svgParent.prepend(svg);
	}
	
  // Get the positions of the start and end elements
  const startRect = startElement.querySelector(".anchor").getBoundingClientRect();
  const endRect = endElement.querySelector(".anchor").getBoundingClientRect();
  
  // Calculate the control points for the Bezier curve
  const startX = startRect.right;
  const startY = startRect.top + startRect.height / 2;
  const endX = endRect.left;
  const endY = endRect.top + endRect.height / 2;
  const control1X = startX + offset + gap / gapControlOffset;
  const control1Y = startY;
  const control2X = endX - offset - gap / gapControlOffset;
  const control2Y = endY;
	
	// Compose the path
	// M is the start point of the bezier curve
	// L is a straight...