ribbon
Fills the region between a baseline and a set of data points as a filled band. Like line, a ribbon traces a layout produced by another chart, selected with select_all() — an array of refs whose placed geometry the ribbon reads.
from gofish import layer, chart, spread, blank, select_all, ribbon
layer([
chart(lake_totals)
.flow(spread(by="lake", dir="x", spacing=64))
.mark(blank(h="count").name("points")),
chart(select_all("points")).mark(ribbon(opacity=0.8)),
]).render(w=500, h=300, axes=True)Signature
ribbon(stroke=None, stroke_width=None, opacity=None, mix_blend_mode=None,
dir=None, curve=None, along=None, w=None, h=None, em_x=None, em_y=None) -> MarkParameters
Edge-mode connector — a filled band between the facing edges of consecutive marks (areas, streamgraphs, sankey ribbons).
| Option | Type | Default | Description |
|---|---|---|---|
fill | str | Fill color of the band, or a field name for a color scale. Omitted, the band takes the color of the marks it connects. | |
stroke | str | Stroke color. | |
stroke_width | float | 0 | Stroke width in pixels. |
opacity | float | Opacity, 0 to 1. | |
mix_blend_mode | str | "normal" | Blend mode where bands overlap. |
dir | str | Connection axis. | |
curve | Any | Screen-space band-edge shape ("linear" | bezier() | "step" | "monotone" | "smooth" | "catmullRom"). "step" steps both edges, as a stepped area does. "monotone" is piecewise monotone: between two neighboring points each edge only rises or only falls, so it never goes past either point, though the band still turns where the data turns (d3 curveMonotoneX, Vega-Lite interpolate "monotone"); "smooth" is a rounder reading over the same parameter, and can go a little past a point; "catmullRom" is a centripetal Catmull-Rom on screen and can overshoot. Omitted = "auto" (monotone on a homogeneous continuous connection axis, else a bezier band). | |
from_ | str | ||
to | str | ||
along | str | Names a flow tier by its by field: that tier becomes the path tier (threading its groups in order) and every OTHER grouping tier splits. Omitted: the path tier is inferred from the flow shape. Naming a field that matches no tier, or using along where the mark doesn't fuse over this chart's own flow (a refs bag, or the pairwise from/to form), is an error. | |
em_x | bool | Blank-fusion anchor key: placed directly in .mark() position, ribbon(opts) elaborates to an invisible anchor tier (a blank() carrying just {w, h, emX, emY}) plus this connector — see the mark construct's doc. Ignored by ribbon itself. | |
em_y | bool | Blank-fusion anchor key — see emX. Ignored by ribbon itself. | |
w | int | float | str | Blank-fusion anchor key — see emX. Ignored by ribbon itself. | |
h | int | float | str | Blank-fusion anchor key — see emX. Ignored by ribbon itself. |
Returns a Mark for use in .mark().
The ribbon pattern
Ribbons use the same two-chart recipe as line: one chart positions named blank marks, a second select_alls them and draws the ribbon(). select_all(name) reads a named layer from an earlier chart as an array of refs, and layer([chartA, chartB]) composes multiple charts into one figure. To re-partition the selection first (e.g. one ribbon per series), run it through group(by="field") — see group.
Stack several ribbons in one layer — with opacity or mix_blend_mode — for layered and stacked area charts.
Default grouping
A ribbon fused into a flow — in .mark() position or as .layer() sugar over the previous tier's marks — splits at the flow's own grouping by default: one tier lays the band's path, and every other grouping in the flow splits it into separate bands. You don't restate the split — the flow one line up already declared it, and ribbon has no option that spells the split directly.
When you need a different path tier than the one inference would pick, name it with along: along="species" finds the flow tier whose by is "species", makes it the path, and splits by every other grouping tier instead. Naming a field no tier groups by is an error. This doesn't apply to a ribbon drawn over an explicit refs bag (chart(select_all(...))) or the pairwise from/to form — along is only meaningful when the ribbon fuses into a chart's own flow, and throws if used on either of those. A refs bag spells its split structurally instead, with an upstream flow(group(by="species")).
Sugar: .layer(ribbon(...))
When the ribbon traces a chart's own marks, skip the two-chart select_all recipe and chain .layer() on the builder — this is the canonical simple ribbon-chart spelling:
from gofish import chart, spread, stack, field, rect, ribbon
chart(seafood, axes=True).flow(
spread(by="lake", dir="x", spacing=64),
stack(by=field("species").sort("count"), dir="y"),
).mark(rect(h="count", fill="species")).layer(
ribbon(opacity=0.8)
).render(w=400, h=400)No by needed: the stack(by="species") tier already told the flow how to group, so the ribbon splits into one band per species by default.
See .layer() for the full semantics, including the zBelow-by-default paint order and the desugaring to the explicit layer([...]) + select_all form (which is still what you want to trace another chart's marks).
Sugar: .mark(ribbon(...)) (blank-fusion)
When there's no earlier tier at all — just raw data that needs both fresh anchors and a connector — place ribbon(...) directly in .mark() position and skip .layer() too:
chart(lake_totals).flow(
spread(by="lake", dir="x", spacing=64)
).mark(ribbon(h="count", opacity=0.8))
# ...is sugar for the explicit two-tier form:
chart(lake_totals).flow(
spread(by="lake", dir="x", spacing=64)
).mark(blank(h="count")).layer(ribbon(opacity=0.8))A grouped flow fuses the same way, with no by needed:
chart(seafood, axes=True).flow(
spread(by="lake", dir="x", spacing=64, alignment="middle"),
stack(by="species", dir="y"),
).mark(ribbon(h="count", fill="species", opacity=0.8))fill here is a shared field name, homogeneous within each species group (the default split, above) — it resolves through the color scale per group, the same as it would if fill were declared on an explicit anchor blank().
See .layer()'s blank-fusion section for the full desugaring rule (the w/h/em_x/em_y anchor/connector key split, .name() chaining, and when the rule doesn't fire).
The w/h/em_x/em_y anchor channels are only meaningful when ribbon gets to synthesize its own anchors this way; passing them to a ribbon that instead connects already-drawn marks (an empty-scope chart() tier inside .layer(), or chart(select_all(...))/chart(ref(...))) is an error, since there's nothing left for them to anchor.
Examples
# Semi-transparent ribbon
chart(select_all("points")).mark(ribbon(opacity=0.8))