Responsive Bar Chart
This example shows how to make a d3-blueprint chart resize to fit its container using the responsivePlugin. The plugin wraps a ResizeObserver so you don't need to manage resize listeners yourself. Axes and bars are handled by reusable AxisChart and BarsChart attachments.
Live Preview
Full Source
js
import { select } from 'd3-selection';
import { D3Blueprint } from 'd3-blueprint';
import { scaleBand, scaleLinear } from 'd3-scale';
import { max } from 'd3-array';
import { AxisChart } from './charts/AxisChart.js';
import { BarsChart } from './charts/BarsChart.js';
import { tooltipPlugin } from './plugins/Tooltip.js';
class ResponsiveBarChart extends D3Blueprint {
initialize() {
this.configDefine('width', { defaultValue: 600 });
this.configDefine('height', { defaultValue: 300 });
this.configDefine('margin', {
defaultValue: { top: 20, right: 20, bottom: 30, left: 40 },
});
const margin = this.config('margin');
this.chart = this.base
.append('g')
.attr('transform', `translate(${margin.left},${margin.top})`);
this.attach('axes', AxisChart, this.chart);
this.attach('bars', BarsChart, this.chart.append('g').classed('bars', true));
this.use(tooltipPlugin(this.chart, (chart, tooltip) => {
chart.attached.bars.base.selectAll('rect')
.on('mouseenter', function (event, d) {
select(this).attr('opacity', 0.8);
tooltip.show(
chart.xScale(d.label) + chart.xScale.bandwidth(),
chart.yScale(d.value),
`${d.label}: ${d.value}`,
);
})
.on('mouseleave', function () {
select(this).attr('opacity', 1);
tooltip.hide();
});
}));
}
preDraw(data) {
const width = this.config('width');
const height = this.config('height');
const margin = this.config('margin');
const innerWidth = width - margin.left - margin.right;
const innerHeight = height - margin.top - margin.bottom;
// Update the SVG and viewBox to match the new width
this.base.attr('width', width).attr('viewBox', `0 0 ${width} ${height}`);
this.xScale = scaleBand()
.domain(data.map((d) => d.label))
.range([0, innerWidth])
.padding(0.1);
this.yScale = scaleLinear()
.domain([0, max(data, (d) => d.value) ?? 0])
.range([innerHeight, 0]);
this.attached.axes.config({ xScale: this.xScale, yScale: this.yScale, innerWidth, innerHeight, duration: 400 });
this.attached.bars.config({ xScale: this.xScale, yScale: this.yScale, innerHeight, fill: 'steelblue', duration: 400, rx: 2 });
}
}Making It Responsive
The chart class itself knows nothing about the DOM container. It just reads its width config. The responsive wiring is handled by the responsivePlugin:
js
import { select } from 'd3-selection';
import { responsivePlugin } from './plugins/responsivePlugin.js';
const container = document.querySelector('#chart');
const svg = select(container).append('svg').attr('height', 300);
const chart = new ResponsiveBarChart(svg);
chart.config('width', container.clientWidth);
chart.use(responsivePlugin({
container,
getSize: (el) => ({ width: el.clientWidth }),
}));
chart.draw(data);How It Works
configDefine('width', ...)makes width a runtime-configurable property.preDraw()readsthis.config('width')each timedraw()is called and updates the SVGwidthandviewBoxattributes so the chart fills the available space.responsivePluginwraps aResizeObserverthat watches the container element. On resize it callsgetSize()to compute the new config values, applies them viachart.config(), and redraws with the last dataset. Bars smoothly animate to their new positions.- AxisChart and BarsChart receive updated scales via
config()and re-render automatically as part of thedrawAttachmentslifecycle step.
This pattern keeps the chart class reusable: it doesn't depend on window, ResizeObserver, or any specific container. The responsive behavior is composed via a plugin.