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

/

Customize Each Part

Customize Each Part

Start with the part you want to change. Data marks and axis geometry use XY's validated renderer-neutral style vocabulary. Colorbars, legends, tooltips, controls, and annotation labels are DOM chrome and also accept component or slot styles.

PartUse
Area, line, point, or bar paintTyped mark props or mark style=
Grid, axis line, ticks, or tick textx_axis(...) / y_axis(...)
Continuous color scale and colorbarMark colormap= / domain=, then xy.colorbar(...)
Built-in legend or tooltipThe component's class_name / style, or chart slots
Rules, bands, arrows, and calloutsAnnotation geometry props plus label style
Crosshair, selection, and toolbarinteraction_config, modebar, theme tokens, or slots

Fill, stroke, opacity, and gradients

Marks are rendered geometry rather than DOM nodes. Use typed props for common choices, or the mark's validated style= mapping when CSS-shaped paint is clearer. Unsupported properties raise while the chart is built instead of silently disappearing in one renderer.

Start Building Now!

Use fill, fill-opacity, stroke, stroke-width, stroke-opacity, and opacity only on mark families that support them. Lines use stroke properties; areas, points, bars, and columns also support fill properties. Bar-like marks add border-radius. line, step, stairs, and ecdf add stroke-dasharray and stroke-linecap; area adds stroke-dasharray only, because a cap is open-path geometry. scatter adds marker-shape.

stroke-linecap (butt, round, square) carries its standard SVG meaning, and XY defaults it to round rather than to the CSS initial value — the native rasterizer has always drawn round caps and it is the reference for static export. marker-shape is the CSS spelling of symbol= and takes any of the 17 built-in marker names.

Axes, grid, and ticks

Treat the x and y axes independently. Grid lines are owned by the axis they cross: the y axis draws horizontal guides and the x axis draws vertical ones. Axis baselines, tick marks, tick text, and titles are separate style properties, so hiding one never requires hiding the others.

Customize horizontal grid lines

Keep the y-axis grid and turn off the x-axis grid for a calm reporting chart. grid_color, grid_width, grid_dash, and grid_opacity style the guides; the remaining zero-width and transparent properties remove the baseline and ticks without removing the horizontal grid.

Start Building Now!

Use "solid", "dashed", "dotted", or "dashdot" for grid_dash. Setting grid_opacity=0 is the direct way to disable one direction of grid.

Customize the axis line, ticks, and tick text

This column chart keeps a deliberate bottom axis. The baseline uses axis_color and axis_width; tick marks use tick_color, tick_width, and tick_length; the month text uses tick_label_color. The y axis remains visually quiet while still drawing horizontal guides.

Start Building Now!

Add label="Month" and label_color only when the axis title adds information that the surrounding heading does not already provide. tick_label_strategy controls collisions independently: use "rotate", "stagger", "hide", or "none" for dense categorical axes.

Clean dashboard axes

For compact dashboard charts, remove both baselines, ticks, and tick text; disable the x grid; and retain only the y grid. This is the same quiet axis treatment used by the product-ready examples, shown here with two vivid series and a compact stroke-shaped legend.

Start Building Now!

The reusable recipe is: tick_label_strategy="none", transparent axis and tick colors with zero widths on both axes, grid_opacity=0 on the x axis, and a subtle grid_color on the y axis. Keep extra top padding when a legend sits inside the plot.

Hide axis chrome with a switch

Because hiding axis parts is subtraction, each axis takes plain switches instead of transparent colors: show, line, ticks, grid, and text (tick labels plus the axis title). show sets the other four, and each of them overrides show, so the reporting look above is two lines.

WantWrite
No axis chrome at allxy.x_axis(show=False)
Horizontal guides onlyxy.y_axis(show=False, grid=True)
Labels, but no baseline or ticksxy.x_axis(line=False, ticks=False)
Everything except the gridxy.x_axis(grid=False)

An explicit style= property still wins, so you can switch a part off and then bring one property back — xy.x_axis(show=False, style={"grid_color": "#eee", "grid_opacity": 1}) — without writing the other six.

Color scales and colorbars

Set a continuous mark's colormap= and domain= together when colors must have a stable meaning across charts. Add xy.colorbar(...) to explain that scale, then style its container, gradient, ticks, and title through the colorbar, colorbar_bar, colorbar_tick, colorbar_title, colorbar_extension, colorbar_line, and colorbar_minor_tick slots.

Build a colormap from your own colors

colormap= takes any of XY's twenty built-in names, and also a custom ramp built from the colors your design system already defines:

FormExample
A list of CSS colors, evenly spacedcolormap=["#0b1220", "#2563eb", "#22d3ee", "#fde68a"]
(position, color) pairscolormap=[(0.0, "#f8fafc"), (0.2, "#38bdf8"), (1.0, "#0f172a")]
A CSS gradient (2–8 stops)colormap="linear-gradient(#0b1220, #2563eb 30%, #fde68a)"

A gradient takes up to 8 stops, the list forms up to 256. Colormap stops are opaque: use the mark's opacity/fill-opacity for transparency rather than a translucent stop, which XY refuses instead of silently flattening to black.

The ramp is resolved once, in Python, so the browser, to_svg(), and to_png() paint identical marks, and the colorbar follows it in every renderer. In the browser the legend row for a continuous encoding also becomes a gradient swatch of the ramp; the static legend draws a solid handle, as it always has.

One rule follows from that: colormap stops must be colors XY can resolve without a browser — hex, rgb(), hsl(), or a named color. var(--brand), oklch(...), and color-mix(...) are fine on an individual color=, stroke, or fill, but not as a colormap stop or an xy.theme(palette=...) entry. Both are indexed color lookups used by static renderers that have no cascade to resolve browser-only colors. Resolve the token to a literal in Python and pass that.

Recolor categories with a chart palette

A categorical color= channel and unnamed series both draw from the chart's palette. xy.theme(palette=[...]) replaces it for the whole chart, so a set of brand swatches colors every series and every category at once:

Series take the palette in order — one slot per series, whatever its trace count, so a box plot (four traces) or a stem (two) advances the cycle by one like everything else. An explicit color= on a mark still wins, and takes no slot, so the next series that needs a color still gets the palette's first. A palette shorter than the number of series or categories repeats, and says so with a warning rather than quietly reusing a color.

To pin specific categories to specific colors, pass a mapping instead of a list:

A list can only say "the first category is blue", so the same category changes color whenever the set of categories does — most visibly across facet panels, where a panel that happens to be missing one category silently recolors the rest. A mapping keys on the label, so a category keeps its color everywhere. Categories the mapping does not name take the next default color it has not already used, and say so with a warning.

Palette entries follow the same rule as colormap stops: literal colors only — hex, rgb(), hsl(), or a named color. var(--brand-500) is fine on an individual color=, but not in a palette, because a palette is indexed and XY has to resolve each entry itself for density surfaces and for to_svg() / to_png(). Several var() entries would land on one fallback and merge distinct categories into a single color, so XY refuses them and says why. Resolve your token to a literal in Python and pass that. Named colors, rgb(), and hsl() are all fine — XY normalizes them to hex for you.

Start Building Now!
Start Building Now!

Use a legend instead when colors identify discrete categories. Built-in colorbars are available in browser, SVG, native PNG, and Chromium output; host framework components passed through render= are not part of standalone XY exports. See Colorbars for supported marks, orientation, inferred scales, and custom-component boundaries.

Legend

Configure legend content and layout with xy.legend(...). Style the component directly, or use the chart's legend, legend_title, legend_item, legend_swatch, and legend_label slots when one rule should cover several charts. Complete literal Tailwind utilities work through class_name / class_names when the host enables Reflex's TailwindV4Plugin.

Start Building Now!

A short, rounded swatch makes an area-series legend read like its visible stroke instead of a generic color chip. A genuinely custom host legend is ordinary Reflex UI: hide the built-in legend with xy.legend(show=False), keep the chart in state, and render the controls beside it. Host-owned UI is not included in standalone XY exports.

Tooltip

Tooltip fields must already be resident in a rendered data channel. Use named data columns for stable lookup, map them to readable display text with labels=, and format them with format=. Style the built-in container directly or target tooltip_title, tooltip_row, tooltip_label, and tooltip_value independently.

Start Building Now!

For a host-owned tooltip, set xy.tooltip(show=False), handle on_point_hover, and render ordinary framework UI from the received row. The built-in tooltip remains the right choice when it must track the pointer or survive a standalone export.

Annotations

Annotation geometry is painted with the chart; annotation text is DOM chrome. Use geometry props such as color, width, and opacity, then use the annotation's style or the chart's annotation_label slot for its label.

Start Building Now!

threshold(...) and threshold_zone(...) are semantic aliases for reference lines and bands. Arrow shafts, markers, rules, and zones remain painted geometry; only their labels respond to DOM slot styles.

Interaction chrome

Crosshairs, selections, and the modebar are configured independently from data marks. Interaction colors belong in xy.theme(...); DOM pieces can also use the crosshair_x, crosshair_y, selection, modebar, and modebar_button slots. The remaining modebar_* slots reach the draggable grip, control group, separators, icons, zoom value, indicators, selection icon, menus, menu content, and history controls independently.

Start Building Now!

Move across the plot to inspect the crosshair. Open the modebar selection menu, or Shift-drag, to inspect the styled selection rectangle. Framework callbacks such as on_select_end can turn the result into filters or related views.

Built with Reflex