Interactive Charts with ECharts and D3

Embedding interactive data visualisations using Apache ECharts and D3 — declarative markup, no JavaScript required in the post.

Interactive Charts with ECharts and D3

Two charting libraries are available out of the box: Apache ECharts for high-level declarative charts, and D3 for fully custom visualisations. Both are loaded on demand — only when [data-viz] or [data-d3] elements appear in the post.


ECharts

Use data-viz="echarts" with a data-options JSON attribute to embed any ECharts chart. The full ECharts option schema is supported.

Bar chart

Line chart

How it works

The renderEChart adapter:

  1. Registers the loom theme (palette + text styles matching the site’s CSS variables)
  2. Calls echarts.init(el, 'loom')
  3. Attaches a ResizeObserver so charts resize with their container
  4. Returns the chart instance for potential scrolly updates

The full ECharts options object is passed directly to chart.setOption(), so anything from the ECharts docs works without any wrapper configuration.


D3

Use data-d3="type" with a data-options JSON attribute containing a data array.

Built-in: bar chart

Built-in: line chart

Built-in: force graph

The force graph above shows the dependency structure of the theme’s visualization system.

Adding custom chart types

Register a new D3 chart type from a post HTML block or a separate JS file. The factory receives the container element, the data array, and the remaining options, and must return an object with an update(newData) method.

import { registerD3Chart } from '/assets/js/viz/d3.js';

registerD3Chart('scatter', (el, data, options) => {
  const d3 = window.d3;
  const { width, height } = el.getBoundingClientRect();

  const svg = d3.select(el)
    .append('svg')
    .attr('width', width).attr('height', height);

  // … draw circles …

  return {
    update(newData) {
      // re-render with newData
    }
  };
});

Then use it in the post:

<div data-d3="scatter" style="height:320px"
     data-options='{"data":[{"x":1,"y":2},{"x":3,"y":4}]}'></div>

The factory pattern means each chart type is entirely self-contained — it owns its own scales, axes, and update logic. The registry just dispatches to the right factory at render time, and stores the returned instance for scrolly updates.