JSFiddle - React, Tailwind, and code Playground

by Alexandru Gatea

HTML

<div id="svgContainer">
  <svg id="svg1" width="0" height="0">
    <defs>
      <style>
        path {
          stroke: #000;
          stroke-width: 4px;
          fill: none;
        }

      </style>
    </defs>
    <path id="path1" d="M0 0" stroke="black" stroke-width="4px"/>
    <path id="path2" d="M0 0" stroke="black" stroke-width="4px"/>
    <path id="path3" d="M0 0" stroke="black" stroke-width="4px"/>
    <path id="path4" d="M0 0" stroke="black" stroke-width="4px"/>
  </svg>
</div>
<div class="container">
  <div class="connect-these">
    <div id="res"></div>
    <div id="act"></div>
    <div id="mkt"></div>
    <div id="prod"></div>
  </div>
  <div class="to-this">
    <div id="output"></div>
  </div>
</div>

SCSS

.container {
  display: flex;
  height: 100vh;
  flex-direction: column;
  align-items: stretch;
  justify-content: space-between;

  .connect-these {
    display: flex;
    width: 420px;
    flex-wrap: wrap;
}

  .connect-these div {
    width: 100px;
    height: 100px;
    border: 4px solid black;
    margin: 20px;
    background: white;
    border-radius: 10px;
    
    &:nth-child(3) {
      margin-left: 100px;
    }
  }

  .to-this {
      display: flex;
      align-items: flex-end;
      justify-content: flex-end;
      
    div {
      width: 100px;
      height: 100px;
      border: 1px solid black;
      margin-right: 100px;
    }
  }
}

#svgContainer { 
	z-index: -10;
	position:absolute;
  top:0; left:0;
}

JavaScript

//helper functions, it turned out chrome doesn't support Math.sgn() 
function signum(x) {
    return (x < 0) ? -1 : 1;
}
function absolute(x) {
    return (x < 0) ? -x : x;
}

function drawPath(svg, path, startX, startY, endX, endY) {
    // get the path's stroke width (if one wanted to be  really precize, one could use half the stroke size)
    var stroke =  parseFloat(path.attr("stroke-width"));
    // check if the svg is big enough to draw the path, if not, set heigh/width
    if (svg.attr("height") <  endY)                 svg.attr("height", endY);
    if (svg.attr("width" ) < (startX + stroke) )    svg.attr("width", (startX + stroke));
    if (svg.attr("width" ) < (endX   + stroke) )    svg.attr("width", (endX   + stroke));
    
    var deltaX = (endX - startX) * 0.45;
    var deltaY = (endY - startY) * 0.25;
    // for further calculations which ever is the shortest distance
    var delta  =  deltaY < absolute(deltaX) ? deltaY : absolute(deltaX);

    // set sweep-flag (counter/clock-wise)
    // if start element is closer to the left edge,
    // draw the first arc counter-clockwise, and the second one clock-wise
    var arc1 = 0; var arc2 = 1;
    if (startX > endX) {
        arc1 = 1;
        arc2 = 0;
    }
    // draw tha pipe-like path
    // 1. move a bit down, 2. arch,  3. move a bit to the right, 4.arch, 5. move down to the end 
    path.attr("d",  "M"  + startX + " " + startY +
                    " V" + (startY + delta) +
                    " A" + delta + " " +  delta + " 0 0 " + arc1 + " " + (startX + delta*signum(deltaX)) + " " + (startY + 2*delta) +
                    " H" + (endX - delta*signum(deltaX)) + 
                    " A" + delta + " " +  delta + " 0 0 " + arc2 + " " + endX + " " + (startY + 3*delta) +
                    " V" + endY );
}

function connectElements(svg, path, startElem, endElem) {
    var svgContainer= $("#svgContainer");

    // if first element is lower than the second, swap!
    if(startElem.offset().top >...