Layered Charts¶
bar, line, area, and scatter charts accept a layers: field to overlay
additional marks — for example a bar and a line — on one shared x-axis.
They're the way to build combo charts and dual-axis charts, where two
series with very different scales (say, revenue in dollars and a conversion
rate in percent) each get their own y-axis instead of one squashing the other.
There is no separate type: layered — the base chart is a real chart type
(bar, line, area, or scatter) that sets the primary mark, x, y, and
query; additional marks go in layers:.
Minimum Required for a Combo Chart¶
| Dataface field | Notes |
|---|---|
type |
bar, line, area, or scatter — the base (primary) mark. |
x |
Shared x-axis column across the base and every layer. |
y |
Base chart's own y column. |
layers |
List of overlay layers. Each layer needs its own type (bar, line, area, or scatter) and y. |
Minimum Example¶
queries: monthly_funnel: columns: [month, revenue, conversion_pct] values: - ["2026-01", 42000, 3.1] - ["2026-02", 51000, 3.4] - ["2026-03", 68000, 4.2] - ["2026-04", 60000, 3.9] charts: revenue_vs_conversion: query: monthly_funnel type: bar title: Revenue vs. Conversion Rate x: month y: revenue layers: - type: line y: conversion_pct rows: - revenue_vs_conversion
Without any extra configuration, the base chart and its layers share a single y-axis — which works fine when the series are on a similar scale, but squashes one series flat when they aren't (as with revenue vs. a percentage above).
Dual Y-Axis¶
Each layer can pin its y-axis to a side with axis_y.position: left or
right. When a layer resolves to a different side than the base chart,
Dataface automatically gives them independent scales — one axis on the left,
one on the right — so neither series gets squashed.
queries:
monthly_funnel:
columns: [month, revenue, conversion_pct]
values:
- ["2026-01", 42000, 3.1]
- ["2026-02", 51000, 3.4]
- ["2026-03", 68000, 4.2]
- ["2026-04", 60000, 3.9]
charts:
revenue_vs_conversion:
query: monthly_funnel
type: bar
title: Revenue vs. Conversion Rate
x: month
y: revenue
y_label: Revenue ($)
layers:
- type: line
y: conversion_pct
axis_y:
position: right
title: Conversion (%)
rows:
- revenue_vs_conversion
| Dataface field | Allowed values | Notes |
|---|---|---|
layers[].axis_y.position |
left or right |
Which side this layer's y-axis renders on. |
layers[].axis_y.title |
String | Axis title override for this layer's side. |
Only setting title (with no position, or the same position as the base
chart) keeps a single shared axis — the split only happens once a layer
actually disagrees on side with the base.
Layer Fields¶
Each entry under layers: accepts:
| Field | Notes |
|---|---|
type |
bar, line, area, or scatter. |
y |
Layer's y column (required). |
x |
Layer's own x-values, extending the base x-scale. Defaults to the base chart's x. |
query |
Overrides the base chart's query for this layer — lets a layer plot a different table/query (e.g. actuals vs. targets). |
label |
Legend label for this layer's series. |
color |
Data channel (bare field name) for per-datum color. |
axis_y |
position (left/right) and title — see Dual Y-Axis above. |
style |
Marks-only style patch for this layer. |
Vega-Lite encoding: is not allowed inside a layer — use the typed channels
above instead.
Group an Overlay by Color¶
Set a layer's color to a column from that layer's query to draw one colored
series per value. This is useful when the base chart shows a total while the
overlay breaks a target or benchmark out by region, team, or segment.
queries:
monthly_revenue:
columns: [month, revenue]
values:
- ["2026-01", 42000]
- ["2026-02", 51000]
- ["2026-03", 68000]
regional_targets:
columns: [month, target, region]
values:
- ["2026-01", 40000, East]
- ["2026-01", 38000, West]
- ["2026-02", 50000, East]
- ["2026-02", 47000, West]
- ["2026-03", 65000, East]
- ["2026-03", 61000, West]
charts:
revenue_vs_regional_targets:
query: monthly_revenue
type: bar
title: Revenue and Regional Targets
x: month
y: revenue
layers:
- type: line
query: regional_targets
y: target
color: region
rows:
- revenue_vs_regional_targets
The line layer is grouped by region; its East and West series receive
separate colors and legend entries. The base revenue bars remain a separate
series in the same legend.
Target & Reference Lines¶
Dataface automatically draws certain reference lines on bar, line, area, and layered charts — you don't author these, but it helps to know when they appear so they aren't mistaken for a data mark:
| Reference line | When it appears |
|---|---|
| Zero baseline | Always, on bar and layered charts. On line and area charts, whenever the data straddles zero (or the y-axis scale hasn't explicitly turned zero-anchoring off with scale.zero: false). |
| 0% / 100% top lines | Normalize-stacked bar and area charts (stack: normalize), in place of the plain zero baseline. |
| 100% unity line | Line, area, and layered charts formatted as a percent (number_format: percent_whole or similar), when the chart's effective y-domain reaches 1.0 — the common guide for retention, conversion, and NRR charts stored as ratios. |
These reference lines pick up their color and stroke width from style.axis_y.grid.zero (see Grid Lines), and are suppressed entirely when style.axis_y.grid.visible: false.
There is currently no way to author a fixed, arbitrary threshold or target line — for example a sales quota drawn as a flat line across the whole chart — beyond the automatic reference lines above.