Skip to content

Adding Vega-Lite Settings To Dataface

This page documents the process for adding more Vega-Lite-backed options to Dataface over time.

Use it when you want to expose a new Vega-Lite capability through Dataface without:

  • mirroring the whole Vega-Lite schema
  • adding ad hoc dict passthroughs
  • hiding mapping logic in renderer helpers
  • creating one-off naming seams that nobody can explain later

For the surrounding architecture, also read Chart Rendering Architecture and Chart Object Model.

Core Rule

Do not start by copying a Vega-Lite field directly into random Dataface models.

Start by deciding whether the setting belongs in Dataface's owned style system, in chart semantics, or should remain upstream Vega-Lite behavior.

The path is:

Vega-Lite capability
-> decide whether Dataface should own it
-> add it to the right Dataface contract
-> compile it into StyleCompiled or chart contracts
-> map it explicitly to Vega-Lite output
-> test the main path

First Decision: Should Dataface Own This Setting?

Before adding anything, classify the setting.

Add It To Dataface Style

Use the style system when the setting is a presentation default or override that should be:

  • themeable
  • inheritable
  • overrideable at board or chart level
  • available through style

Examples:

  • axis label font size
  • title color
  • bar corner radius
  • line stroke width
  • chart background
  • view stroke or fill
  • palettes and range colors
  • chart inference defaults such as style.charts.inference.infer_zero_when_missing

Add It To Chart Semantics

Use chart contracts instead of style when the setting changes chart meaning or encoding behavior rather than presentation defaults.

Examples:

  • chart type
  • mark choice when it affects chart meaning
  • axis field assignment
  • sort semantics
  • stack behavior
  • inferred type or scale behavior

Do Not Add It Yet

Leave the setting upstream if:

  • Dataface does not need to own it yet
  • Vega-Lite's default is acceptable
  • the setting would only be used through raw passthrough for edge cases
  • there is no clear authored API for it yet

Not every Vega-Lite option needs to exist in Dataface immediately.

Second Decision: Where Does It Live In Dataface?

If Dataface should own the setting, place it in the right layer.

Authored Surface

Add it under style if it is a presentation option.

Examples:

style:
  charts:
    axis:
      label_font_size: 12
    bar:
      corner_radius: 4
    inference:
      infer_zero_when_missing: true

Use names that are clear in Dataface first. Do not contort the authored API just to mimic Vega-Lite naming exactly.

Patch Model

Add the field to the appropriate Compiled*Style class in style_model.py and expose it through the corresponding Compiled*StylePatch (generated via build_patch_model).

Current live authored-surface primitives in style.py:

  • TitleStyle — chart title font/anchor/overflow
  • ScaleStyle — chart-local scale config (band padding, etc.)
  • RangeStyle — chart-local palette/range overrides
  • CompiledAxisStylePatch, CompiledLegendStylePatch — axis/legend overlays

Compiled Model

Make sure the setting has a home in StyleCompiled.

Rules:

  • StylePatch is sparse and optional
  • StyleCompiled is authoritative compiled presentation state
  • only add fields Dataface intends to own

Output Model

Add the corresponding field to VegaLiteConfig if Dataface will emit it.

Do not use the output model as the authored model.

The Standard Implementation Process

Follow this order when adding a new setting.

1. Define The Dataface Meaning

Write down:

  • what the setting means in Dataface
  • whether it is board-level, chart-level, or both
  • whether it is inheritable
  • whether it belongs in style or chart semantics

If this cannot be explained in one or two sentences, the setting is probably not ready to add.

2. Add It To A Shared Style Primitive

Put the field on the smallest shared nested model that makes sense.

Good:

  • AxisStyle.label_font_size
  • BarStyle.corner_radius

Bad:

  • dumping unrelated fields onto one giant flat model
  • adding a Vega-Lite-only object that bypasses the shared primitive layer

3. Expose It Through StylePatch

Make the setting available to authored YAML and theme presets through the patch model.

This is the layer where sparse overrides live.

4. Compile It Into StyleCompiled

Make sure the compiled board-scoped style object can carry the setting after:

  • default style merge
  • preset inheritance
  • board-level overrides
  • chart-level overrides

This step is what turns a setting from "available" into "real."

If the setting is chart-specific, the resolved chart path should consume one effective chart style view, not repeated chart.style or face.style logic.

5. Map It Explicitly In style_to_vega_lite(...)

Add the field-to-field mapping deliberately.

Examples:

  • style.background -> Vega-Lite top-level background
  • style.charts.axis.label_font_size -> config.axis.labelFontSize
  • style.charts.bar.corner_radius -> config.bar.cornerRadius

Do not hide the mapping in unrelated render helpers.

6. Emit Only The Supported Vega-Lite Output

If Dataface has non-Vega sections such as:

  • style.charts.table
  • style.charts.kpi
  • markdown

they must not leak into Vega-Lite output.

style_to_vega_lite(...) should emit only the supported chart-relevant subset.

7. Add Tests At Three Levels

Every newly owned setting should be covered by tests that prove:

  1. The contract exists.
  2. The contract is used.
  3. The contract is enforced.

Concretely:

  1. Contract exists
  2. authored style accepts the setting
  3. StylePatch, StyleCompiled, and VegaLiteConfig know about it
  4. Contract is used
  5. board or chart style reaches the main render path
  6. the emitted Vega-Lite output includes the mapped field
  7. Contract is enforced
  8. unsupported values fail validation if appropriate
  9. regression tests catch accidental removal or bypass

Naming Rules

When naming the Dataface field:

  • prefer clear Dataface names over raw upstream names
  • keep naming consistent with nearby style primitives
  • avoid introducing aliases unless compatibility requires them
  • avoid shadow fields or inheritance tricks that rename upstream models

If the Vega-Lite name is awkward for authored YAML, use a better Dataface name and map it explicitly.

When Not To Add A Setting

Do not add a new Dataface-owned setting if:

  • it is only needed once and passthrough already handles it
  • it has unclear authored semantics
  • it belongs to chart meaning rather than style
  • it would force Dataface to mirror a large upstream sub-schema just to expose one niche field

The bar is not "can Vega-Lite do this?" The bar is "should Dataface own this as part of its deliberate authored contract?"

Anti-Patterns

Avoid these patterns:

  • adding random dict keys to compiled style blobs
  • making the generated upstream schema the runtime source of truth
  • hiding field translation in renderer-specific branches
  • adding a setting only to docs or only to a validator without wiring the main path
  • letting non-Vega sections bleed into Vega-Lite output

Short Checklist

When adding a new Vega-Lite-backed setting, verify all of these:

  • classify it first: style, semantics, or upstream-only
  • add it to the right shared primitive
  • expose it through StylePatch
  • compile it into StyleCompiled
  • make sure chart-local style produces one effective chart style view
  • map it in style_to_vega_lite(...)
  • keep non-Vega sections out of Vega-Lite output
  • add contract-exists, contract-used, and contract-enforced tests
  • update docs if the authored surface changed

If a proposed change skips one of these steps, it is probably incomplete.