Skip to content

Chart Color Channels

Dataface supports data-driven styling via five chart-level channels: color, background, opacity, stroke.color, stroke.width. Each uses the {column, value, scale} grammar for continuous encoding.

Discrete, rule-driven styling lives in a separate top-level conditional_formatting: block on the chart (see Conditional (threshold) below).

Shorthand

A plain string binds a column to the channel in "series" mode:

color: segment

Equivalent to {column: segment}.

Literal

Fix a channel to a static value regardless of data:

color:
  value: "#1aff3c"

Or use the literal shorthand at chart.style.<channel>:

style:
  color: "#1aff3c"

Gradient scale

Map a numeric column to a continuous color or numeric range:

color:
  column: revenue
  scale:
    palette: ["#ffffff", "#0000ff"]
    min: 0
    max: 1000000

Omit min/max to auto-scale from data. palette must be list[str] for color channels (color, background, stroke.color) and list[float] for numeric channels (opacity, stroke.width).

Default opacity palette when omitted: [0.2, 1.0].

Conditional (threshold)

Discrete, rule-driven styling goes under the chart-level conditional_formatting: block, indexed by column name. Each column entry carries a single when: list of rules; each rule has exactly one predicate and one or more style outputs (background, font):

Predicate Shape Matches
eq: x any scalar values equal to x (bool/int cross-match guarded)
ne: x any scalar values not equal to x
lt / lte / gt / gte number one-sided numeric comparison (non-numeric never matches)
between: [low, high] two numbers, low <= high closed inclusive range
in: [v1, v2, ...] non-empty list equality against any list element
is_null: true boolean null/None values
is_null: false boolean any non-null value (including 0, "", false)
default: true boolean catch-all — fires when no other rule matched
chart:
  type: bar
  x: quarter
  y: arr
  conditional_formatting:
    arr:
      when:
        - gt: 1000000
          background: "#166534"
          font: {color: "#ffffff", weight: bold}
        - between: [0, 1000000]
          background: "#dcfce7"
        - is_null: true
          background: "#e5e7eb"
        - default: true
          background: "#fee2e2"
          font: {color: "#991b1b"}

Non-default rules are evaluated in list order; later matches override earlier ones for overlapping style keys. A default: true rule must be the last entry in its when: list and only fires when no other rule matched for the current row — it is a true fallback, not a last-match-wins override. Without a default:, rows that match nothing fall back to the chart's un-conditional styling.

Channel reference

Channel Chart types Value type
color All str (color)
background KPI, table str (color)
opacity All float [0, 1]
stroke.color Bar, line, area, arc str (color)
stroke.width Bar, line, area float

Tables

On type: table, the table renderer reads conditional_formatting rules directly from the chart-level block at render time — there is no per-column when: shape under style.columns. Per-channel color.scale / background.scale still lower to style.columns[<column>].scale because scale is a continuous encoding the renderer interpolates per cell.

KPI channels

On type: kpi, color controls the value text color and background controls the card fill; both support literal and gradient modes via the per-channel shape. Rule-driven overrides (mix of background and font color/weight per threshold) go under conditional_formatting:. Provide explicit min/max in gradient scale for KPIs — auto-domain over a single row always collapses.

Precedence

chart.<channel> (data-bound) wins over chart.style.<channel> (literal). Using a data-bound form under chart.style.<channel> is a validation error. conditional_formatting rules apply on top of the per-channel encoding for matching rows.