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.