Every chart on this site is hand-written SVG. No plotting library, not even for the
three-dimensional one. That was not a principled decision at the start and it has turned out to
be the right one for a reason I did not anticipate: content has to be in the initial HTML for
crawlers that do not run JavaScript, and a server-rendered <svg> satisfies that for free,
where a canvas-based library actively fights it.
But the interesting part is not the technology. It is a rule about resolution that took me embarrassingly long to state.
The rule
Draw at the resolution the sample supports. Both halves of that matter, and they pull in opposite directions.
Bin a distribution too coarsely and you invent shape — a bimodality that is two bins where three would have shown one hump. This is the well-known failure and everybody guards against it.
The other direction is the one that gets missed. A facet score has seventeen possible values and a norm cell for one age band holds about eight hundred people, which is roughly forty-eight per value: fine. A domain score has ninety-seven possible values, and the same eight hundred people spread over ninety-seven points is about eleven per point, carrying something like 31% Poisson noise.
Drawn raw, that is a jagged spike field. And every spike in it is sampling noise rendered as though it were structure — while looking scrupulously honest, because nothing was smoothed.
Refusing to bin is not automatically the careful choice. It is a choice, with the same failure mode, wearing better clothes.
Two other rules that came from the same place
A chart that does not declare its source does not compile. The chart wrapper takes the population it describes as a required argument. This is not tidiness: the site shows figures from Johnson's reference sample and figures from our own respondents on the same pages, and those are different samples, collected a decade apart, by different means. Rendered side by side without labels they read as one population. Making the label a required parameter means it cannot be forgotten in a hurry.
Every chart ships a table underneath it. Partly that is the accessibility answer. But it is also the crawler answer, and increasingly the more important one: an SVG polyline carries no meaning to a language model reading the page, and a chart whose numbers appear nowhere else is invisible to exactly the audience this project most wants to reach. The table is not a fallback. It is the primary representation, and the picture is the convenience.
And one about text
Text never goes inside a scaled SVG. A viewBox scales its whole coordinate system, so a
9-pixel label on a 620-wide chart reaches a phone at about five pixels, and there is no
attribute that fixes it — vector-effect governs strokes, not glyphs, and a CSS font size
inside a scaled system scales with everything else. Geometry goes in the SVG; every label is
real HTML beside it. Which is also why the labels are selectable, translatable, and read in
order by a screen reader.