Skip to content

Sector Charts

Sector charts divide a whole into angular parts, making the total-and-parts relationship visually explicit; this family includes pie charts, donut charts, and polar-area charts. They are most useful when showing simple composition with a small number of categories, especially when the main message is share of a whole rather than precise comparison between similar values.

dbt Charts sector charts use a small set of top-level shorthand fields. In most cases, you only need query, type: pie, theta, and color to get started.

A chart's control surface is the full set of authored properties available on a single chart. In dbt Charts, that surface is primarily top-level chart fields plus the typed style object.

This page demonstrates every sector-specific authored capability with at least one runnable example. For the field-level source of truth (default ownership and the shared style properties every chart family inherits: palette, border, padding, font, tooltip) see the YAML Schema Reference.


A Complete Sector Chart

The full package: a donut that reports a formatted grand total in the center and labels every slice with its category, share of the whole, and value. The rest of the page breaks these capabilities apart field by field.

source: examples_db
style:
  frame:
    width: 700
charts:
  revenue_share_hero:
    query:
      sql: |
        SELECT category, SUM(revenue) AS revenue FROM ecommerce_orders GROUP BY category ORDER BY revenue DESC
    type: donut
    title: Revenue by Category
    theta: revenue
    color: category
    total:
      label: Total Revenue
    style:
      total:
        value:
          format: "$,.0f"
      marks:
        slice:
          labels:
            template: "{{ '{:.0%}'.format(percent) }} {{ category }}\n{{ '${:,.0f}'.format(revenue) }}"
rows:
  - revenue_share_hero
$8,671,498Total Revenue47% Electronics$4,098,27635% Accessories$3,008,06818% Tools$1,565,154Revenue by Category Data as of 14:25 UTC on 6 Oct 2026 made withdbt Charts
▶

Minimum Required for a Sector Chart

These are the minimum fields required to render a basic sector chart in dbt Charts.

dbt Charts field Maps to Vega-Lite Allowed values Notes
query data.values Query name or query reference Supplies the dataset.
type: pie mark.type: "arc" Literal pie Selects the standard pie form.
theta encoding.theta.field Field name Numeric field that sets wedge size.
color encoding.color.field Field name Category field that splits the whole into slices.

Minimum Example

source: examples_db
style:
  frame:
    width: 700
charts:
  revenue_share_minimal:
    query:
      sql: |
        SELECT category, SUM(revenue) AS revenue FROM ecommerce_orders GROUP BY category ORDER BY revenue DESC
    type: pie
    title: Revenue Share by Category
    theta: revenue
    color: category
rows:
  - revenue_share_minimal
47% Electronics4,098,27635% Accessories3,008,06818% Tools1,565,154Revenue Share by Category Data as of 14:25 UTC on 6 Oct 2026 made withdbt Charts
▶

Top-Level Chart Fields

These are the top-level chart properties you set directly on a sector chart before you get into nested properties under style.

dbt Charts field Maps to Vega-Lite Allowed values Notes
query data.values Query name or query reference Query results become the plotted dataset.
type mark.type: "arc" pie or donut donut is shorthand for a pie with an inner radius (see style.inner_radius).
theta encoding.theta.field Field name Required wedge-size field.
color encoding.color.field Field name Category field that splits the whole into slices.
title title.text String Chart title.
subtitle title.subtitle String Secondary line displayed under the title.
notes metadata String AI search/context metadata. Never rendered.
link drill-down URL URL template string Click-through target; {{ column }} tokens interpolate per slice.
total center total slot {visible, label} Donut center total: the summed value with an optional caption. The number format is styled separately at style.total.value.format.
labels per-slice text {template, where} Direct per-slice labels. template is a Jinja string over each row's columns plus the injected percent, value, color, and total; where is a Jinja expression that filters which slices are labeled. Omitting labels applies the default two-line template: percent and the slice name on the first line, value on the second.
height chart viewport height Positive number (pixels) Explicit chart height. Overrides style.aspect_ratio and the theme cascade when set.
width chart viewport width Positive number (pixels) Chart width. Pins the chart's slot in a rows: layout (capped at the row); in cols: and grid: it contributes to the dashboard's intrinsic width and the layout owns the final slot.

title and subtitle render above the chart. notes and link are non-visual: notes feeds AI search and tooling (never rendered), and link turns each slice into a click-through (here into that category's page). (The center total and its formatting are shown in A Complete Sector Chart above.)

source: examples_db
style:
  frame:
    width: 700
charts:
  revenue_share_composition:
    query:
      sql: |
        SELECT category, SUM(revenue) AS revenue FROM ecommerce_orders GROUP BY category ORDER BY revenue DESC
    type: pie
    title: Product Mix
    subtitle: Fiscal year to date
    notes: Share of total revenue by product category.
    link: "/category/{{ category }}"
    theta: revenue
    color: category
    style:
      marks:
        slice:
          labels:
            template: "{{ category }}"
rows:
  - revenue_share_composition
ElectronicsAccessoriesToolsProduct MixFiscal year to date Data as of 14:25 UTC on 6 Oct 2026 made withdbt Charts
▶

Slice Label Templates

Override the default template with style.marks.slice.labels.template. Following the default's convention, lead with percent. Three donuts on the same data: a compact percent-and-name line, a name over a currency-formatted value, and a where-filtered label that names only the largest slices.

source: examples_db
style:
  frame:
    width: 700
queries:
  by_category:
    sql: |
      SELECT category, SUM(revenue) AS revenue FROM ecommerce_orders GROUP BY category ORDER BY revenue DESC
charts:
  labels_compact:
    query: by_category
    type: donut
    title: Percent and Name
    theta: revenue
    color: category
    total:
      visible: false
    style:
      marks:
        slice:
          labels:
            template: "{{ '{:.0%}'.format(percent) }} {{ category }}"

  labels_currency:
    query: by_category
    type: donut
    title: Name and Value
    theta: revenue
    color: category
    total:
      visible: false
    style:
      marks:
        slice:
          labels:
            template: "{{ category }}\n{{ '${:,.0f}'.format(revenue) }}"

  labels_filtered:
    query: by_category
    type: donut
    title: Largest Slices Only
    theta: revenue
    color: category
    total:
      visible: false
    style:
      marks:
        slice:
          labels:
            template: "{{ category }}"
            where: "percent >= 0.3"
rows:
  - labels_compact
  - labels_currency
  - labels_filtered
47% Electronics35% Accessories18% ToolsPercent and NameElectronics$4,098,276Accessories$3,008,068Tools$1,565,154Name and ValueElectronicsAccessoriesLargest Slices Only Data as of 14:25 UTC on 6 Oct 2026 made withdbt Charts
▶

Style Fields

Use style for the sector-specific presentational surface: ring geometry, slice paint, and the center-total paint. Shared style properties every chart inherits (palette, border, padding, font, tooltip) live in the YAML Schema Reference.

dbt Charts field Maps to Vega-Lite Allowed values Notes
style.inner_radius mark.innerRadius 0–1 Hole-to-disk ratio. 0 (or omitted) is a solid pie; type: donut defaults it to 0.6. Set it explicitly for any other ring thickness.
style.aspect_ratio viewport ratio Positive number Width ÷ height of the chart viewport.
style.marks.slice arc mark paint {opacity, gap, corner_radius, stroke, labels} Per-slice mark paint and layout.
style.legend.position legend or table placement {edge, align} Where the key sits: edge is left, right, top or bottom; align is start, center or end along that edge. Unset, the key sits right or below depending on width. A pie key never overlays the wheel.
style.total center total paint {value, label} Typography of the donut center total's number (value, which also carries its format) and caption (label).

Ring Thickness

style.inner_radius sets any hole-to-disk ratio beyond the donut default of 0.6. 0.3 is a thick ring; values near 1 are a thin band.

source: examples_db
style:
  frame:
    width: 700
charts:
  revenue_share_ring:
    query:
      sql: |
        SELECT category, SUM(revenue) AS revenue FROM ecommerce_orders GROUP BY category ORDER BY revenue DESC
    type: pie
    title: Thick Ring
    theta: revenue
    color: category
    total:
      visible: false
    style:
      inner_radius: 0.3
      marks:
        slice:
          labels:
            template: "{{ category }}"
rows:
  - revenue_share_ring
ElectronicsAccessoriesToolsThick Ring Data as of 14:25 UTC on 6 Oct 2026 made withdbt Charts
▶

Viewport

style.aspect_ratio reshapes the chart viewport: its width ÷ height. Above 1 is wider than tall.

source: examples_db
style:
  frame:
    width: 700
charts:
  revenue_share_viewport:
    query:
      sql: |
        SELECT category, SUM(revenue) AS revenue FROM ecommerce_orders GROUP BY category ORDER BY revenue DESC
    type: pie
    title: Wide Viewport
    theta: revenue
    color: category
    style:
      aspect_ratio: 1.6
      marks:
        slice:
          labels:
            template: "{{ category }}"
rows:
  - revenue_share_viewport
ElectronicsAccessoriesToolsWide Viewport Data as of 14:25 UTC on 6 Oct 2026 made withdbt Charts
▶

Slice Paint

style.marks.slice paints the arcs: here noticeably rounded corners and a wide angular gap between slices.

source: examples_db
style:
  frame:
    width: 700
charts:
  revenue_share_slice_paint:
    query:
      sql: |
        SELECT category, SUM(revenue) AS revenue FROM ecommerce_orders GROUP BY category ORDER BY revenue DESC
    type: donut
    title: Rounded, Gapped Slices
    theta: revenue
    color: category
    total:
      visible: false
    style:
      marks:
        slice:
          corner_radius: 10
          gap: 0.05
          labels:
            template: "{{ category }}"
rows:
  - revenue_share_slice_paint
ElectronicsAccessoriesToolsRounded, Gapped Slices Data as of 14:25 UTC on 6 Oct 2026 made withdbt Charts
▶

Center-Total Styling

style.total styles the center-total typography. Here the total value is painted in a distinct accent color.

source: examples_db
style:
  frame:
    width: 700
charts:
  revenue_share_total_style:
    query:
      sql: |
        SELECT category, SUM(revenue) AS revenue FROM ecommerce_orders GROUP BY category ORDER BY revenue DESC
    type: donut
    title: Accented Total
    theta: revenue
    color: category
    total:
      label: Total
    style:
      total:
        value:
          format: "$,.0f"
          font:
            color: "#2563eb"
      marks:
        slice:
          labels:
            template: "{{ category }}"
rows:
  - revenue_share_total_style
$8,671,498TotalElectronicsAccessoriesToolsAccented Total Data as of 14:25 UTC on 6 Oct 2026 made withdbt Charts
▶

Slice Count and Density

How a sector chart labels itself is automatic and data-and-width aware; you do not author it. As slice count rises or the chart narrows, direct labels stop fitting, so the renderer switches presentation. Understanding the transitions is the difference between a chart that reads cleanly and one that collapses into unreadable callouts.

The rules, at a glance:

  • Wedges under 8% of the whole keep their slice and hover tooltip but drop their direct label: small slices don't get to crowd the disk.
  • Once the number of labelable wedges exceeds what fits around the disk (about 4 at a typical width, fewer when narrow), the chart renders as a donut plus an attached table that carries slice identity, color, and value.
  • Very narrow charts always use the attached table.

Small Slices Drop Their Label

Four slices, one of them under 8%. The small wedge still renders (and its value is in the tooltip), but it gets no direct label rather than cluttering the disk.

source: examples_db
style:
  frame:
    width: 700
queries:
  long_tail:
    type: values
    columns: [segment, revenue]
    values:
      - ["Enterprise", 50]
      - ["Mid-Market", 30]
      - ["SMB", 15]
      - ["Trial", 5]
charts:
  small_slices:
    query: long_tail
    type: donut
    title: Small Slices Suppressed
    theta: revenue
    color: segment
    total:
      visible: false
    style:
      marks:
        slice:
          labels:
            template: "{{ segment }}"
rows:
  - small_slices
EnterpriseMid-MarketSMBSmall Slices Suppressed Data as of 14:25 UTC on 6 Oct 2026 made withdbt Charts
▶

High Cardinality Attaches a Table

Eight products is more than fits as direct callouts, so the chart renders the donut with an attached table instead, each row keyed by slice color, with the value alongside.

source: examples_db
style:
  frame:
    width: 700
charts:
  by_product:
    query:
      sql: |
        SELECT product, SUM(units_sold) AS units FROM ecommerce_orders GROUP BY product ORDER BY units DESC
    type: donut
    title: Units Sold by Product
    theta: units
    color: product
rows:
  - by_product
290,380Total Units22% Widget B62,61218% Widget A51,89814% Gadget Y41,51012% Gadget X34,98611% Widget C31,52811% Tool Z30,967Units Sold by Product Data as of 14:25 UTC on 6 Oct 2026 made withdbt Charts
▶

Explicit Height and Width

height and width pin the chart's viewport in pixels, overriding style.aspect_ratio and the theme cascade. Use them when a sector chart needs a fixed footprint rather than one derived from the surrounding layout.

source: examples_db
style:
  frame:
    width: 400
charts:
  revenue_share_fixed_size:
    query:
      sql: |
        SELECT category, SUM(revenue) AS revenue FROM ecommerce_orders GROUP BY category ORDER BY revenue DESC
    type: pie
    title: Fixed 320x320 Viewport
    theta: revenue
    color: category
    height: 320
    width: 320
rows:
  - revenue_share_fixed_size
Fixed 320x320 Viewport 47%Electronics4.1 ​M35%Accessories3.01M18%Tools1.57M Data as of 14:25 UTC on 6 Oct 2026 made withdbt Charts
▶

Key Placement

style.legend.position places the key: the legend when one is shown (style.legend.visible: true), or the attached table when the chart attaches one. Here the legend sits left of the wheel and is centered down its height.

source: examples_db
style:
  frame:
    width: 900
charts:
  by_product_left:
    query:
      sql: |
        SELECT product, SUM(units_sold) AS units FROM ecommerce_orders GROUP BY product ORDER BY units DESC
    type: donut
    title: Legend Left, Centered
    theta: units
    color: product
    style:
      legend:
        visible: true
        position:
          edge: left
          align: center
rows:
  - by_product_left
290,380Total Units22% Widget B62,61218% Widget A51,89814% Gadget Y41,51012% Gadget X34,98611% Widget C31,52811% Tool Z30,967Widget BWidget AGadget YGadget XWidget CTool ZGadget ZTool AProductLegend Left, Centered Data as of 14:25 UTC on 6 Oct 2026 made withdbt Charts
▶

Field Labels

The legend title and tooltip rows derive their text from the column name. field_labels maps a column to the text to paint instead, written exactly as given; the donut's center total reads Total <label>. See Field Labels.

source: examples_db
charts:
  revenue_share_labeled:
    query:
      sql: |
        SELECT category, SUM(revenue) AS revenue FROM ecommerce_orders GROUP BY category ORDER BY revenue DESC
    type: donut
    title: Revenue Share
    theta: revenue
    color: category
    field_labels:
      revenue: Net revenue (USD)
      category: Product line
rows:
  - revenue_share_labeled
8,671,498Total Net revenue (USD)47% Electronics4,098,27635% Accessories3,008,06818% Tools1,565,154Revenue Share Data as of 14:25 UTC on 6 Oct 2026 made withdbt Charts
▶

Authored Surface

dbt Charts sector charts are authored with type: pie or type: donut plus top-level channels such as theta and color, the top-level total/labels fields, and the typed style: object. Arbitrary Vega-Lite spec, mark, encoding, config, transform, params, and composition blocks are rejected on the authored surface.