/**
 * I started with Scott Murray's tutorial, here: http://alignedleft.com/tutorials/d3/making-a-bar-chart
 * Then I introduced es6, pulled out some common functions, added scales and color scales.
 * To get good contrasting colors for the labels, used some methods from tinycolor.
 * I walk through the changes on my blog: http://billkidwell.com/post/2016-07-04-some-fun-with-d3-scales/
 */

// The canvas represents the entire SVG
var canvas = { w: 700, h: 250 };
// Chart area is where the primary chart graphic appears
var chart = { w: 500, h: 100, x: 100, y: 100 };

var barPadding = 5;
	
var dataset = [ 5, 10, 13, 19, 21, 25, 22, 18, 15, 13,
							11, 12, 15, 20, 25, 17, 16, 18, 23, 25 ];

var x = d3.scale.ordinal()
          .rangeBands([0, chart.w], .1);

var y = d3.scale.linear()
          .range([chart.h, 0]);

var blueScale = d3.scale.linear()
                    .domain([d3.min(dataset), d3.max(dataset)])
                    .range(["lightblue", "darkblue"]);

var inverseBlue = d3.scale.linear()
                    .domain([d3.max(dataset), d3.min(dataset)])
                    .range(["lightblue", "darkblue"]);

function contrastingColor(d) {
  var barColor = d3.rgb(blueScale(d));
  var color = d3.rgb(inverseBlue(d)); // start with the color from the inverse scale
  if (isDark(barColor)) return color.brighter(2);
  else return color.darker(2);
}

x.domain(dataset.map( (d) => d ));
y.domain([0, d3.max(dataset)]);

console.log(d3.max(dataset));

var labelFont = {
  family: 'sans-serif',
  size: '11px'
}

function font(selection, fontInfo) {
  selection.attr('font-family', fontInfo.family)
           .attr('font-size', fontInfo.size)
  return selection;
}

function fill(selection, fillColor) {
  selection.attr("fill", fillColor);
  return selection;
}

// Thanks to tinycolor
function getBrightness(color) {
  //http://www.w3.org/TR/AERT#color-contrast
  var rgb = d3.rgb(color)
  return (rgb.r * 299 + rgb.g * 587 + rgb.b * 114) / 1000;
}

// Thanks to tinycolor
function isDark(color) {
  return getBrightness(color) < 128;
}

// Thanks to tinycolor
function isLight(color) {
  return !isDark(color);
}
			
//Create SVG element
var svg = d3.select("body")
						.append("svg")
						.attr("width", canvas.w)
						.attr("height", canvas.h);

var group = svg.append("g")
                .attr("transform", "translate(" + chart.x + "," + chart.y + ")");
                

group.selectAll("rect")
   .data(dataset)
   .enter()
   .append("rect")
   .attr("x", (d) => x(d))
   .attr("y", (d) => y(d))
   .attr("width", x.rangeBand())
   .attr("height", (d) => chart.h - y(d) )
   .call(fill, (d) => blueScale(d));

group.selectAll("text")
   .data(dataset)
   .enter()
     .append("text")
     .text((d) => d )
     .attr("x", (d, i) => x(d) + 0.5*x.rangeBand() ) // center of range
     .attr("text-anchor", "middle") // bottom middle is anchor
     .attr("y", (d) => y(d) + 15 )
     .call(font, labelFont)
     .call(fill, (d)=> contrastingColor(d));

View Compiled

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

  1. https://d3js.org/d3.v3.min.js