Exploring legend-positioning with CSS and SVG foreignObject

Not sure whether this really fits into the “Show and Tell” category, but I did some exploration around how to position legends and include them into SVGs generated by Observable-Plot. I found the documentation around this lacking, so perhaps some others may find this helpful, too:

I have done something similar using serialize(chart) from Mike.
Year to date CO2 Emissions variation per State (vs 2019) / Enrico Spinielli | Observable

Huh, interesting. Thanks for sharing. I admit that I don’t fully get what serialise does. It seems to have come from a time when Charts were not readily downloadable as SVG at all, but that is now built right into the Observable Cells, as Mike comments on the top of his notebook, right?

Instead of modifying the SVG externally I’d recommend to use a mark function:

Plot.plot({
  marks: [
    Plot.dot(penguins, {
      x: "culmen_length_mm",
      y: "culmen_depth_mm",
      stroke: "species"
    }),
    // RenderFunction: https://github.com/observablehq/plot/blob/v0.6.17/src/mark.d.ts#L42-L60
    (index, scales, values, dims, context) => {
      const width = 100;
      const height = 80;
      const x = dims.width - dims.marginRight - width;
      const y = dims.height - dims.marginBottom - height;
      const label = htl.html`<div style="font:bold 12px/1.5 system-ui,sans-serif">Species`;
      return htl.svg`<foreignObject ${{width, height, x, y}}>${label}${Plot.legend(scales.scales)}`;
    }
  ],
})

1 Like

Brillant! Thanks for this. Yes, I was aware that this might be possible via the marks-interface, but I was a bit overwhelmed by the 5-argument function API. Super useful to have this example ready!