For AI agents: the complete XY documentation index is at llms.txt. Markdown versions are available by appending .md or sending Accept: text/markdown.
Styling

/

Capability Matrix

Capability Matrix

<!-- generated by scripts/gen_capability_matrix.py --write; do not edit -->

Every styling question about XY has the same two halves: can I change this, and does the change survive where I need it. This page answers both from the registry the implementation is checked against.

  • 11 mark style properties across 21 mark kinds, drawn by all three renderers.
  • 29 stable chrome slots for CSS and Tailwind in the browser.
  • 1 way to add a mark kind XY does not ship, without forking it.

Mark style properties

What a mark's style= accepts. Anything outside this raises while the chart is built — one renderer never silently ignores what another draws.

propertyvocabularymark kindswebglsvgnativestatus
opacitycssarea, bar, box, column, contour, ecdf, error_band, errorbar, heatmap, hexbin, hist, histogram, line, ribbon, scatter, segments, stairs, stem, step, triangle_mesh, violinfullfullfullshipped
fillsvgarea, bar, box, column, error_band, hist, histogram, scatter, triangle_mesh, violinfullfullfullshipped
fill-opacitysvgarea, bar, box, column, error_band, heatmap, hexbin, hist, histogram, ribbon, scatter, triangle_mesh, violinfullfullfullshipped
strokesvgarea, bar, box, column, contour, ecdf, error_band, errorbar, hist, histogram, line, ribbon, scatter, segments, stairs, stem, step, triangle_meshfullfullfullshipped
stroke-opacitysvgarea, bar, box, column, contour, ecdf, error_band, errorbar, hist, histogram, line, ribbon, scatter, segments, stairs, stem, step, triangle_meshfullfullfullshipped
stroke-widthsvgarea, bar, box, column, contour, ecdf, error_band, errorbar, hist, histogram, line, ribbon, scatter, segments, stairs, stem, step, triangle_meshfullfullfullshipped
stroke-dasharraysvgarea, ecdf, line, stairs, stepfullfullfullshipped
stroke-linecapsvgecdf, line, stairs, stepfullfullfullshipped
border-radiuscssbar, column, hist, histogramfullfullfullshipped
wedge-gapxybar, column, hist, histogramfullfullfullshipped
marker-shapexyscatterfullfullfullshipped

Notes

  • opacity — Multiplies the mark's own alpha in every renderer.
  • fill — A plain color compiles to the mark's paint; a linear-gradient(...) compiles to a gradient and is accepted only by area and rect kinds, which are the ones with a gradient program.
  • fill-opacity — Independent of opacity; the two multiply.
  • stroke — The paint for line-like geometry, and the border for filled marks.
  • stroke-width — CSS px; a bare number is px, matching the chrome style convention.
  • stroke-dasharray — 2-8 positive px lengths, or none. The WebGL client tracks arc length on the CPU so dashes stay continuous across segments and constant on screen through zoom.
  • stroke-linecap — Line family only — a cap is open-path geometry. XY's default is round, not CSS's butt, because the native rasterizer has always drawn round and is the reference for static export. Verified per renderer: a Rust coverage test, a rasterized-ink test, and three Chromium screenshots that hash differently per cap.
  • border-radius — Rect kinds only. corner_radius=(tip, base) rounds the two ends separately.
  • wedge-gap — Gap between neighbouring polar wedges, in px. Rect kinds under coords="polar" only; ignored elsewhere. Deliberately a LENGTH rather than an angle: an angular pad's seam is r * dtheta wide, so it tapers to nothing at the hole and reads as uneven spacing. The angular inset therefore grows as the radius shrinks, which is the same construction as d3's padAngle/padRadius pair. An XY vocabulary name: CSS has no gap between two arcs.
  • marker-shape — 17 shapes, drawn as analytic signed-distance fields in all three renderers. An XY vocabulary name: CSS has no shape keyword for a non-DOM point mark, and the CSS spelling and symbol= compile to the same value.

Chrome slots

Stable data-xy-slot names that take class_names= and styles=. The native raster and vector writers have no cascade, so per-slot styling is a browser mechanism; put anything that must survive export in the chart-level style= token bag or in mark and axis style=, which every renderer reads.

slotbrowsernative rasternative vector
rootfullpartialpartial
titlefullpartialpartial
chromefullnonenone
canvasfullnonenone
labelsfullnonenone
legendfullpartialpartial
legend_titlefullpartialpartial
legend_itemfullnonenone
legend_swatchfullnonenone
legend_labelfullpartialpartial
colorbarfullpartialpartial
colorbar_barfullnonenone
colorbar_tickfullpartialpartial
colorbar_titlefullpartialpartial
tooltipfullnonenone
tooltip_titlefullnonenone
tooltip_rowfullnonenone
tooltip_labelfullnonenone
tooltip_valuefullnonenone
modebarfullnonenone
modebar_buttonfullnonenone
selectionfullnonenone
crosshair_xfullnonenone
crosshair_yfullnonenone
badgefullnonenone
badge_itemfullnonenone
tick_labelfullpartialpartial
axis_titlefullpartialpartial
annotation_labelfullnonenone

Notes

  • root (via chart style=) — styles={'root': ...} is browser-only, but the chart-level style= token bag targets the same element and every renderer reads it (spec['dom']['style']). Prefer it for anything that must survive export.
  • title (via styles={'title': ...}) — Vector (SVG, PDF) honors font-size, font-weight, font-style, font-family, letter-spacing, opacity and the text paint (fill, or color). The raster writer's glyph primitive takes a size and one RGBA paint and nothing else, so it honors font-size and the paint only — font-weight, font-style, font-family, letter-spacing and opacity are vector-only rather than silently approximated. Properties outside the subset stay browser-only.
  • legend (via styles={'legend': ...} / xy.legend(style=...) / --chart-legend-bg) — The frame box. Both spellings and the theme token now converge on one merged declaration block before the writers see it, so what agrees in the browser agrees in a PNG. background, boxShadow, borderRadius, --xy-legend-frame-alpha, and padding/rowGap in em are honored; an explicit background paints opaque, as it does in the browser.
  • legend_title (via styles={'legend_title': ...}) — Vector (SVG, PDF) honors font-size, font-weight, font-style, font-family, letter-spacing, opacity and the text paint (fill, or color). The raster writer's glyph primitive takes a size and one RGBA paint and nothing else, so it honors font-size and the paint only — font-weight, font-style, font-family, letter-spacing and opacity are vector-only rather than silently approximated. Properties outside the subset stay browser-only.
  • legend_label (via styles={'legend_label': ...}) — Vector (SVG, PDF) honors font-size, font-weight, font-style, font-family, letter-spacing, opacity and the text paint (fill, or color). The raster writer's glyph primitive takes a size and one RGBA paint and nothing else, so it honors font-size and the paint only — font-weight, font-style, font-family, letter-spacing and opacity are vector-only rather than silently approximated. Properties outside the subset stay browser-only.
  • colorbar (via styles={'colorbar': ...}) — Vector (SVG, PDF) honors font-size, font-weight, font-style, font-family, letter-spacing, opacity and the text paint (fill, or color). The raster writer's glyph primitive takes a size and one RGBA paint and nothing else, so it honors font-size and the paint only — font-weight, font-style, font-family, letter-spacing and opacity are vector-only rather than silently approximated. Properties outside the subset stay browser-only.
  • colorbar_tick (via styles={'colorbar_tick': ...}) — Vector (SVG, PDF) honors font-size, font-weight, font-style, font-family, letter-spacing, opacity and the text paint (fill, or color). The raster writer's glyph primitive takes a size and one RGBA paint and nothing else, so it honors font-size and the paint only — font-weight, font-style, font-family, letter-spacing and opacity are vector-only rather than silently approximated. Properties outside the subset stay browser-only.
  • colorbar_title (via styles={'colorbar_title': ...}) — Vector (SVG, PDF) honors font-size, font-weight, font-style, font-family, letter-spacing, opacity and the text paint (fill, or color). The raster writer's glyph primitive takes a size and one RGBA paint and nothing else, so it honors font-size and the paint only — font-weight, font-style, font-family, letter-spacing and opacity are vector-only rather than silently approximated. Properties outside the subset stay browser-only.
  • tick_label (via styles={'tick_label': ...}) — Vector (SVG, PDF) honors font-size, font-weight, font-style, font-family, letter-spacing, opacity and the text paint (fill, or color). The raster writer's glyph primitive takes a size and one RGBA paint and nothing else, so it honors font-size and the paint only — font-weight, font-style, font-family, letter-spacing and opacity are vector-only rather than silently approximated. Properties outside the subset stay browser-only.
  • axis_title (via styles={'axis_title': ...}) — Vector (SVG, PDF) honors font-size, font-weight, font-style, font-family, letter-spacing, opacity and the text paint (fill, or color). The raster writer's glyph primitive takes a size and one RGBA paint and nothing else, so it honors font-size and the paint only — font-weight, font-style, font-family, letter-spacing and opacity are vector-only rather than silently approximated. Properties outside the subset stay browser-only.

Extension points

See Custom Marks for the worked example.

extension pointstatusentry pointlimits
mark_plugin_compositionshippedxy.register_mark / xy.MarkPlugin / xy.markcomposes built-in marks only, one level deep; cannot reach the Figure, the trace list, or the column store; cannot add a GPU primitive
mark_plugin_shaderplanned
custom_rendererplanned

Where renderers still disagree

Differences in the defaults that no property selects. They are listed because an undocumented difference reads as a bug; a documented one is a contract.

whatbrowsersvgnative pngvisible when
Interior vertices of a wide polylinethe notch two overlapping segment quads leaveround (the writer names it explicitly)round (the capsule distance field fills the vertex)stroke-width above ~4px at a sharp angle

For what is still alpha, see Limitations and Alpha Status.

Built with Reflex