--- url: https://chartbuddy.io/embed/docs/configuration/arrows.md --- > Docs index: [llms.txt](https://chartbuddy.io/embed/docs/llms.txt) # Arrows Difference, CAGR, and level arrows live under `annotations` as **id-keyed maps**. Each entry is one arrow. | Kind | chartData path | What it shows | |---|---|---| | Difference | `annotations.differenceArrows.` | Absolute and/or % delta between two columns | | CAGR | `annotations.cagrs.` | Compound annual growth between two columns | | Level | `annotations.levelArrows.` | Horizontal compare between two anchor points | CAGR and difference use simple column indices. Level arrows need fuller `anchorPoints` geometry (`barTop` / `linePoint`, `columnIndex`, `seriesIndex`), so they are the easiest of the three to mis-wire by hand. ## Chart type support | Kind | Stacked / clustered bar | 100% stacked | Waterfall | Combo (bars) | Line / area | Pie / scatter / mekko | |---|---|---|---|---|---|---| | Difference | yes | yes\* | yes | yes | no | no | | CAGR | yes | no | yes | yes | no | no | | Level | yes | yes | yes | yes | yes | no | \*Engine can draw difference on 100% stacked; the editor add menu may not offer it on every type. Combo arrows attach to **bar** series only. ## Shared styling These fields are common across the three arrow maps: | Field | Type | Typical default | Notes | |---|---|---|---| | `lineColor` | string | `'#000000'` | Stroke color | | `lineWidth` | number | `1.5` | Stroke width (px) | | `lineStyle` | string | `'solid'` | `solid` · `dashed` · `densely-dashed` · `dotted` · `dash-dot` | | `labelShape` | string | `'pill'` | `none` · `rounded` · `pill` · `circle` | | `fontSize` | number | `8`–`10` | Label size (pt) | | `chartType` | string | current type | Snapshotted when the arrow was created | Category anchors (`start` / `end` / `columnIndex`) are **0-based column indices** in `seriesData` (after the header row / label column). ### Labels `displayMode` (and default `labelParts`) drive the **computed** absolute / % text. Optional custom copy uses `label` on level arrows, and `text` or `label` on difference / CAGR arrows. If you set custom text **and** leave a computed mode active, both can render (for example `+36` next to another `+36`, or `Expansion` plus a computed `+15%`). For custom text only: ```js label: 'Expansion', labelParts: ['label'], ``` To show custom text plus a computed part: `labelParts: ['label', 'percentage']` (or `'absolute'`). ### Layout offsets Fields like `verticalLinePositionIndex`, `horizontalYOffset`, and CAGR’s `startYOffset` / `endYOffset` are usually set by dragging in edit mode. When hand-authoring, start with `null` / omit offsets where possible, or copy them from an exported config. ## Difference arrows (`annotations.differenceArrows`) U-shaped connector between two columns. Label is usually a computed delta (`displayMode` / `labelParts`). ```js annotations: { differenceArrows: { q1VsQ2: { start: 0, end: 1, startSeries: null, // null = bar tops / stack totals endSeries: null, displayMode: 'percentage', chartType: 'stackedBar', lineColor: '#111827', lineWidth: 1.5, lineStyle: 'solid', labelShape: 'pill', }, }, } ``` | Field | Meaning | |---|---| | `start` / `end` | Column indices | | `startSeries` / `endSeries` | Series index, or `null` for tops / totals | | `displayMode` | `percentage` · `absolute` · `absoluteWithPercentage` · `percentageWithAbsolute` | | `labelParts` | Composite label parts (e.g. `['percentage']`, `['label','percentage']`) | | `label` / `text` | Optional custom text | | `numberFormatAbsolute` / `numberFormatPercentage` | Per-arrow number specs — see [Number formats](https://chartbuddy.io/embed/docs/configuration/number-formats.md) | | `horizontalYOffset` | Drag offset for the horizontal segment | ## CAGR arrows (`annotations.cagrs`) Diagonal growth-rate arrow. Duration comes from the category span (`|end - start|`), or the series span when comparing within a column. ```js annotations: { cagrs: { fySpan: { start: 0, end: 5, startSeries: null, endSeries: null, chartType: 'stackedBar', lineColor: '#000000', lineWidth: 1.5, lineStyle: 'solid', startYOffset: -8, endYOffset: -8, }, }, } ``` | Field | Meaning | |---|---| | `start` / `end` | Column indices | | `startSeries` / `endSeries` | Series index, or `null` for tops / totals | | `text` | Optional custom label override | | `startYOffset` / `endYOffset` | Per-endpoint vertical drag offsets | On waterfall, CAGR uses cumulative totals (not step deltas). ## Level arrows (`annotations.levelArrows`) Horizontal comparison between two explicit anchors (bar tops, segments, or line points). ```js annotations: { levelArrows: { seriesCompare: { anchorPoints: [ { columnIndex: 4, seriesIndex: 2, type: 'barTop' }, { columnIndex: 5, seriesIndex: 2, type: 'barTop' }, ], verticalLinePositionIndex: null, // or a snap index from the editor displayMode: 'percentage', chartType: 'stackedBar', lineColor: '#000000', lineWidth: 1.5, lineStyle: 'solid', label: '', labelShape: 'pill', fontSize: 8, }, }, } ``` Custom label only (no computed delta beside it): ```js label: '+36', labelParts: ['label'], ``` Line-chart variant: ```js anchorPoints: [ { columnIndex: 0, seriesIndex: 0, type: 'linePoint' }, { columnIndex: 4, seriesIndex: 0, type: 'linePoint' }, ] ``` | Field | Meaning | |---|---| | `anchorPoints` | Two anchors: `columnIndex`, `seriesIndex` (or `'total'` / `null`), optional `type` | | `verticalLinePositionIndex` | Snap index for the vertical rail / label (`null` = engine default) | | `displayMode` / `labelParts` | What the label shows | | `label` | Optional custom text | `levelArrows` is not seeded in defaults; the bag appears when the first level arrow is added. ## Authoring tip Add arrows in edit mode (or Hub MCP `manage_annotations`), then snapshot with `getChartData()` / `exportConfig()`. That picks up anchors and layout offsets without guessing snap indices. See also [Level lines](https://chartbuddy.io/embed/docs/configuration/level-lines.md).