Skip to content

Number formats ​

Every numeric surface uses the same spec shape. There is no separate sibling format bag.

js
{
  mode: 'custom', // or 'linked'
  source: undefined, // only when mode is 'linked'
  customFormat: {
    prefix: '',
    postfix: '',
    thousandSeparator: ',',
    decimalSeparator: '.',
    decimalPlaces: 0,
    forceDecimals: true,
    signDisplay: 'auto', // 'auto' | 'always' | 'parens'
    scaleDown: 0,
  },
}
FieldRole
mode'custom' — this surface owns customFormat. 'linked' — inherit another surface
sourceWhen linked: 'primaryRangeAxis' | 'dataLabels' | 'totalLabels'
customFormatThe formatting knobs (also kept as an unlink stash when linked)
prefix / postfixStrings before / after the number (postfix: '%' on percentage charts)
thousandSeparator / decimalSeparatorGrouping and decimal marks
decimalPlacesDigits after the decimal
forceDecimalsPad to decimalPlaces even with trailing zeros
signDisplay'auto' | 'always' | 'parens'
scaleDownScale exponent (0 = raw; higher values shorten large magnitudes)

Not a number format: category date patterns live at axes.<side>.labels.dateFormat.format (a string like 'MMM D, YYYY').


1. Axes ​

Value-axis tick labels. Path is always under the side that holds that axis for the current orientation.

SurfacePathNotes
Primary value axis (Y on vertical charts)axes.left.labels.numberFormatUsually the main “Y-axis” format hub
Primary value axis (Y on horizontal charts)axes.bottom.labels.numberFormatSame role after orientation flip
Secondary value axisaxes.right (vertical) or axes.top (horizontal) .labels.numberFormatDual-axis; typically mode: 'custom' only — see Secondary
Domain / category axisaxes.<side>.labels.numberFormatRarely used for numbers; mekko-style % domain ticks are the main case

Link targets elsewhere refer to the primary value axis as source: 'primaryRangeAxis' (not the side name).

js
axes: {
  left: {
    labels: {
      numberFormat: {
        mode: 'custom',
        customFormat: {
          prefix: '$',
          postfix: '',
          thousandSeparator: ',',
          decimalSeparator: '.',
          decimalPlaces: 0,
          forceDecimals: true,
          signDisplay: 'auto',
          scaleDown: 0,
        },
      },
    },
  },
}

2. Chart labels ​

Whole-chart label formats under annotations.

Data labels ​

SurfacePathDefault / notes
Main data-label formatannotations.dataLabels.numberFormatHub used by Absolute when Absolute is linked
Absolute partannotations.dataLabels.numberFormatAbsoluteDefaults to mode: 'linked', source: 'dataLabels'
Percentage partannotations.dataLabels.numberFormatPercentageDefaults to custom with postfix: '%'

Which part paints depends on annotations.dataLabels.labelParts (e.g. ['absolute'], ['percentage'], ['absolute','percentage']). Pie / donut slice labels also use these Absolute / Percentage helpers.

js
annotations: {
  dataLabels: {
    labelParts: ['absolute'],
    numberFormat: {
      mode: 'custom',
      customFormat: { /* … */ },
    },
    numberFormatAbsolute: {
      mode: 'custom', // or keep linked to 'dataLabels'
      customFormat: { /* … */ },
    },
    numberFormatPercentage: {
      mode: 'custom',
      customFormat: {
        prefix: '',
        postfix: '%',
        thousandSeparator: ',',
        decimalSeparator: '.',
        decimalPlaces: 0,
        forceDecimals: true,
        signDisplay: 'auto',
        scaleDown: 0,
      },
    },
  },
}

Stack / column totals ​

SurfacePathCharts
Totalsannotations.totals.numberFormatStacked bar / stackedBar100, mekko, bar mekko, waterfall

Not used on line or area (no stack totals). Changing the Y-axis does not change totals unless totals are linked to primaryRangeAxis.

js
annotations: {
  totals: {
    visible: true,
    numberFormat: {
      mode: 'custom',
      customFormat: { /* … */ },
    },
  },
}

3. Annotation overlays ​

Per-object formats. Each arrow / line has its own spec (not shared chart-wide).

Level lines ​

SurfacePath
Level-line value labelannotations.levelLines.<id>.labelNumberFormatSpec

Same shape as numberFormat. Default when unset: follow the primary Y-axis (linked → primaryRangeAxis). Lines bound to the secondary axis follow that axis’s custom format. See Level lines.

js
annotations: {
  levelLines: {
    target: {
      value: 70,
      labelNumberFormatSpec: {
        mode: 'custom',
        customFormat: { /* … */ },
      },
    },
  },
}

Arrows (difference, CAGR, level) ​

Each entry under these maps can carry Absolute and Percentage specs:

KindMap pathAbsolutePercentage
Differenceannotations.differenceArrows.<id>numberFormatAbsolutenumberFormatPercentage
CAGRannotations.cagrs.<id>numberFormatAbsolutenumberFormatPercentage
Levelannotations.levelArrows.<id>numberFormatAbsolutenumberFormatPercentage

Which of Absolute / Percentage appears depends on that arrow’s displayMode / labelParts. See Arrows.

js
annotations: {
  differenceArrows: {
    q1VsQ2: {
      start: 0,
      end: 1,
      displayMode: 'absoluteWithPercentage',
      numberFormatAbsolute: {
        mode: 'custom',
        customFormat: { /* … */ },
      },
      numberFormatPercentage: {
        mode: 'custom',
        customFormat: {
          prefix: '',
          postfix: '%',
          thousandSeparator: ',',
          decimalSeparator: '.',
          decimalPlaces: 1,
          forceDecimals: true,
          signDisplay: 'auto',
          scaleDown: 0,
        },
      },
    },
  },
}

4. Linking ​

Three hubs can be link sources: primary Y-axis (primaryRangeAxis), data labels (dataLabels), totals (totalLabels).

TargetCan link to
Primary Y-axisdataLabels, totalLabels
Data labelsprimaryRangeAxis, totalLabels
TotalsprimaryRangeAxis, dataLabels
Absolute / Percentage partsdataLabels, primaryRangeAxis, totalLabels
Arrow Absolute / PercentageprimaryRangeAxis, dataLabels, totalLabels
Level-line labelsprimaryRangeAxis, dataLabels, totalLabels

Hand-authored configs should prefer mode: 'custom' so the format does not depend on another surface.

js
// Totals follow the Y-axis
annotations: {
  totals: {
    numberFormat: {
      mode: 'linked',
      source: 'primaryRangeAxis',
      customFormat: { /* stash if unlinked later */ },
    },
  },
}

5. Quick path index ​

CategoryFull path
Primary / secondary / domain axisaxes.<left|right|bottom|top>.labels.numberFormat
Data labels (main)annotations.dataLabels.numberFormat
Data-label Absoluteannotations.dataLabels.numberFormatAbsolute
Data-label Percentageannotations.dataLabels.numberFormatPercentage
Stack totalsannotations.totals.numberFormat
Level-line labelannotations.levelLines.<id>.labelNumberFormatSpec
Difference arrow Absolute / %annotations.differenceArrows.<id>.numberFormatAbsolute / numberFormatPercentage
CAGR arrow Absolute / %annotations.cagrs.<id>.numberFormatAbsolute / numberFormatPercentage
Level arrow Absolute / %annotations.levelArrows.<id>.numberFormatAbsolute / numberFormatPercentage

Percentage chart types (stackedBar100, stackedArea100, mekko) often set customFormat.postfix: '%' on the surfaces you care about (usually the primary axis and/or totals).

Developer documentation for Chartbuddy Embed · Not the end-user Help Center · Help Center