stack
Shorthand for spread({ glue: true }). Children are glued together and their data-driven sizes sum into a continuous positional axis at this level. Used for stacked bar charts.
gf.chart(seafood, { axes: true })
.flow(
gf.spread({ by: "lake", dir: "x" }),
gf.stack({ by: "species", dir: "y" })
)
.mark(gf.rect({ h: "count", fill: "species" }))
.render(root, { w: 400, h: 250 });Signature
// Operator form:
stack({ by?, dir, alignment?, ... })
// Combinator form:
stack({ dir, ... }, [m1, m2, ...])Parameters
stack — Operator form
spread({ glue: true }) under its own wire tag — children glued together (touching, no gaps).
| Option | Type | Default | Description |
|---|---|---|---|
by | string | FieldAccessor | Field to partition rows by; also accepts a field(...) accessor carrying domain ops (sort/reverse/bin). | |
dir | string | Axis to stack along: x, y, or an axis name the enclosing coordinate space declares (polar theta/r, geo lon/lat). | |
spacing | number | Forwarded to the underlying spread. Glue semantics force the effective gap to 0; accepted for spread-parity. | |
glue | boolean | Spread-parity passthrough; stack always glues regardless. | |
alignment | string | "baseline" | Cross-axis alignment ("start" | "middle" | "end" | "baseline"). |
sharedScale | boolean | false | Share one scale across all children. |
anchor | "edge" | "start" | "middle" | "end" | "baseline" | "edge" | Whether spacing is measured between facing edges (edge), or as a fixed pitch between the named anchor point on each child. |
reverse | boolean | false | Reverse the children's order along dir. |
axes | AxesOptions | ||
x | number | string | FieldExpr | Left edge of this operator's box, in the parent's space (pixels). Omitted, the parent places it. | |
y | number | string | FieldExpr | Top/bottom edge (y-up: bottom) of this operator's box, in the parent's space (pixels). Omitted, the parent places it. | |
w | number | string | FieldExpr | Data-driven cross-axis extent (field/datum-sized children). | |
h | number | string | FieldExpr | Data-driven cross-axis extent (field/datum-sized children). | |
size | number | string | FieldExpr | Per-entry stack-axis extent (field/datum-sized children); a field(...).normalize() accessor makes it a space-filling spine. |
stack — Combinator form
Low-level combinator form of stack. Same fields as the operator form (OPERATORS.stack) plus key and the full box-dims group.
| Option | Type | Default | Description |
|---|---|---|---|
x | number | string | FieldExpr | Left edge of this operator's box, in the parent's space (pixels). Omitted, the parent places it. | |
w | number | string | FieldExpr | Data-driven cross-axis extent (field/datum-sized children). | |
y | number | string | FieldExpr | Top/bottom edge (y-up: bottom) of this operator's box, in the parent's space (pixels). Omitted, the parent places it. | |
h | number | string | FieldExpr | Data-driven cross-axis extent (field/datum-sized children). | |
by | string | FieldAccessor | Field to partition rows by; also accepts a field(...) accessor carrying domain ops (sort/reverse/bin). | |
dir | string | Axis to stack along: x, y, or an axis name the enclosing coordinate space declares (polar theta/r, geo lon/lat). | |
spacing | number | Forwarded to the underlying spread. Glue semantics force the effective gap to 0; accepted for spread-parity. | |
glue | boolean | Spread-parity passthrough; stack always glues regardless. | |
alignment | string | "baseline" | Cross-axis alignment ("start" | "middle" | "end" | "baseline"). |
sharedScale | boolean | false | Share one scale across all children. |
anchor | "edge" | "start" | "middle" | "end" | "baseline" | "edge" | Whether spacing is measured between facing edges (edge), or as a fixed pitch between the named anchor point on each child. |
reverse | boolean | false | Reverse the children's order along dir. |
axes | AxesOptions | ||
size | number | string | FieldExpr | Per-entry stack-axis extent (field/datum-sized children); a field(...).normalize() accessor makes it a space-filling spine. | |
key | string | Internal per-node key override. |
Box dimensions
| Option | Type | Default | Description |
|---|---|---|---|
cx | number | string | FieldExpr | Center x. | |
x2 | number | string | FieldExpr | Right edge position. | |
emX | boolean | Embed x in the parent's x space. | |
cy | number | string | FieldExpr | Center y. | |
y2 | number | string | FieldExpr | Other y edge position. | |
emY | boolean | Embed y in the parent's y space. | |
dims | Record<string, AxisDimsValue> | Box dimensions by axis name: x/y, or a name the enclosing coordinate space declares (polar theta/r, geo lon/lat). Each value is a position (like x) or an interval {min, center, max, size, embedded}. |
dir takes "x", "y", or an axis name the enclosing coordinate space declares, such as "theta" under polar; see spread → naming the axis.
Same as spread without spacing or glue — stack always glues, so neither is configurable. Its by is the same path-aware option ("field", which also works on refs after a selection, a function, or a field(...) accessor); see spread → path-aware by. If you want gaps between children plus a continuous data axis, use spread({ spacing: N }) — spread's "data-driven SIZE composition" mode is the natural fit there.
size sets each entry's stack-axis extent (a field name, a field(...) accessor, or an explicit array). Set it to field(<name>).normalize() to make a stack a space-filling spine — its segments fill the extent in proportion to their share (the mosaic/marimekko conditional axis). See spread → Field-expression pipeline and spread → Space-filling spines.
Signed parts
stack places its parts end to end, in order. Each part starts where the one before it ends, and the first part starts at 0. A negative part goes back, so the stack ends at the sum of its parts, and parts that cancel overlap. Parts (+100, −30, +20, −50, +10) rise to 100, step back and forth, and end at 50.
To pile positive parts up from 0 and negative parts down from 0 (a diverging stacked bar), group by sign first, so each stack holds one sign:
// `direction` is "Inflow" for positive amounts and "Outflow" for negative.
.flow(
spread({ by: "quarter", dir: "x" }),
group({ by: "direction" }),
stack({ by: "flow", dir: "y" })
)Centered stacks
When the chart's schema gives the by column a midpoint (Schema.ordered(levels).diverging()), the stack's 0 is the midpoint of that order instead of the start of its first part. The parts must be nonnegative. This draws Likert charts and population pyramids:
chart(survey, { schema: { response: Schema.ordered(LEVELS).diverging() } })
.flow(
spread({ by: "question", dir: "y" }),
stack({ by: "response", dir: "x" })
)
.mark(rect({ w: "count", fill: "response" }));Example
// Stacked bar chart grouped by "site", stacked by "variety"
.flow(
spread({ by: "variety", dir: "x" }),
stack({ by: "site", dir: "y" })
)