← Typeface

type-01-typeface.C

Life expectancy at birth by country12 countries vs. world baseline of 73.48 years60 years70 years80 yearsbaseline 73.48Japan84.04 (+10.56)France82.98 (+9.50)Germany80.79 (+7.31)United States78.89 (+5.41)China78.02 (+4.54)Brazil76.02 (+2.54)Mexico75.26 (+1.78)India72.23 (-1.25)Egypt, Arab Rep.71.81 (-1.67)Indonesia71.29 (-2.19)South Africa66.31 (-7.17)Nigeria54.63 (-18.85)Data source: https://api.worldbank.org/v2/country/USA;JPN;DEU;BRA;IND;NGA;CHN;MEX;ZAF;FRA;IDN;EGY/indi...
Arm
C · with a tool
Method
reference-PNG + Chrome screenshot diff
Ran on
type-01-typeface.A
Mode
refine — the treated arms ran on the default arm's output, so the only difference is the lever
Fixture
table12
Runs
1 · shipped: only
Generated
2026-07-26

Verbatim prompt

Here is a chart module, and a reference image at this path:

  C:/Users/dusti/AppData/Local/Temp/claude/c--Users-dusti-brain/cee7adcd-243e-4488-bf2e-98015f28d074/scratchpad/directed-gen/refs/owid-life-expectancy.png

The reference is an Our World in Data chart of life expectancy, published under
CC BY. It is a generation target only.

Implement this design. Take a screenshot of the result and compare it to the
original. List differences and fix them.

export const meta = { fixture: "table12", width: 640, height: 400 };

export function render(svg, data) {
  const doc = svg.ownerDocument;
  const svgNS = "http://www.w3.org/2000/svg";
  const { width, height } = meta;

  while (svg.firstChild) svg.removeChild(svg.firstChild);

  svg.setAttribute("viewBox", `0 0 ${width} ${height}`);
  svg.setAttribute("width", String(width));
  svg.setAttribute("height", String(height));
  svg.setAttribute(
    "font-family",
    "system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif"
  );

  function el(tag, attrs, text) {
    const node = doc.createElementNS(svgNS, tag);
    for (const key in attrs) node.setAttribute(key, attrs[key]);
    if (text != null) node.textContent = text;
    return node;
  }

  svg.appendChild(el("rect", { x: 0, y: 0, width, height, fill: "#ffffff" }));

  const rows = data.rows.slice().sort((a, b) => b.value - a.value);
  const baseline = data.baseline;

  const unitMatch = /^(.*?)\s*\(([^)]+)\)\s*$/.exec(data.unit || "");
  const axisUnit = unitMatch ? unitMatch[1].trim() : data.unit || "";
  const titleLabel = unitMatch
    ? unitMatch[2].charAt(0).toUpperCase() + unitMatch[2].slice(1)
    : data.unit || "Value";

  const margin = { top: 64, right: 92, bottom: 36, left: 136 };
  const innerWidth = width - margin.left - margin.right;
  const innerHeight = height - margin.top - margin.bottom;
  const rowHeight = innerHeight / rows.length;
  const barHeight = Math.min(18, rowHeight * 0.62);

  const allValues = rows.map((r) => r.value).concat([baseline]);
  const rawMin = Math.min.apply(null, allValues);
  const rawMax = Math.max.apply(null, allValues);
  const pad = Math.max((rawMax - rawMin) * 0.1, 1);
  const domainMin = rawMin - pad;
  const domainMax = rawMax + pad;
  const span = domainMax - domainMin || 1;

  function x(v) {
    return ((v - domainMin) / span) * innerWidth;
  }

  // Title + subtitle
  svg.appendChild(
    el(
      "text",
      { x: margin.left, y: 22, "font-size": 17, "font-weight": 700, fill: "#1a1a1a" },
      `${titleLabel} by country`
    )
  );
  svg.appendChild(
    el(
      "text",
      { x: margin.left, y: 40, "font-size": 12.5, fill: "#666666" },
      `${rows.length} countries vs. world baseline of ${baseline.toFixed(2)} ${axisUnit}`
    )
  );

  // Legend (top right, independent of title length)
  const above = "#2a9d8f";
  const below = "#e76f51";
  svg.appendChild(el("rect", { x: 494, y: 10, width: 8, height: 8, fill: above, rx: 2 }));
  svg.appendChild(
    el("text", { x: 506, y: 17, "font-size": 10, fill: "#444444" }, "Above baseline")
  );
  svg.appendChild(el("rect", { x: 494, y: 24, width: 8, height: 8, fill: below, rx: 2 }));
  svg.appendChild(
    el("text", { x: 506, y: 31, "font-size": 10, fill: "#444444" }, "Below baseline")
  );

  const plot = el("g", { transform: `translate(${margin.left}, ${margin.top})` });
  svg.appendChild(plot);

  // Gridlines + x-axis ticks (nice step)
  const tickCount = 5;
  const rawStep = span / (tickCount - 1);
  const magnitude = Math.pow(10, Math.floor(Math.log10(rawStep)));
  const niceMultiples = [1, 2, 5, 10];
  let step = magnitude;
  for (let i = 0; i < niceMultiples.length; i++) {
    if (rawStep <= niceMultiples[i] * magnitude) {
      step = niceMultiples[i] * magnitude;
      break;
    }
  }
  const tickStart = Math.ceil(domainMin / step) * step;
  const ticks = [];
  for (let t = tickStart; t <= domainMax + 1e-9; t += step) {
    ticks.push(Math.round(t * 100) / 100);
  }

  ticks.forEach((t) => {
    const tx = x(t);
    plot.appendChild(
      el("line", {
        x1: tx,
        x2: tx,
        y1: 0,
        y2: innerHeight,
        stroke: "#e8e8e8",
        "stroke-width": 1,
      })
    );
    plot.appendChild(
      el(
        "text",
        {
          x: tx,
          y: innerHeight + 18,
          "font-size": 10.5,
          fill: "#888888",
          "text-anchor": "middle",
        },
        String(t)
      )
    );
  });

  plot.appendChild(
    el(
      "text",
      {
        x: innerWidth,
        y: innerHeight + 32,
        "font-size": 10.5,
        fill: "#888888",
        "text-anchor": "end",
      },
      axisUnit
    )
  );

  plot.appendChild(
    el("line", {
      x1: 0,
      x2: innerWidth,
      y1: innerHeight,
      y2: innerHeight,
      stroke: "#cccccc",
      "stroke-width": 1,
    })
  );

  // Baseline reference line
  const baseX = x(baseline);
  plot.appendChild(
    el("line", {
      x1: baseX,
      x2: baseX,
      y1: -4,
      y2: innerHeight,
      stroke: "#999999",
      "stroke-width": 1.25,
      "stroke-dasharray": "4,3",
    })
  );
  plot.appendChild(
    el(
      "text",
      {
        x: baseX,
        y: -8,
        "font-size": 10.5,
        fill: "#666666",
        "text-anchor": "middle",
      },
      `baseline ${baseline.toFixed(2)}`
    )
  );

  // Bars
  rows.forEach((row, i) => {
    const rowY = i * rowHeight + (rowHeight - barHeight) / 2;
    const isAbove = row.value >= baseline;
    const valueX = x(row.value);
    const barX = Math.min(baseX, valueX);
    const barW = Math.max(Math.abs(valueX - baseX), 1);
    const color = isAbove ? above : below;

    plot.appendChild(
      el("rect", {
        x: barX,
        y: rowY,
        width: barW,
        height: barHeight,
        fill: color,
        rx: 2,
      })
    );

    plot.appendChild(
      el(
        "text",
        {
          x: -10,
          y: rowY + barHeight / 2 + 4,
          "font-size": 11.5,
          fill: "#333333",
          "text-anchor": "end",
        },
        row.label
      )
    );

    const deltaSign = row.delta > 0 ? "+" : "";
    const labelX = isAbove ? valueX + 6 : valueX - 6;
    plot.appendChild(
      el(
        "text",
        {
          x: labelX,
          y: rowY + barHeight / 2 + 4,
          "font-size": 10.5,
          fill: color,
          "text-anchor": isAbove ? "start" : "end",
        },
        `${row.value.toFixed(2)} (${deltaSign}${row.delta.toFixed(2)})`
      )
    );
  });

  // Source footnote
  if (data.source) {
    const footnote =
      data.source.length > 92 ? data.source.slice(0, 89) + "..." : data.source;
    svg.appendChild(
      el(
        "text",
        {
          x: margin.left,
          y: height - 8,
          "font-size": 9,
          fill: "#aaaaaa",
        },
        `Source: ${footnote}`
      )
    );
  }
}

Keep the same module shape (meta + render(svg, data)) and the same data. Return
only the module code.

Structural change vs the default arm

Added: nothing. Removed: nothing.