<!DOCTYPE html> <head> <meta charset="utf-8"> <script src="https://d3js.org/d3.v4.min.js"></script> <style> body { font-family: "avenir next", Arial, sans-serif; font-size: 12px; color: #696969; margin: 0; } .text { margin-right: 50px; font-size: 14px; } article { width: 960px; display: flex; align-items: center; } </style> </head> <body> <article> <div id="svg-grid"></div> <div class="text">Here is some explanatory text. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Ut id lacus quis mauris auctor euismod. Pellentesque consectetur turpis in lorem laoreet, at porta metus aliquam. Etiam eget purus et dolor auctor volutpat. Nulla vitae nibh tempor, sollicitudin eros et, gravida enim. Duis tempor, nulla quis convallis venenatis, turpis erat molestie nibh, quis aliquet nibh ex in libero. Praesent interdum ipsum eu felis pellentesque, vel imperdiet neque ullamcorper.</div> </article> <script> let margin = {top:50, right:50, bottom:50, left:50}, width = 550 - margin.left - margin.right, height = 500 - margin.top - margin.bottom; // svg container let svg = d3.select("#svg-grid") .append("svg") .attr("width", width + margin.left + margin.right) .attr("height", height + margin.top + margin.bottom) .append("g") .attr("transform", "translate(" + margin.left + "," + margin.top + ")"); let numIcons = 84; let numAcross = 10; let iconPadding = 5; let squareWidth = Math.floor(width/numAcross); let iconWidth = squareWidth - iconPadding*2; // define icon and store it in svg <defs> elements as a reusable component svg.append("defs") .append("g") .attr("id", "iconCustom") .append("path") .attr("d","M9.14.86H8.86A.86.86,0,0,0,8,0H.86A.86.86,0,0,0,0,.86V8a.86.86,0,0,0,.86.86H9.14A.86.86,0,0,0,10,8V1.71A.86.86,0,0,0,9.14.86Zm0,7.57A.42.42,0,0,1,8.71,8V1.29h.43a.42.42,0,0,1,.43.43V8A.42.42,0,0,1,9.14,8.43Zm-.57,0H.86A.42.42,0,0,1,.43,8V.86A.42.42,0,0,1,.86.43H8a.42.42,0,0,1,.43.43V8A.71.71,0,0,0,8.57,8.43ZM1.14,4.14H4V1.29H1.14Zm.29-2.57H3.71V3.86H1.43ZM1.14,4.86H4v.29H1.14Zm0,.71H4v.29H1.14Zm0,.71H4v.29H1.14Zm0,.71H4v.29H1.14ZM4.86,5.57H7.71v.29H4.86Zm0,.71H7.71v.29H4.86Zm0,.71H7.71v.29H4.86Zm0-5.71H7.71v.29H4.86Zm0,.71H7.71v.29H4.86Zm0,.71H7.71V3H4.86Zm0,.71H7.71v.29H4.86Zm0,.71H7.71v.29H4.86Zm0,.71H7.71v.29H4.86Z") .attr("transform", "scale(" + iconWidth / 10 + ")"); // draw icons drawIcons(numIcons, numAcross, iconWidth, iconPadding, svg); function drawIcons(count, row, width, padding, canvas) { // count: number of icons // nAcross: number of icons in each row // width: width of icon // padding: padding around each icon // canvas: canvas element to draw on let data = d3.range(count); // compute dimensions let nAcross = row; let nDown = Math.ceil(count/nAcross); let squareWidth = width + padding*2; console.log(squareWidth); // width and height of icon area let w = nAcross * squareWidth; let h = nDown * squareWidth; // clear previously drawn chart d3.select("#icons").remove(); // append group with specfied dimensions let area = canvas.append("g") .attr("id", "icons") .attr("width", w) .attr("height", h); // draw icons area.selectAll("use") .data(data) .enter() .append("use") .attr("xlink:href", "#iconCustom") .attr("id", function(d) { return "icon"+d; }) .attr("x", function(d) { return d%nAcross * squareWidth; }) .attr("y", function(d) { return Math.floor(d/nAcross) * squareWidth; }) .style("fill", function(d) { if (d > 12) { return "#1c255c"; } else { return "#ff2c50"; } }) } </script> </body>