console.clear() d3.select('body').selectAppend('div.tooltip') var data = [ {y: 2013, t: 'h', s: 15}, {y: 2013, t: 'p', s: 14}, {y: 2013, t: 'd', s: 11}, {y: 2014, t: 'h', s: 24}, {y: 2014, t: 'p', s: 14}, {y: 2014, t: 'd', s: 11}, {y: 2015, t: 'h', s: 25}, {y: 2015, t: 'p', s: 16}, {y: 2015, t: 'd', s: 11}, {y: 2016, t: 'h', s: 25}, {y: 2016, t: 'p', s: 16}, {y: 2016, t: 'd', s: 11}, {y: 2017, t: 'h', s: 25.6}, {y: 2017, t: 'p', s: 15}, {y: 2017, t: 'd', s: 11}, ] var types = d3.nestBy(data, d => d.t) // 2014-2016 has georgia var sel = d3.select('#graph').html('') var c = d3.conventions({sel, margin: {left: 85, right: 30, bottom: 30}}) c.x.domain([2013, 2017]) c.y.domain([0, 30]) c.xAxis.ticks(3).tickFormat(d => d) c.yAxis.ticks(5).tickFormat(d => d + 'px') d3.drawAxis(c) var byYear = d3.nestBy(data, d => d.y) d3.selectAll('.x text') .st({fontSize: (d, i) => d3.mean(byYear[i], d => d.s) }) d3.selectAll('.y text') .st({fontSize: d => d}) var line = d3.line() .x(d => c.x(d.y)) .y(d => c.y(d.s)) c.svg.appendMany('path', types) .at({ stroke: '#000', fill: 'none', d: line }) c.svg.appendMany('circle', data) .at({ cx: d => c.x(d.y), cy: d => c.y(d.s), stroke: '#000', r: d => d.s/2, }) .call(d3.attachTooltip) types.forEach(d => d.last = _.last(d)) c.svg.appendMany('text', types) .text((d, i) => ['.g-headline', '.g-body', '.g-date'][i]) .translate(d => [c.x(d.last.y), c.y(d.last.s)]) .at({textAnchor: 'end', dy: '-1em'}) var annoPos = [c.x(2015), c.y(19)] c.svg.appendMany('path', types[1].slice(1, 4)) .at({ d: d => 'M' + annoPos + 'L' + [c.x(d.y), c.y(d.s)], stroke: '#000', strokeDasharray: '2 5' }) c.svg.append('rect') .translate(annoPos) .at({x: -70, width: 100, height: 7, fill: '#fff'}) c.svg.append('text').text('georgia') .translate(annoPos) .at({textAnchor: 'middle', fontWeight: 600})