AI-readable SVG attributes
Every plotlet SVG carries data-plotlet-* attributes describing plot
type, axes, scales, ranges, and series labels — so an AI tool can read
plot semantics directly without OCR-ing text-as-paths or inverting pixel
positions. Schema is semver-stable, declared via data-plotlet-schema="2"
on the root.
Schema reference
Root <svg>
| Attribute | Example | Notes |
|---|---|---|
data-plotlet-version |
"0.6.2" |
the plotlet release that emitted this SVG |
data-plotlet-schema |
"2" |
bumped only when names change incompatibly |
data-plotlet-kind |
"layout" |
always "layout" — a lone chart is a 1x1 layout |
The root's first child is the figure-background <rect>; it carries no
data-plotlet-* attributes — skip it when walking for tagged elements.
Panel <g> (one per chart leaf)
| Attribute | Example | Notes |
|---|---|---|
data-plotlet-kind |
"panel" |
discriminator for nested panels in layouts |
data-plotlet-title |
"Daily revenue" |
omitted if empty |
data-plotlet-xlabel / -ylabel |
"date" / "USD" |
omitted if empty |
data-plotlet-xscale / -yscale |
"linear" / "log" / "category" |
|
data-plotlet-xlim / -ylim |
"0,42" / "-1.5,1.5" |
resolved limits post-autoscale; omitted on categorical axes |
data-plotlet-yflip |
"true" |
present only when y is rendered inverted (e.g. imshow(origin="upper")) |
data-plotlet-panel-bbox |
"0,0,560,348" |
"x,y,w,h" of the full panel rect in figure-SVG coords |
data-plotlet-data-area |
"60,24,500,300" |
"x,y,w,h" of the data region in panel-local coords; add the bbox's (x,y) for figure-SVG coords |
For categorical axes, the label list is emitted as a child <metadata> —
see "Categorical lists" below.
Diagram <g> (debug-visualizer leaves)
pt.layout_diagram(c) produces a leaf with data-plotlet-kind="diagram"
and no panel/axis attrs (it's not a chart). Consumers can identify and
skip these when scanning for data panels.
Legend <g> (standalone legend leaves)
pt.legend(...) produces a leaf with data-plotlet-kind="legend" and:
| Attribute | Example | Notes |
|---|---|---|
data-plotlet-legend-bbox |
"640,32,80,256" |
"x,y,w,h" of the legend's allocated rect in figure-SVG coords |
Inline legends (rendered inside a panel via c.legend(True, position=...)) live inside the panel <g> and don't carry
these attrs — their geometry is part of the panel's chrome rather than
a sibling leaf. Filter by data-plotlet-kind to distinguish.
Inset <g> (inset axes)
c.inset(...) renders inside the parent panel's <g> as a nested
<g data-plotlet-kind="inset"> wrapping a complete inner SVG — the
inset's own panel/artist attrs live inside it, so a consumer walking
for panels should recurse into (or deliberately skip) inset groups.
Artist <g class="plotlet-artist"> (one per recorded artist)
Common attrs (always emitted):
| Attribute | Example |
|---|---|
data-plotlet-type |
"line", "bar", "hist", "imshow", … |
data-plotlet-index |
"0", "1", … |
data-plotlet-label |
"actual" (only if user passed label=) |
data-plotlet-color |
"#1f77b4" (resolved hex) |
Type-specific attrs:
| Artist | Extras |
|---|---|
line |
n, x-min, x-max, y-min, y-max, linestyle, marker, curve |
scatter |
n, x-min, x-max, y-min, y-max, marker |
bar |
n, y-min, y-max |
hist |
n (raw obs), bins, x-min, x-max, count-max |
fill_between |
n, x-min, x-max, y-min, y-max, curve |
area |
n, x-min, x-max, y-min, y-max, base, curve |
rect / polygon |
n, x-min, x-max, y-min, y-max |
errorbar |
n, x-min, x-max, y-min, y-max, marker |
axhline / axvline |
y / x |
axhspan / axvspan |
ymin, ymax / xmin, xmax |
axline |
x1, y1 plus slope (point + slope form) or x2, y2 (two-point form) |
hlines / vlines |
n, x-min, x-max, y-min, y-max |
text |
n, x-min, x-max, y-min, y-max |
annotate |
x, y (annotated point), tx, ty (text position), text |
imshow |
rows, cols, vmin, vmax, cmap, extent, data-encoding ("rects" below ~10000 cells, "png-embedded" above); when non-default: origin, norm, center, annot ("values"/"custom") |
heatmap |
rows, cols, data-encoding plus either vmin, vmax, cmap (value mode) or mode="categorical", categories (palette mode); numeric-x charts add x-axis="continuous", x-extent; when non-default: norm, center, annot |
hist2d |
n (raw obs), bins-x, bins-y, count-max |
dendrogram |
orientation, n-leaves, max-height, leaves (concatenated scipy leaf order) |
Categorical lists
Category labels live in a <metadata> child of the panel <g> so the
attribute namespace stays simple and labels can contain arbitrary
characters:
<g data-plotlet-kind="panel" data-plotlet-xscale="category" ...>
<metadata data-plotlet-payload="xcategories"><![CDATA[["Q1","Q2","Q3","Q4"]]]></metadata>
...
</g>
CDATA lets values contain < > & unescaped; json.dumps won't
produce ]]>, so the wrap is safe.
Custom artists
Set ArtistSpec.data_attrs (optional) to add type-specific attrs. Common
attrs (type, index, label, color) come automatically.
add_artist(ArtistSpec(
name="lollipop", record=..., draw=...,
data_attrs=lambda a: {
"n": len(a["xs"]),
"x-min": min(a["xs"]),
"x-max": max(a["xs"]),
},
))
Worked example
import plotlet as pt
from plotlet import aes
df = {"day": [1, 2, 3, 4], "usd": [10.5, 12.3, 11.0, 14.7]}
c = pt.chart()
c.title("Daily revenue").xlabel("day").ylabel("USD")
c.add_line(df, aes(x="day", y="usd"), label="actual")
print(c.to_svg())
What an AI sees, semantically:
SVG kind=layout plotlet=0.6.2 schema=2
PANEL title="Daily revenue" xlabel=day ylabel=USD
xscale=linear xlim=1.0,4.0 yscale=linear ylim=10.0,15.0
ARTIST 0 type=line label=actual color=#1f77b4
n=4 x-min=1 x-max=4 y-min=10.5 y-max=14.7
All recoverable with one XML parse — no glyph-path OCR, no pixel→data inversion.
Schema-only consumer: pt.layout_diagram
pt.layout_diagram(chart) is a debug
visualizer that reads only data-plotlet-* attrs — no private imports.
Treat its source as the canonical worked example for building your own
schema consumer (layout linter, AI inspection script, etc.).