gridstack

HTML

<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.0.0-beta/css/bootstrap.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/gridstack.js/0.3.0/gridstack.min.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.11.0/umd/popper.min.js"></script>
<script src="https://code.jquery.com/jquery-1.11.1.min.js"></script>
<script src="https://code.jquery.com/ui/1.11.0/jquery-ui.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jqueryui-touch-punch/0.2.3/jquery.ui.touch-punch.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.0.0-beta/js/bootstrap.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/gridstack.js/0.3.0/gridstack.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/gridstack.js/0.3.0/gridstack.jQueryUI.min.js"></script>
<body>
<div class="repeating-grid">
  <div class="container-fluid">


  <div class="grid-stack">
    <a class="btn btn-default" id="add-new-widget" href="#">Add Widget</a>
  </div>
</div>
</div>

</body>

SCSS

$charcoal: #343837;

$col-number: 24;
$col-size: 1/$col-number;

$col-padding: 0;

@for $i from 1 through $col-number {
  .grid-stack-item[data-gs-width="#{$i}"]  { 
    width: percentage($col-size*$i) !important;
  }
  
  .grid-stack-item[data-gs-x="#{$i}"]  { 
    left: percentage($col-size*$i) !important;
  }
}

body {
  background-color: $charcoal;
}

$minor-grid-size: 0px;
$minor-grid-color: rgba(120,120,120,0.22);
$minor-grid-weight: 1px;
$major-grid-size: $minor-grid-size * 4;
$major-grid-color: rgba(120,120,120,120.44);
$major-grid-weight: 2px;

.repeating-grid {
  position: absolute;
  top: 0; bottom: 0;
  left: 0; right: 0;
  background-size: $major-grid-size $major-grid-size;
  background-image:
    repeating-linear-gradient(0deg, $major-grid-color, $major-grid-color $major-grid-weight, transparent $major-grid-weight, transparent $major-grid-size),
    repeating-linear-gradient(-90deg, $major-grid-color, $major-grid-color $major-grid-weight, transparent $major-grid-weight, transparent $major-grid-size),
    repeating-linear-gradient(0deg, $minor-grid-color, $minor-grid-color $minor-grid-weight, transparent $minor-grid-weight, transparent $minor-grid-size),
    repeating-linear-gradient(-90deg, $minor-grid-color, $minor-grid-color $minor-grid-weight, transparent $minor-grid-weight, transparent $minor-grid-size);
}

.grid-stack-item-content {
  color: #2c3e50;
  border-radius: 7px !important;
  border-style: solid;
  border-width: 1px;
  border-color: black;
  text-align: center;
  background-color: #252525;
}

.grid-stack .grid-stack-placeholder > .placeholder-content {
  left: $col-padding;
  right: $col-padding;
}

.grid-stack > .grid-stack-item > .grid-stack-item-content {
  left: $col-padding;
  right: $col-padding;
}

JavaScript

$(function () {
		
    // Initialize gridstack.js
    var options = {
      float: true,
      animate: true,
      width: 24,
      cellHeight: null,
      verticalMargin: 0,
      alwaysShowResizeHandle: /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent)
    };
    
    $('.grid-stack').gridstack(options);
    
    (function () {
        this.grid = $('.grid-stack').data('gridstack');
        this.addNewWidget = function () {
            this.grid.addWidget($('<div><div class="grid-stack-item-content" /><div/>'),
                0, 0, 1, 1, true);
            return false;
        }.bind(this);
        $('#add-new-widget').click(this.addNewWidget);
    })();
    
    function generateRepeatingGridBackgroundImageCSS(majorGridSize, minorGridSize) {
   		var minorGridColor = "rgba(120,120,120,0.22)";
    	var minorGridWeight = "1px";
    	var majorGridColor = "rgba(120,120,120,120.44)";
    	var minorGridWeight = "2px";
    	return `repeating-linear-gradient(0deg, ${minorGridColor}, ${minorGridColor} ${minorGridWeight}, transparent ${minorGridWeight}, transparent ${majorGridSize}),
    repeating-linear-gradient(-90deg, ${minorGridColor}, ${minorGridColor} ${minorGridWeight}, transparent ${minorGridWeight}, transparent ${majorGridSize}),
    repeating-linear-gradient(0deg, ${minorGridColor}, ${minorGridColor} ${minorGridWeight}, transparent ${minorGridWeight}, transparent ${minorGridSize}),
    repeating-linear-gradient(-90deg, ${minorGridColor}, ${minorGridColor} ${minorGridWeight}, transparent ${minorGridWeight}, transparent ${minorGridSize})`;
    }
    
    function updateGridSize() {
    	var $grid = $('.grid-stack').data('gridstack');
      $grid.cellHeight($grid.cellWidth());
      var majorGridSize = ($grid.cellWidth()*4).toString().concat("px");
      var minorGridSize = ($grid.cellWidth()).toString().concat("px");
      $('.repeating-grid').css("background-size", `${majorGridSize} ${majorGridSize}`);
     ...