<h2 class="bx--graph-header">Grouped Bar Graph</h2>
<div class="graph-container">
	<svg></svg>
	<div class="tooltip"></div>
</div>
body {
	padding: 1rem 3%;
}

.bx--graph-header {
	font-weight: 300;
	font-size: 24px;
}

.line {
	stroke-width: 2;
	stroke: #FF00FF;
	fill: none;
	pointer-events: none;
}

.axis path {
	stroke: #5A6872;
}

.tick line {
	stroke: #5A6872;
}

.tick text {
	fill: #5A6872;
}

.graph-container {
	position: relative;
}

.tooltip {
	font-weight: 700;
	padding-left: 1rem 2rem;
	background-color: #fff;
	position: absolute;
	box-shadow: 0 4px 8px 0 rgba(0, 0, 0, .1); 
	border: 1px solid #DFE3E6;
	padding: .25rem .5rem;
	pointer-events: none;
	display: none;
	 
	&:after {
		content: '';
		top: 100%;
		left: 50%;
		transform: translateX(-50%);
		position: absolute;
		width: 0; 
		height: 0; 
		border-left: 5px solid transparent;
		border-right: 5px solid transparent;
		border-top: 5px solid #fff;
	}
}

.y path {
	display: none;
}

.label {
	font-size: 10px;
	font-weight: 700;
	fill: #5A6872;
	text-anchor: middle;
}
View Compiled
function createData(num) {
	let data = [];

	for (var i = 0; i < num; i++) {
		let numArr = [];
		const one = Math.floor(Math.random() * 1000 + 10);
		const two = Math.floor(Math.random() * 1000 + 10);
		const three = Math.floor(Math.random() * 1000 + 10);
		const four = Math.floor(Math.random() * 1000 + 10);
		const five = Math.floor(Math.random() * 1000 + 10);
		numArr.push(one, two, three, four, five);
		let d = new Date();
    d = d.setDate(d.getDate() + i * 30);
		const entry = [numArr, d];
		data.push(entry);
	}
	
	return data;
}

// Create + Format data
let data = createData(4).sort(function(a, b) { return a.date - b.date; });

const colors = d3.scaleOrdinal()
    .range(['#00A78F', '#3b1a40', '#473793', '#3c6df0', '#56D2BB']);

// what are these and are they things that someone should edit
const margin = { top: 30, right: 20, bottom: 60, left: 65 };
const width = 800 - (margin.left + margin.right);
const height = 300 - (margin.top + margin.bottom);
const labelOffset = 50;
const axisOffset = 16;

// Set the scales
// Groups scale, x axis

// Group Scale
const x0 = d3.scaleBand()
    .rangeRound([0, width])
		.domain(data.map(d => d[1]))
		.paddingInner(0.3);

// const numGroups = data
const x1 = d3.scaleBand()
	.rangeRound([0, x0.bandwidth()])
	.domain(d3.range(data[0][0].length))
	.padding(0.05);

const y = d3.scaleLinear()
	.range([height, 0])
	.domain([0, d3.max(data, (d) => {
		return d3.max(d[0], (i) => {
			return i
		})
	})]);

// // Set the axes
const xAxis = d3.axisBottom()
	.scale(x0)
	.tickSize(0)
	.tickFormat(d3.timeFormat('%b'))

const yAxis = d3.axisLeft()
	.ticks(4) 
	.tickSize(-width)
	.scale(y.nice());

// // Set up SVG with initial transform to avoid repeat positioning
const svg = d3.select('svg')
		.attr('class', 'graph')
		.attr('width', width + (margin.left + margin.right))
		.attr('height', height + (margin.top + margin.bottom))
		.append('g')
		.attr('class', 'group-container')
		.attr('transform', `translate(${margin.left}, ${margin.top})`)
		.attr('font-family', 'ibm-plex-sans');

// // Add Y axis
svg.append('g')
	.attr('class', 'axis y')
	.attr('stroke-dasharray', '4')
	.call(yAxis)
	.selectAll('text')
	.attr("x", -axisOffset)
	.attr('font-family', 'ibm-plex-sans');

// // Add Y axis label
const yLabel = svg.select('.y')
	.append('text')
	.text('USAGE ($)')
	.attr('class', 'label')
	.attr('transform', `translate(${-labelOffset}, ${height / 2}) rotate(-90)`)
	.attr('font-family', 'ibm-plex-sans');

// // Add X axis
svg.append('g')
	.attr('class', 'axis x')
	.attr('transform', `translate(0, ${height})`)
	.call(xAxis)
	.selectAll('text')
	.attr("y", axisOffset)
	.attr('font-family', 'ibm-plex-sans');

// // Add X axis label
const xLabel = svg.select('.x')
	.append('text')
	.text('MONTH')
	.attr('class', 'label')
	.attr('transform', `translate(${width / 2}, ${labelOffset})`)
	.attr('font-family', 'ibm-plex-sans');

let count = 0;
svg.append('g')
	.selectAll('g')
	.data(data)
	.enter().append('g')
		.attr('transform', (d) => {
			return `translate(${x0(d[1])}, 0)`
	})
	.selectAll('rect')
	.data((d) => {
		count++;
		return d[0].map((key, index) => {
			return {
				key,
				index,
				series: count
			}
		})
	})
	.enter().append('rect')
	.attr('class', 'bar')
	.attr('x', (d) => x1(d.index))
	.attr('y', (d) => y(d.key))
	.attr('width', x1.bandwidth())
	.attr('height', (d) => height - y(d.key))
	.attr('fill', (d) => colors(d.index));
View Compiled

External CSS

  1. https://unpkg.com/carbon-components@latest/css/carbon-components.css

External JavaScript

  1. https://unpkg.com/carbon-components@latest/scripts/carbon-components.js
  2. https://cdnjs.cloudflare.com/ajax/libs/d3/4.11.0/d3.min.js