responsive matrix design - using string with 0 & 1 for dynamic visualization

This fiddle does automatically fit boxes inside a equilateral grid - you can adjust the number of boxes with changing the maxDots variable (just make sure to stay with 2x2, 3x3, ...). The grid reads a string with 1's and 0's and transform it accordingly to the grid with boxes being a 1 and dots being a 0. Works with all current Browser, IE10+ (because of flexbox).

by Ercan Cicek

HTML

<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.6.5/angular.min.js"></script>
<!-- using jquery -->
<!-- using angular -->
<body ng-app="symApp" ng-controller="symCtrl" class="flex-display flex-center">

    <div class="matrix-wrapper flex-display flex-center">
      <div class="matrix-container flex-display flex-spaceA flex-flow">
        <div id="dot-{{dot.id}}" ng-repeat="dot in dots" ng-class="dot.state ? '' : 'noBg'" class="dot dot-expanded flex-display flex-center">
          <div ng-class="dot.state ? 'noBg' : ''" class="dot-default"></div>
        </div>
      </div>
    </div>

</body>

CSS

html, body {
  position: relative;
  width: 100%;
  height: 100%;
}

.matrix-wrapper {
  width: 100%;
  height: 100%;
  position: relative;
}

.matrix-container {
  position: relative;
  width: 100%; /* placeholder */
  height: 100%; /* placeholder */
  border: 1px solid #69F0AE;
}

.dot-expanded {
  border-radius: 1px;
  background-color: #69F0AE;
}

.dot-default {
  width: 25%;
  height: 25%;
  border-radius: 50%;
  background-color: #69F0AE;
}

/* --- utilities --- */
.softHide {
  visibility: hidden;
}

.noBg {
  background-color: transparent;
}

.flex-display {
  display: -webkit-box;
  display: -ms-flexbox;
  display: -moz-flex;
  display: -webkit-flex;
  display: flex;
}
.flex-center {
  -webkit-box-align: center;
  -moz-box-align: center;
  -webkit-align-items: center;
  -ms-flex-align: center;
  align-items: center;
  -webkit-box-pack: center;
  -moz-box-pack: center;
  -webkit-justify-content: center;
  -ms-flex-pack: center;
  justify-content: center;
}
.flex-spaceA {
  -webkit-box-pack: justify;
  -webkit-justify-content: space-around;
  -ms-flex-pack: distribute;
  justify-content: space-around;
  -webkit-align-content: space-around;
  align-content: space-around;
}
.flex-flow {
  -webkit-flex-flow: row wrap;
  -ms-flex-flow: row wrap;
  flex-flow: row wrap;
}

JavaScript

angular.module('symApp',[]).controller('symCtrl', ['$scope', '$timeout', function($scope, $timeout) {

		// --- vars
    var maxDots = 64; // --- has to be 2*2, 3*3, ...

		// --- angular vars
    $scope.dots = [];
    // ------ fill $scope.dots with default values
    for(var i = 1; i <= maxDots; i++) {
    	$scope.dots.push({ id: i, state: false });
    }

		// --- functions
    $(init);
    
    function init() {
    // --- maxboxsize 52%, margin 40% (defined by me ;))
    var dotSize = 52 / Math.sqrt(maxDots),
    		dotMargin = 40 / (Math.sqrt(maxDots) * 2);
    // --- set dot sizes suitable for maxDots
    $("body").append("<style type='text/css'>.dot { width: " + dotSize + "%; height: " + dotSize + "%; margin-left: " + dotMargin + "%; margin-right: " + dotMargin + "%; }</style>");
    	// --- responsivity
      $(window).resize(setElements);
      setElements();
      // --- 
      $timeout(getMatrix);
    }

    function getMatrix() {
    	// --- generate string with 0 & 1 randomly with count equally to maxDots - every 1 will be shown as expanded dot
      var testItem = "";
    	for(var i = 0; i < maxDots; i++) {
      	testItem += getRandomInt(0,1);
      }
      for(var j = 0; j < maxDots; j++) {
      	$scope.dots[j].state = (testItem[j] === '1');
      }
      console.log($scope.dots);
    }

    function setElements() {
      var $gaCont = $(".matrix-wrapper"),
          wndw = { w: $gaCont.width(), h: $gaCont.height() },
          distSize = isPortrait() ? wndw.w * 0.75 : wndw.h * 0.75;
      $(".matrix-container").css({ width: distSize + "px", height: distSize + "px" });
    }
    
    function getRandomInt(min, max) {
    	return Math.floor(Math.random() * (max - min + 1)) + min;
    }

    function isPortrait() {
      return window.matchMedia("(orientation: portrait)").matches;
    }

}]);