const data = [2.3, 2.5, 3.4, 8.7, 7.8, 2.8, 2.2, 1.7];
// Create scale
const scale = d3.scaleLinear().domain([17, 20.5]).range([0, 400]);
const tickFormat = val => {
const year = Math.floor(val);
const quarter = val - year === 0.5 ? 4 : 2;
return `${quarter}Q ${year}`;
}
// Add scales to axis
const x_axis = d3.axisBottom().scale(scale).tickFormat(tickFormat);
const svg = d3.select('svg');
//Insert axis
svg.append("g").attr('transform', 'translate(50,100)').call(x_axis);
const step = 400 / (data.length - 1);
const valuesContainer = svg.append('g').attr('transform', 'translate(50,50)')
valuesContainer.selectAll('text.value')
.data(data, (d, i) => i)
.enter()
.append('text')
.classed('value', true)
.text(d => `${d}%`)
.attr('x', (d, i) => i * step)
.attr('text-anchor', 'middle')
.style('font-family', 'sans-serif')
.style('font-size', '12px');
const ranges = data.slice(0, data.length - 1)
.map((v, i) => ({from: v, to: data[i + 1]}));
const rangesContainer = svg.append('g')
.attr('transform', 'translate(50,60)')
const gradients = svg.selectAll('linearGradient')
.data(ranges, (d, i) => i)
.enter()
.append('linearGradient')
.attr('id', (d, i) => `grad-${i}`)
.attr('x1', '0%')
.attr('x2', '100%')
gradients.append('stop')
.attr('offset', '0%')
.attr('stop-color', '#008')
.attr('stop-opacity', d => d.from / 10);
gradients.append('stop')
.attr('offset', '100%')
.attr('stop-color', '#008')
.attr('stop-opacity', d => d.to / 10);
rangesContainer.selectAll('rect.range')
.data(ranges, (d, i) => i)
.enter()
.append('rect')
.classed('range', true)
.style('fill',(d, i) => `url(#grad-${i})`)
.attr('x', (d, i) => i * step)
.attr('width', step)
.attr('height', 30)
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<svg width="500" height="150">
</svg>