Skip to content

Latest commit

 

History

History
160 lines (134 loc) · 13.7 KB

File metadata and controls

160 lines (134 loc) · 13.7 KB

Capability matrix

What XY can be styled and extended with, per renderer. Generated from python/xy/styling/capabilities.py, which tests/test_capability_registry.py pins to styles.py and dom.py, so it cannot list a property the implementation does not compile or omit one it does.

full means the renderer draws the property as specified. partial means it draws something the notes have to qualify. none means it does not draw it — which is sometimes deliberate, and the notes say which.

In one line

  • 11 mark style properties across 22 mark kinds, drawn by all three renderers.
  • 48 stable chrome slots, CSS- and Tailwind-addressable in the browser; 10 of them reach the native writers — nine through styles={slot: ...} itself, and root through the chart-level style= token bag.
  • 1 shipped extension point.
  • 1 known default divergence between renderers, listed below rather than left to be discovered.

Mark style properties

The subset a mark's style= mapping accepts. Anything outside it raises before data is ingested, so no renderer silently drops a declaration another one honors.

property vocabulary mark kinds webgl svg native status
opacity css area, bar, box, column, contour, ecdf, error_band, errorbar, funnel, heatmap, hexbin, hist, histogram, line, ribbon, scatter, segments, stairs, stem, step, triangle_mesh, violin full full full shipped
fill svg area, bar, box, column, error_band, hist, histogram, scatter, triangle_mesh, violin full full full shipped
fill-opacity svg area, bar, box, column, error_band, funnel, heatmap, hexbin, hist, histogram, ribbon, scatter, triangle_mesh, violin full full full shipped
stroke svg area, bar, box, column, contour, ecdf, error_band, errorbar, funnel, hist, histogram, line, ribbon, scatter, segments, stairs, stem, step, triangle_mesh full full full shipped
stroke-opacity svg area, bar, box, column, contour, ecdf, error_band, errorbar, funnel, hist, histogram, line, ribbon, scatter, segments, stairs, stem, step, triangle_mesh full full full shipped
stroke-width svg area, bar, box, column, contour, ecdf, error_band, errorbar, funnel, hist, histogram, line, ribbon, scatter, segments, stairs, stem, step, triangle_mesh full full full shipped
stroke-dasharray svg area, ecdf, line, stairs, step full full full shipped
stroke-linecap svg ecdf, line, stairs, step full full full shipped
border-radius css bar, column, hist, histogram full full full shipped
wedge-gap xy bar, column, hist, histogram full full full shipped
marker-shape xy scatter full full full shipped

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 accept class_names= and styles= in the browser. The native raster and vector writers have no cascade, so they read a property subset rather than a stylesheet: the chart-level style= token bag, plus styles={slot: ...} for the slots that name chrome a file actually contains. class_names= reaches neither — a class selects a rule out of a stylesheet an exported file does not have. That boundary is contracted in export.md §9 and pinned by tests/test_export_style_survival.py.

slot browser native raster native vector
root full partial partial
title full partial partial
chrome full none none
canvas full none none
annotation_layer full none none
labels full none none
legend full partial partial
legend_title full partial partial
legend_item full none none
legend_swatch full none none
legend_label full partial partial
colorbar full partial partial
colorbar_bar full none none
colorbar_extension full none none
colorbar_line full none none
colorbar_tick full partial partial
colorbar_minor_tick full none none
colorbar_title full partial partial
tooltip full none none
tooltip_title full none none
tooltip_row full none none
tooltip_label full none none
tooltip_value full none none
modebar full none none
modebar_drag_handle full none none
modebar_control_group full none none
modebar_separator full none none
modebar_button full none none
modebar_icon full none none
modebar_zoom_value full none none
modebar_indicator full none none
modebar_selection_icon full none none
modebar_menu full none none
modebar_menu_separator full none none
modebar_menu_icon full none none
modebar_menu_label full none none
modebar_history_controls full none none
selection full none none
crosshair_x full none none
crosshair_y full none none
badge full none none
badge_item full none none
axis_band full none none
axis_line full none none
tick_mark full none none
tick_label full partial partial
axis_title full partial partial
annotation_label full none none

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

Ways to add behavior the core does not ship, without forking it.

extension point status entry point limits
mark_plugin_composition shipped xy.register_mark / xy.MarkPlugin / xy.mark composes 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_shader planned
custom_renderer planned

Notes

  • mark_plugin_composition — A calc over declared columns plus a build that returns built-in marks. Its output is ordinary traces, so it reuses the built-in rendering, picking, and export paths rather than reimplementing them.
  • mark_plugin_shader — §24's WGSL/GLSL snippet pair. Deferred: a plugin with its own shader reuses none of the built-in rendering, picking, or export paths and would have to reimplement them.
  • custom_renderer — No way to add a fourth renderer or replace one of the three.

Known renderer divergences

Differences in the defaults, which no style property selects — invisible until someone diffs two exports. Listed here for that reason.

what webgl svg native visible when tracked by
Interior vertices of a wide polyline the notch two overlapping segment quads leave round (the writer names it explicitly) round (the capsule distance field fills the vertex) stroke-width above ~4px at a sharp angle no style property selects a join; the default is the whole contract

Regenerating

uv run python scripts/gen_capability_matrix.py --write

--check fails if this file is stale, and the test suite runs it.