Published-release documentation · release compatibility

Scale Options

Grouped density area chart of car acceleration
Derived density paths with grouped color and guides.

Scales map semantic field or datum values into concrete positions and appearance values. Encodings create compatible scales automatically; use editScale when the inferred mapping needs a documented revision.

At a glance

Scale family Default domain Default range Common controls
Continuous position "auto" Plot bounds type, nice, zero, clamp, reverse
Band/point position First-appearance order Plot bounds padding and alignment
Ordinal appearance/offset First-appearance order Palette, patterns, or parent band explicit domain/range
Categorical size Source appearance order or explicit categories Automatic areas [24, 196] across categories ordinal, reverse, unknown area
Continuous size Numeric extent Area [24, 196] linear, log, sqrt, pow, clamp, reverse
Discrete size Type-specific samples or cuts Explicit nondecreasing areas quantize, quantile, threshold, reverse
Color/stroke/strokeDash First-appearance order Built-in palette/patterns palette or explicit range
Sequential/discretized color or stroke Type-specific numeric boundaries viridis or explicit colors interpolate, clamp, reverse

Encoding actions accept a nested scale object. Omitted properties use channel defaults and stored program state.

For categorical color on a row-owned Cartesian line, unknown requires an explicit, nonempty ordinal domain. Missing or null colors and valid categories outside that domain use the fallback paint; raw rows, vertices, separate series, and explicit legend samples remain intact. This does not extend to aggregate, binned, parallel, or polar lines. Removing the domain or fallback must remain compatible with every consumer and its data; incompatible edits reject atomically. The same restriction applies to the first position on an otherwise incomplete line.

Existing scales can be changed with editScale:

Contextual fragment. Use an ES module with the imports, data, and prepared resource state described in this section. Caller-provided receivers: program. Resolve these names from setup in this fragment or section; alternatives branch from the same base.

const reversed = program.editScale({ id: "x", reverse: true });

Color scales can use the same top-level palette shorthand during creation and editing. It is mutually exclusive with range:

Contextual fragment. Use an ES module with the imports, data, and prepared resource state described in this section. Caller-provided receivers: program. Resolve these names from setup in this fragment or section; alternatives branch from the same base.

const recolored = program.editScale({ id: "color", palette: "set2" });

Full programs also expose channel-specific editors, so ordinary revisions do not require knowing the generated scale ID:

Contextual fragment. Use an ES module with the imports, data, and prepared resource state described in this section. Resource selectors used here: target: "points". Resolve these names from setup in this fragment or section; alternatives branch from the same base.

program
  .editXScale({ domain: [0, 100], nice: true })
  .editYScale({ zero: false })
  .editColorScale({ palette: "set2" })
  .editStrokeScale({ target: "points", palette: "set1" })
  .editSizeScale({ range: [20, 200] });

The complete focused family and declared selectors are generated below.

Focused editor Selectors Role and API layer
editXScale id (optional), target (optional) H2 · user-facing
editYScale id (optional), target (optional) H2 · user-facing
editXOffsetScale target (required) H2 · user-facing
editYOffsetScale target (required) H2 · user-facing
editParallelScale target (required), dimension (required) H2 · user-facing
editThetaScale id (optional), target (optional) H2 · user-facing
editRScale id (optional), target (optional) H2 · user-facing
editColorScale id (optional), target (optional) H2 · user-facing
editStrokeScale target (required) H2 · user-facing
editSizeScale id (optional), target (optional) H2 · user-facing
editOpacityScale id (optional), target (optional) H2 · user-facing
editShapeScale id (optional), target (optional) H2 · user-facing
editStrokeWidthScale id (optional), target (optional) H2 · user-facing
editStrokeDashScale id (optional), target (optional) H2 · user-facing

Offset editors require target; they edit that mark’s nested offset scale. editParallelScale requires both target and the exact dimension field. editStrokeScale also requires target and does not accept a raw scale ID. Other focused selectors follow the rules below. editRScale means Polar radial position; point glyph radius remains a constant appearance action.

Count-based linear domain rounding

Use nice: { count: 10 } on a linear position scale to choose automatic domain rounding density. An observed range of 0–108 becomes 0–110. The count controls rounding; it does not force the number of axis ticks. This is useful when facet panels have independent ranges.

A line reaching 108 within an automatically rounded 0 through 110 Y domain
Count density 10 rounds the automatic Y domain to 0–110 while preserving all three data points.

Contextual fragment. Use an ES module with the imports, data, and prepared resource state described in this section. Resolve these names from setup in this fragment or section; alternatives branch from the same base.

import { chart } from "ggaction";

const program = chart()
  .createCanvas({ plot: { width: 200, height: 120 }, margin: { left: 40, top: 20, right: 20, bottom: 30 } })
  .createData({ values: [{ x: 0, y: 0 }, { x: 0.5, y: 54 }, { x: 1, y: 108 }] })
  .createLinePlot({
    x: { field: "x", scale: { domain: [0, 1] } },
    y: { field: "y", scale: { zero: true, nice: { count: 10 } } },
    guides: false
  });

The object accepts only count, a positive safe integer. Existing omitted, false, and true behavior is unchanged. An explicit domain takes precedence; otherwise zero inclusion happens before rounding. The rule chooses outward 1–2–5 decade steps and preserves finite observed endpoints at numeric limits. If a requested density has no stable outward grid, the observed domain remains unchanged. A constant domain remains constant after zero inclusion.

For a binned position, the existing default bin boundaries and row membership stay unchanged; count rounding extends only the final automatic display domain. For example, boundaries [0, 54, 108] remain exact while the display domain becomes [0, 110]. The separate createBinData.nice option remains boolean-only.

The count object is available through position encodings, chart facades, editScale, and focused position-scale editors. It is rejected for temporal, transformed, discrete, color, size, and measured-radius scales that disallow this policy. Data revisions, compatible shared scales, independent facets and program persistence preserve the chosen count. For independent panels, use facet({ field: "group", scales: { y: "independent" } }) after declaring the chart.

Point size scale ranges are areas rather than radii. With fieldType: "nominal" or "ordinal", size uses an ordinal scale. Automatic domains preserve first appearance; explicit domains preserve their order and distinguish numbers from numeric-looking strings. Automatic areas span 24 through 196, with a single category assigned area 110. Explicit nonnegative area lists cycle across the domain, matching other ordinal appearance mappings. Size legends show every category with its original label and mapped area; numeric sampling (count, values) and numeric label formats are unavailable for categorical size.

Continuous size mappings support linear, log, sqrt, and pow; log defaults to base 10 and pow requires a positive exponent. Discrete quantize, quantile, and threshold mappings require explicit nondecreasing area ranges. Type-family changes require an explicit destination domain, and entering a discrete family also requires a new range. Use reverse to reverse area assignment while retaining domain and legend-label order.

Most focused editors accept an optional id, target, or both. A supplied pair must agree. Without selectors, ggaction first uses the current mark’s scale for that channel, then a unique channel scale across the program. Multiple marks sharing one scale remain unambiguous; multiple scale IDs require a selector. The scale must actually be bound to the named channel, so a position scale named color is rejected by editColorScale. Use generic editScale for unattached named scales. Successful focused edits run through editScale, including shared mark, axis, grid, and legend refresh.

editStrokeScale requires target and does not accept id. Color and stroke use independent generated scale IDs. Authors may explicitly give them the same scale ID when every consumer has a compatible field type and color-scale family; focused stroke edits then refresh both sets of consumers.

Advanced authors can create a named unattached scale with the same complete type vocabulary:

Contextual fragment. Use an ES module with the imports, data, and prepared resource state described in this section. Resolve these names from setup in this fragment or section; alternatives branch from the same base.

const program = chart().createScale({
  id: "temperature",
  type: "sequential",
  domain: [0, 100],
  palette: { name: "viridis", count: 5 }
});

For sequential scales, count is the number of concrete gradient stops and must be an integer of at least 2. The same descriptor is accepted as range: { palette: { name: "viridis", count: 5 } }.

The accepted types are linear, log, pow, sqrt, symlog, time, band, point, ordinal, sequential, quantize, quantile, and threshold. A later encoding attachment validates whether the type, range, and fallback are compatible with that channel and mark.

Before an ordinal scale has a consumer, editScale({ range: ... }) accepts the same numeric pair, color array, dash patterns, or palette range object as createScale. Attaching a consumer applies that channel’s range restrictions; position scales continue to reject palette ranges.

Generic editScale allows id to be omitted when the current scale or the program’s only scale is unambiguous. At least one editable option is required. Use "auto" to reset domain or range; omission preserves the current value. Quantitative position scales can change atomically between linear, log, pow, sqrt, and symlog:

Contextual fragment. Use an ES module with the imports, data, and prepared resource state described in this section. Caller-provided receivers: program. Resolve these names from setup in this fragment or section; alternatives branch from the same base.

const logarithmic = program.editScale({ id: "x", type: "log", base: 10 });

Categorical bar positions use band; categorical point and rule positions use point. Both preserve first-appearance domain order. A band scale exposes a non-zero slot width and accepts paddingInner, paddingOuter, and align. A point scale exposes zero bandwidth and accepts padding and align.

Contextual fragment. Use an ES module with the imports, data, and prepared resource state described in this section. Caller-provided receivers: program. Resolve these names from setup in this fragment or section; alternatives branch from the same base.

program.encodeX({
  field: "country",
  fieldType: "nominal",
  scale: { type: "band", paddingInner: 0.2, paddingOuter: 0.1 }
});

align is between 0 and 1; both padding families are non-negative and paddingInner is less than 1. editScale rematerializes all connected marks and guides. A band can be shared by bars and point centers, but changing it to point is rejected while a bar requires its bandwidth.

removeScale({ id })

Use the Full-only removal action to delete an unattached named scale:

Contextual fragment. Use an ES module with the imports, data, and prepared resource state described in this section. Caller-provided receivers: program. Resolve these names from setup in this fragment or section; alternatives branch from the same base.

const cleaned = program.removeScale({ id: "temporaryColor" });

The ID is required. Every mark encoding, offset, Parallel dimension, axis, grid, legend, and retained authoring recipe is checked before deletion. A live reference rejects with its owner and exact path. A current-scale pointer alone is cleared and does not block deletion. On success, the semantic definition and its resolved cache entry are removed together; existing graphics and layout are left exactly as authored. The action does not rewire consumers, infer a replacement, cascade, or operate across concat children.

Focused scale families

Position scalesLinear, transformed, time, band, and point mappings. Ordinal and dash scalesStable categorical domains and stroke-dash ranges. Continuous colorNamed palettes and continuous quantitative color. Discretized colorQuantize, quantile, and threshold classes. Missing valuesUnknown fallbacks and compound-grain limits.

Errors and limitations

One scale cannot be shared across unrelated channels. Color and stroke are the supported appearance-sharing exception and still keep independent IDs by default. Explicit domains must contain every observed value required by ordinal consumers. A successful edit rematerializes connected marks and guides; a failed edit leaves the earlier immutable program unchanged.

Scale type transitions reject incompatible field types, channels, or mark grains before changing the immutable program. Quantitative Point, aggregate Bar, and Rect color can transition between sequential, log, symlog, and discretized scales. A compatible gradient or interval legend on any of the four edges changes family in the same action, retaining its edge, alignment and common text/layout settings. Custom family-specific settings require explicit removal and recreation; see color transitions.

Position encodings · Series encodings · Semantic and graphical state · Troubleshooting

Count-based nice belongs to linear position scales. Opacity and stroke-width encoding scales retain boolean nice; categorical dash fallbacks apply only to Cartesian lines and rules, not Parallel or Polar facades.

Documentation provenance

293 actions · sha256:e755591a2e5f5e2b494867583876e28020a478c5220b77bdc6ad469a6224891d