# Bullet Chart

Canonical page: https://www.adaptabletools.com/docs/handbook-styled-column-bullet

- The Bullet Chart Style renders a compact, single-row chart in each cell of a numeric column
- Each cell shows the actual value as a **bar**
- Two optional extras include **qualitative bands** behind the bar (e.g. poor / ok / good), and a **target** marker
- The chart axis spans from the resolved minimum to the maximum of the band scale 
- Targets can be hard-coded, taken from another column, or computed across the column (`Col-Avg`, `Col-Median`)

The Bullet Chart is a [Styled Column](https://www.adaptabletools.com/docs/handbook-styled-column-overview/index.md) which enables **numeric** columns to display a [bullet graph](https://en.wikipedia.org/wiki/Bullet_graph) in each cell.

Bullet Chart Styles can only be applied to Numeric Columns

The Chart is designed to compare a single quantitative measure (the cell value) against 2 measures:

- a set of qualitative **bands** (the wizard requires at least one; the API allows none — see [Bands](#bands-cell-ranges))
- an optional **target** measure

- Bullet Charts work well for KPI columns where you want to answer, in one row of pixels, this question:
- *"How is this row's value doing against its target, and does it sit in the good or bad band?"*

### Bullet Chart vs Percent Bar: Which Styled Column should I use?

Bullet Chart and Percent Bar look superficially similar but answer different questions:

|                              | Percent Bar                               | Bullet Chart                                                             |
| ---------------------------- | ----------------------------------------- | ------------------------------------------------------------------------ |
| Primary question             | "where does this value sit on the scale?" | "is this value meeting its target, and which qualitative band is it in?" |
| `CellRanges` colour the...   | bar                                       | background bands                                                         |
| Target marker                | not supported                             | first-class concept (`TargetProperties`)                                 |
| Multiple bands shown at once | no - only the matching one                | yes - all bands always visible                                           |
| Negative values              | supported via `ColumnComparison`          | supported natively via `Origin: 'Auto' \| 'Zero'`                        |

- Bullet Chart also supports `ColumnComparison` on `BulletChartStyle` in API state (for axis min/max)
- However the UI Wizard does not expose it — use **Origin** in the UI, or set `ColumnComparison` in `initialState`.

- If you have a clear target value to compare against (e.g. "actual vs forecast", "AUM vs limit") use a Bullet Chart
- If you just want to visualise where a value sits on a scale, Percent Bar is simpler

## Anatomy

A Bullet Chart cell is composed of three layers, drawn back-to-front:

1. **Bands** - the qualitative scale (e.g. bad / good) drawn behind everything else using Cell Ranges
2. **Bar** - the actual value of the cell, rendered as a horizontal bar drawn on top of the bands
3. **Target marker(s)** - the comparative value, rendered as a marker on the bar

- The Bullet Chart's `CellRanges` colour the **background bands** and are drawn unconditionally
- This matches the bullet-graph convention of using the bands as the qualitative scale
- By contrast, the Percent Bar's `CellRanges` which colour the **bar** based on the cell value

**Example: Bullet Chart: Showcase**

Three contrasting Bullet Charts side-by-side

- This example shows 3 Bullet Charts side by side (more explanations below)
  - **GitHub Stars** — number bands (red / amber / green), fixed **Line** target at `100,000`, blue bar with the value shown below
  - **Closed PRs** — percentage bands in a teal palette, **Triangle** target at `Col-Avg` (orange), cyan bar with the value **merged** centred on top
  - **Weekly Issue Change** — signed bullet (`Origin: 'Auto'`), red / green bands either side of `0`, **Diamond** target at `0` (gold), purple bar with the value shown below

Demo project (Vanilla); the files without a marker are shown below:

```text
src/
  adaptableOptions.ts
  agGridModules.ts     [template]
  columnDefs.ts        [template]
  gridOptions.ts       [template]
  index.ts             [template]
  onAdaptableReady.ts  [template]
  rowData.ts           [template]
  styles.css           [template]
index.html             [template]
package.json           [template]
tsconfig.json          [template]
```

Files marked `[template]` are the same in every demo; see the [demo template](https://www.adaptabletools.com/llms/demo-template.md#vanilla).

**`src/adaptableOptions.ts`**

```ts
import {AdaptableOptions} from '@adaptabletools/adaptable';
import {WebFramework} from './rowData';

export const adaptableOptions: AdaptableOptions<WebFramework> = {
  primaryKey: 'id',
  adaptableId: 'Bullet Chart Showcase',
  initialState: {
    Dashboard: {
      ModuleButtons: ['StyledColumn'],
    },
    Theme: {CurrentTheme: 'dark'},
    Layout: {
      CurrentLayout: 'Standard Layout',
      Layouts: [
        {
          Name: 'Standard Layout',
          TableColumns: [
            'name',
            'github_stars',
            'language',
            'closed_pr_count',
            'has_pages',
            'week_issue_change',
            'open_issues_count',
            'closed_issues_count',
            'updated_at',
            'pushed_at',
          ],
          ColumnSorts: [
            {
              ColumnId: 'github_stars',
              SortOrder: 'Desc',
            },
          ],
          AutoSizeColumns: true,
        },
      ],
    },
    StyledColumn: {
      StyledColumns: [
        {
          ColumnId: 'github_stars',
          Name: 'GitHub Stars KPI',
          BulletChartStyle: {
            RangeValueType: 'Number',
            CellRanges: [
              {Min: 'Col-Min', Max: 'Col-Avg', Color: 'rgba(220, 53, 69, 0.35)'},
              {Min: 'Col-Avg', Max: 100000, Color: 'rgba(255, 193, 7, 0.35)'},
              {Min: 100000, Max: 'Col-Max', Color: 'rgba(40, 167, 69, 0.35)'},
            ],
            TargetProperties: {
              Target: 100000,
              Marker: {
                Shape: 'Line',
                Color: 'var(--ab-color-foreground, #e2e2e2)',
                Size: 2,
              },
            },
            Bar: {Color: '#3b82f6', Height: 8},
            CellTextProperties: {
              CellTextLayout: {
                CellValue: {Horizontal: 'Right', Vertical: 'Below'},
              },
            },
          },
        },
        {
          ColumnId: 'closed_pr_count',
          Name: 'Closed PRs',
          BulletChartStyle: {
            RangeValueType: 'Percentage',
            CellRanges: [
              {Min: 0, Max: 33, Color: 'rgba(56, 189, 248, 0.18)'},
              {Min: 33, Max: 66, Color: 'rgba(20, 184, 166, 0.30)'},
              {Min: 66, Max: 100, Color: 'rgba(13, 148, 136, 0.45)'},
            ],
            TargetProperties: {
              Target: 'Col-Avg',
              Marker: {
                Shape: 'Triangle',
                Color: '#fb923c',
                Size: 10,
              },
            },
            Bar: {Color: '#22d3ee', Height: 8},
            CellTextProperties: {
              CellTextLayout: {
                CellValue: {Horizontal: 'Center', Vertical: 'Merged'},
              },
            },
          },
        },
        {
          ColumnId: 'week_issue_change',
          Name: 'Weekly Issue Change',
          BulletChartStyle: {
            Origin: 'Auto',
            RangeValueType: 'Number',
            CellRanges: [
              {Min: 'Col-Min', Max: 0, Color: 'rgba(244, 63, 94, 0.30)'},
              {Min: 0, Max: 'Col-Max', Color: 'rgba(34, 197, 94, 0.30)'},
            ],
            TargetProperties: {
              Target: 0,
              Marker: {
                Shape: 'Diamond',
                Color: '#fbbf24',
                Size: 10,
              },
            },
            Bar: {Color: '#a855f7', Height: 8},
            CellTextProperties: {
              CellTextLayout: {
                CellValue: {Horizontal: 'Left', Vertical: 'Below'},
              },
            },
          },
        },
      ],
    },
  },
};
```

## Styling the Chart

There are many elements that can be defined and configured when creating a Bullet Chart Style.

### Bands

The qualitative bands behind the bar are defined using the `CellRanges` property.

Each band has 3 properties:

- a minimum value
- a maximum value
- an associated colour

There are 2 types of Ranges supported (just like with the [Percent Bar Style](https://www.adaptabletools.com/docs/handbook-styled-column-percent-bar/index.md)):

- _Numeric_ - hard-coded numbers, or the dynamic endpoints `Col-Min`, `Col-Max`, `Col-Avg`, `Col-Median`
- _Percentage_ - 0-100 values that resolve against the column's own min/max

- A common pattern is to create 3 bands - red / amber / green
- These classify each row's value as good / average / bad against a fixed scale

`Col-Avg` and `Col-Median` evaluated across all visible rows, and are recomputed when row data changes.

- This makes them useful as automatic band thresholds (e.g. "below average is red, above average is green")
- But without having to know the actual numbers up front

#### Numeric Bands

Use **Number** bands when thresholds are absolute values — fixed numbers or dynamic endpoints such as `Col-Min`, `Col-Avg` and `Col-Max`.

**Example: Bullet Chart: Number Bands**

Qualitative bands and a fixed target

- **GitHub Stars** — three **Number** bands (`Col-Min` → `Col-Avg` → `100,000` → `Col-Max`) drawn behind the bar; all bands stay visible
- Fixed **target** at `100,000` (line marker); the bar shows each row's stars on the full axis
- Sorted descending on **GitHub Stars**

Demo project (Vanilla); the files without a marker are shown below:

```text
src/
  adaptableOptions.ts
  agGridModules.ts     [template]
  columnDefs.ts        [template]
  gridOptions.ts       [template]
  index.ts             [template]
  onAdaptableReady.ts  [template]
  rowData.ts           [template]
  styles.css           [template]
index.html             [template]
package.json           [template]
tsconfig.json          [template]
```

Files marked `[template]` are the same in every demo; see the [demo template](https://www.adaptabletools.com/llms/demo-template.md#vanilla).

**`src/adaptableOptions.ts`**

```ts
import {AdaptableOptions} from '@adaptabletools/adaptable';
import {WebFramework} from './rowData';

export const adaptableOptions: AdaptableOptions<WebFramework> = {
  primaryKey: 'id',
  adaptableId: 'Bullet Chart Number Bands',
  initialState: {
    Dashboard: {
      ModuleButtons: ['StyledColumn'],
    },
    Theme: {CurrentTheme: 'dark'},
    Layout: {
      CurrentLayout: 'Standard Layout',
      Layouts: [
        {
          Name: 'Standard Layout',
          TableColumns: [
            'name',
            'github_stars',
            'language',
            'closed_pr_count',
            'open_issues_count',
            'closed_issues_count',
            'week_issue_change',
            'has_projects',
            'has_pages',
            'updated_at',
            'pushed_at',
          ],
          ColumnSorts: [
            {
              ColumnId: 'github_stars',
              SortOrder: 'Desc',
            },
          ],
          AutoSizeColumns: true,
        },
      ],
    },
    StyledColumn: {
      StyledColumns: [
        {
          ColumnId: 'github_stars',
          Name: 'GitHub Stars KPI',
          BulletChartStyle: {
            RangeValueType: 'Number',
            CellRanges: [
              {
                Min: 'Col-Min',
                Max: 'Col-Avg',
                Color: 'rgba(220, 53, 69, 0.35)',
              },
              {
                Min: 'Col-Avg',
                Max: 100000,
                Color: 'rgba(255, 193, 7, 0.35)',
              },
              {
                Min: 100000,
                Max: 'Col-Max',
                Color: 'rgba(40, 167, 69, 0.35)',
              },
            ],
            TargetProperties: {
              Target: 100000,
              Marker: {
                Shape: 'Line',
                Color: 'var(--ab-color-foreground, #e2e2e2)',
                Size: 2,
              },
            },
            Bar: {Color: '#07c', Height: 8},
          },
        },
      ],
    },
  },
};
```

#### Percentage Bands

Use **Percentage** bands when each band is a slice of the column's value span (`0`–`100`), so you can label "bottom third / middle / top third" without knowing the underlying numbers.

**Example: Bullet Chart: Percentage Bands**

Percentage bands and a column-average target

- **Closed PRs** — three **Percentage** bands (`0`–`33`, `33`–`66`, `66`–`100`) resolved against the column's value span
- **Target** is `Col-Avg` (triangle marker) — recomputes when visible data changes

Demo project (Vanilla); the files without a marker are shown below:

```text
src/
  adaptableOptions.ts
  agGridModules.ts     [template]
  columnDefs.ts        [template]
  gridOptions.ts       [template]
  index.ts             [template]
  onAdaptableReady.ts  [template]
  rowData.ts           [template]
  styles.css           [template]
index.html             [template]
package.json           [template]
tsconfig.json          [template]
```

Files marked `[template]` are the same in every demo; see the [demo template](https://www.adaptabletools.com/llms/demo-template.md#vanilla).

**`src/adaptableOptions.ts`**

```ts
import {AdaptableOptions} from '@adaptabletools/adaptable';
import {WebFramework} from './rowData';

export const adaptableOptions: AdaptableOptions<WebFramework> = {
  primaryKey: 'id',
  adaptableId: 'Bullet Chart Percentage Bands',
  initialState: {
    Dashboard: {
      ModuleButtons: ['StyledColumn'],
    },
    Theme: {CurrentTheme: 'dark'},
    Layout: {
      CurrentLayout: 'Standard Layout',
      Layouts: [
        {
          Name: 'Standard Layout',
          TableColumns: [
            'name',
            'closed_pr_count',
            'language',
            'github_stars',
            'open_issues_count',
            'closed_issues_count',
            'week_issue_change',
            'has_projects',
            'has_pages',
            'updated_at',
            'pushed_at',
          ],
          AutoSizeColumns: true,
        },
      ],
    },
    StyledColumn: {
      StyledColumns: [
        {
          ColumnId: 'closed_pr_count',
          Name: 'Closed PRs percentage bands',
          BulletChartStyle: {
            RangeValueType: 'Percentage',
            CellRanges: [
              {Min: 0, Max: 33, Color: 'rgba(220, 53, 69, 0.35)'},
              {Min: 33, Max: 66, Color: 'rgba(255, 193, 7, 0.35)'},
              {Min: 66, Max: 100, Color: 'rgba(40, 167, 69, 0.35)'},
            ],
            TargetProperties: {
              Target: 'Col-Avg',
              Marker: {
                Shape: 'Triangle',
                Color: 'var(--ab-color-foreground, #e2e2e2)',
                Size: 8,
              },
            },
            Bar: {Color: 'var(--ab-color-accent, #07c)', Height: 8},
          },
        },
      ],
    },
  },
};
```

### Chart Axis

For each cell, AdapTable resolves the chart axis used to position the bar and markers.

The logic differs based on whether Cell Ranges are provided (the normal case and required in the wizard) or are omitted (possible only in AdapTable Initial State or when using the API):

| `CellRanges` | Chart axis                                                                                                                                                        | Background                                          |
| ------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------- |
| **Set**      | Resolved **min of the first band** → **max of the last band**. All bands are drawn on that span; the bar and target markers are positioned with `clamp` along it. | Coloured **bands** behind the bar                   |
| **Omitted**  | Column **Col-Min** → **Col-Max** across visible rows                                                                                                              | Flat **`BackColor`** (default transparent) no bands |

- When you omit Cell Ranges, set `BackColor` to tint the chart area
- Use this API-only pattern when you want a bullet bar + target without a qualitative scale

- This is why a value can sit in the "green" band visually while the bar is pinned to the end of the track
- The bar reflects position on the **full axis**, not "which band matched" (unlike Percent Bar)

### Bar

The cell's value is rendered as a filled bar on top of the bands.

The `Bar` group contains 2 properties to configure how this bar will look:

| Property     | Description                                                                                       |
| ------------ | ------------------------------------------------------------------------------------------------- |
| `Bar.Color`  | Colour of the bar (defaults to `var(--ab-color-primary)`)                                         |
| `Bar.Height` | Bar thickness in px when horizontal (defaults to `8`); interpreted as **bar width** when vertical |

### Targets

Each cell can have one or more **targets** displayed as markers on the bar.

Targets are configured through the `TargetProperties` group, which has 2 properties:

- `Target` — the resolvable target value, or an array of them
- `Marker` — the chart-level default marker style (shared by every target unless overridden per-target)

`Target` can be one of 4 potential values:

- a hard-coded number — e.g. `Target: 100000` (every row uses the same target)
- the id of another column — e.g. `Target: 'forecast'` (each row uses its own row's value)
- `'Col-Avg'` or `'Col-Median'` — a value computed across the column's visible rows
- an object: `{ Value: ..., Marker: { Shape, Color, Size }, Label? }` (a `BulletChartTargetDefinition`) to override the marker style or supply a **Label** shown in the tooltip beside that target's value

**Example: Bullet Chart: Column Target**

A different target on every row

- **Open Issues** — the bar is open issues; the **target** is **Closed Issues** on the same row (`TargetProperties: { Target: 'closed_issues_count' }`)
- Three numeric bands classify open-issue counts; the marker moves per row

Demo project (Vanilla); the files without a marker are shown below:

```text
src/
  adaptableOptions.ts
  agGridModules.ts     [template]
  columnDefs.ts        [template]
  gridOptions.ts       [template]
  index.ts             [template]
  onAdaptableReady.ts  [template]
  rowData.ts           [template]
  styles.css           [template]
index.html             [template]
package.json           [template]
tsconfig.json          [template]
```

Files marked `[template]` are the same in every demo; see the [demo template](https://www.adaptabletools.com/llms/demo-template.md#vanilla).

**`src/adaptableOptions.ts`**

```ts
import {AdaptableOptions} from '@adaptabletools/adaptable';
import {WebFramework} from './rowData';

export const adaptableOptions: AdaptableOptions<WebFramework> = {
  primaryKey: 'id',
  adaptableId: 'Bullet Chart Column Target',
  initialState: {
    Dashboard: {
      ModuleButtons: ['StyledColumn'],
    },
    Theme: {CurrentTheme: 'dark'},
    Layout: {
      CurrentLayout: 'Standard Layout',
      Layouts: [
        {
          Name: 'Standard Layout',
          TableColumns: [
            'name',
            'open_issues_count',
            'closed_issues_count',
            'language',
            'github_stars',
            'closed_pr_count',
            'week_issue_change',
            'has_projects',
            'has_pages',
            'updated_at',
            'pushed_at',
          ],
          AutoSizeColumns: true,
        },
      ],
    },
    StyledColumn: {
      StyledColumns: [
        {
          ColumnId: 'open_issues_count',
          Name: 'Open Issues vs closed target',
          BulletChartStyle: {
            RangeValueType: 'Number',
            CellRanges: [
              {Min: 0, Max: 500, Color: 'rgba(220, 53, 69, 0.35)'},
              {Min: 500, Max: 1500, Color: 'rgba(255, 193, 7, 0.35)'},
              {Min: 1500, Max: 5000, Color: 'rgba(40, 167, 69, 0.35)'},
            ],
            TargetProperties: {
              Target: 'closed_issues_count',
              Marker: {
                Shape: 'Line',
                Color: '#f0e68c',
                Size: 2,
              },
            },
            Bar: {Color: '#5b9bd5', Height: 8},
          },
        },
      ],
    },
  },
};
```

#### Array of Targets

You can also pass an **array** as `TargetProperties.Target` to render several markers in the same cell.

This is useful for comparing against both a primary target and a stretch goal

```ts
TargetProperties: {
  Target: [
    { Value: 'forecast', Marker: { Shape: 'Line', Color: 'gray' } },
    { Value: 'stretch_target', Marker: { Shape: 'Diamond', Color: 'gold' } },
  ],
},
```

**Example: Bullet Chart: Multiple Targets**

Two markers in one cell

- **GitHub Stars** — `TargetProperties.Target` is an **array**: fixed goal (`100,000`, line) and **column median** (diamond), each with its own `Marker` and tooltip **Label**
- Hover for cell value and axis **percentage**; compare marker shapes side by side

Demo project (Vanilla); the files without a marker are shown below:

```text
src/
  adaptableOptions.ts
  agGridModules.ts     [template]
  columnDefs.ts        [template]
  gridOptions.ts       [template]
  index.ts             [template]
  onAdaptableReady.ts  [template]
  rowData.ts           [template]
  styles.css           [template]
index.html             [template]
package.json           [template]
tsconfig.json          [template]
```

Files marked `[template]` are the same in every demo; see the [demo template](https://www.adaptabletools.com/llms/demo-template.md#vanilla).

**`src/adaptableOptions.ts`**

```ts
import {AdaptableOptions} from '@adaptabletools/adaptable';
import {WebFramework} from './rowData';

export const adaptableOptions: AdaptableOptions<WebFramework> = {
  primaryKey: 'id',
  adaptableId: 'Bullet Chart Multiple Targets',
  initialState: {
    Dashboard: {
      ModuleButtons: ['StyledColumn'],
    },
    Theme: {CurrentTheme: 'dark'},
    Layout: {
      CurrentLayout: 'Standard Layout',
      Layouts: [
        {
          Name: 'Standard Layout',
          TableColumns: [
            'name',
            'github_stars',
            'language',
            'github_watchers',
            'closed_pr_count',
            'open_issues_count',
            'closed_issues_count',
            'week_issue_change',
            'has_projects',
            'has_pages',
            'updated_at',
            'pushed_at',
          ],
          ColumnSorts: [
            {
              ColumnId: 'github_stars',
              SortOrder: 'Desc',
            },
          ],
          AutoSizeColumns: true,
        },
      ],
    },
    StyledColumn: {
      StyledColumns: [
        {
          ColumnId: 'github_stars',
          Name: 'GitHub Stars dual targets',
          BulletChartStyle: {
            RangeValueType: 'Number',
            CellRanges: [
              {Min: 'Col-Min', Max: 'Col-Avg', Color: 'rgba(220, 53, 69, 0.3)'},
              {Min: 'Col-Avg', Max: 'Col-Max', Color: 'rgba(40, 167, 69, 0.3)'},
            ],
            TargetProperties: {
              Target: [
                {
                  Value: 100000,
                  Label: 'Goal',
                  Marker: {Shape: 'Line', Color: '#bdbdbd', Size: 2},
                },
                {
                  Value: 'Col-Median',
                  Label: 'Median',
                  Marker: {Shape: 'Diamond', Color: '#d4a017', Size: 9},
                },
              ],
            },
            Bar: {Color: '#07c', Height: 8},
            ToolTipText: ['CellValue', 'PercentageValue'],
          },
        },
      ],
    },
  },
};
```

### Marker Shapes

The Shape of the Marker can also be configured.

This is done using the `TargetProperties.Marker.Shape` property (or the `Marker.Shape` of a per-target `BulletChartTargetDefinition`), which supports 4 values:

- `'Line'` (the default) - a thin vertical line crossing the bar
- `'Triangle'` - a downward-pointing triangle
- `'Dot'` - a filled circle
- `'Diamond'` - a filled diamond

Markers use the unified `BarChartMarker` shape (`{ Shape, Color, Size }`) shared with [Range Bar](https://www.adaptabletools.com/docs/handbook-styled-column-range-bar/index.md) and [Percent Bar](https://www.adaptabletools.com/docs/handbook-styled-column-percent-bar/index.md).

The [Multiple Targets](#targets) demo shows **Line** and **Diamond** markers in the same cell.

### Negative Values

For columns with negative values, you can configure the bar to grow from a non-zero origin.

This is done via the `Origin` property :

| Value              | Behaviour                                                                                                                                  |
| ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------ |
| `'Auto'` (default) | Bar grows from the chart's leading edge unless any cell value or band endpoint is negative, in which case it grows from `0` (centred axis) |
| `'Zero'`           | Bar always grows from `0`; positive values extend toward **Max**, negative values extend toward **Min**                                    |
| `number`           | Bar grows from that fixed origin                                                                                                           |

`number` is only available when creating the Bullet Chart via API  or Initial State, and not in the UI Wizard

- For variance columns ("actual vs forecast") set `Origin` to `'Zero'`
- This renders a centred bar that extends to the right for over-performance and to the left for under-performance

**Example: Bullet Chart: Negative Values**

Centred axis for signed values

- **Issue Change** — `Origin: 'Auto'` with bands `Col-Min`…`0` and `0`…`Col-Max`; bar grows left or right from `0`
- **Target** at `0` (line); cell value shown **below** the chart
- Sorted ascending on **Issue Change**

Demo project (Vanilla); the files without a marker are shown below:

```text
src/
  adaptableOptions.ts
  agGridModules.ts     [template]
  columnDefs.ts        [template]
  gridOptions.ts       [template]
  index.ts             [template]
  onAdaptableReady.ts  [template]
  rowData.ts           [template]
  styles.css           [template]
index.html             [template]
package.json           [template]
tsconfig.json          [template]
```

Files marked `[template]` are the same in every demo; see the [demo template](https://www.adaptabletools.com/llms/demo-template.md#vanilla).

**`src/adaptableOptions.ts`**

```ts
import {AdaptableOptions} from '@adaptabletools/adaptable';
import {WebFramework} from './rowData';

export const adaptableOptions: AdaptableOptions<WebFramework> = {
  primaryKey: 'id',
  adaptableId: 'Bullet Chart Negative Values',
  initialState: {
    Dashboard: {
      ModuleButtons: ['StyledColumn'],
    },
    Theme: {CurrentTheme: 'dark'},
    Layout: {
      CurrentLayout: 'Standard Layout',
      Layouts: [
        {
          Name: 'Standard Layout',
          TableColumns: [
            'name',
            'week_issue_change',
            'language',
            'github_stars',
            'open_issues_count',
            'closed_issues_count',
            'closed_pr_count',
            'has_projects',
            'has_pages',
            'updated_at',
            'pushed_at',
          ],
          ColumnSorts: [
            {
              ColumnId: 'week_issue_change',
              SortOrder: 'Asc',
            },
          ],
          AutoSizeColumns: true,
        },
      ],
    },
    StyledColumn: {
      StyledColumns: [
        {
          ColumnId: 'week_issue_change',
          Name: 'Issue Change variance',
          BulletChartStyle: {
            Origin: 'Auto',
            RangeValueType: 'Number',
            CellRanges: [
              {Min: 'Col-Min', Max: 0, Color: 'rgba(220, 53, 69, 0.4)'},
              {Min: 0, Max: 'Col-Max', Color: 'rgba(40, 167, 69, 0.4)'},
            ],
            TargetProperties: {
              Target: 0,
              Marker: {
                Shape: 'Line',
                Color: 'var(--ab-color-foreground, #e2e2e2)',
                Size: 2,
              },
            },
            Bar: {Color: '#64b5f6', Height: 8},
            CellTextProperties: {
              CellTextLayout: {
                CellValue: {Horizontal: 'Left', Vertical: 'Below'},
              },
            },
          },
        },
      ],
    },
  },
};
```

### Vertical Bars

By default Bullet Charts are drawn horizontally (bands and bar run left-to-right). 

However it is possible to draw the chart **vertically** by setting the `Orientation` property to `'Vertical'`.

AdapTable will then render the Bullet Chart in vertical mode as follows:

- **Bands and the bar** stack vertically; lower values sit at the bottom of the cell.
- **Line markers** are drawn horizontally across the bar.
- **Triangle markers** rotate 90° so the apex points right.
- **`Bar.Height`** is reinterpreted as the bar's short-axis size (i.e. its width in vertical mode).

The same property is used deliberately, so that existing configurations migrate cleanly

```ts
BulletChartStyle: {
  Orientation: 'Vertical',
  RangeValueType: 'Percentage',
  CellRanges: [
    { Min: 0, Max: 50, Color: 'rgba(220, 53, 69, 0.35)' },
    { Min: 50, Max: 100, Color: 'rgba(40, 167, 69, 0.35)' },
  ],
  TargetProperties: {
    Target: 'Col-Avg',
  },
  Bar: {
    // In vertical mode `Bar.Height` is interpreted as the bar's *width*
    // (its short axis); the long axis stretches to fill the cell.
    Height: 10,
  },
}
```

- Vertical bullet charts need a **tall row height** to be readable - we recommend at least 60px
- AG Grid row height is a per-grid setting; set `gridOptions.rowHeight` (or use `getRowHeight` for per-row variation)

**Example: Bullet Chart: Vertical**

Vertical orientation with taller rows

- **GitHub Watchers** — `Orientation: 'Vertical'`; percentage bands and `Col-Avg` target (triangle points right in vertical mode)
- `gridOptions.rowHeight` is **72** so the chart has room; `Bar.Height` is the bar **width** in vertical mode

Demo project (Vanilla); the files without a marker are shown below:

```text
src/
  adaptableOptions.ts
  agGridModules.ts     [template]
  columnDefs.ts        [template]
  gridOptions.ts
  index.ts             [template]
  onAdaptableReady.ts  [template]
  rowData.ts           [template]
  styles.css           [template]
index.html             [template]
package.json           [template]
tsconfig.json          [template]
```

Files marked `[template]` are the same in every demo; see the [demo template](https://www.adaptabletools.com/llms/demo-template.md#vanilla).

**`src/adaptableOptions.ts`**

```ts
import {AdaptableOptions} from '@adaptabletools/adaptable';
import {WebFramework} from './rowData';

export const adaptableOptions: AdaptableOptions<WebFramework> = {
  primaryKey: 'id',
  adaptableId: 'Bullet Chart Vertical',
  initialState: {
    Dashboard: {
      ModuleButtons: ['StyledColumn'],
    },
    Theme: {CurrentTheme: 'dark'},
    Layout: {
      CurrentLayout: 'Standard Layout',
      Layouts: [
        {
          Name: 'Standard Layout',
          TableColumns: [
            'name',
            'github_watchers',
            'language',
            'github_stars',
            'closed_pr_count',
            'open_issues_count',
            'closed_issues_count',
            'week_issue_change',
            'has_projects',
            'has_pages',
            'updated_at',
            'pushed_at',
          ],
          ColumnSorts: [
            {
              ColumnId: 'github_watchers',
              SortOrder: 'Desc',
            },
          ],
          AutoSizeColumns: true,
          ColumnSizing: {
            github_watchers: {Width: 72},
          },
        },
      ],
    },
    StyledColumn: {
      StyledColumns: [
        {
          ColumnId: 'github_watchers',
          Name: 'GitHub Watchers vertical',
          BulletChartStyle: {
            Orientation: 'Vertical',
            RangeValueType: 'Percentage',
            CellRanges: [
              {Min: 0, Max: 50, Color: 'rgba(220, 53, 69, 0.35)'},
              {Min: 50, Max: 100, Color: 'rgba(40, 167, 69, 0.35)'},
            ],
            TargetProperties: {
              Target: 'Col-Avg',
              Marker: {
                Shape: 'Triangle',
                Color: 'var(--ab-color-foreground, #e2e2e2)',
                Size: 8,
              },
            },
            Bar: {Color: '#07c', Height: 10},
            CellTextProperties: {
              CellTextLayout: {
                CellValue: {Horizontal: 'Left', Vertical: 'Below'},
              },
            },
          },
        },
      ],
    },
  },
};
```
**`src/gridOptions.ts`**

```ts
import {GridOptions, themeBalham} from 'ag-grid-enterprise';

import {rowData, WebFramework} from './rowData';
import {columnDefs} from './columnDefs';

export const gridOptions: GridOptions<WebFramework> = {
  theme: themeBalham,
  rowHeight: 72,
  defaultColDef: {
    resizable: true,
    sortable: true,
    editable: true,
    filter: true,
    floatingFilter: true,
  },
  columnDefs: columnDefs,
  rowData: rowData,
  sideBar: true,
  suppressMenuHide: true,
  columnMenu: 'new',
  cellSelection: true,
  statusBar: {
    statusPanels: [
      {statusPanel: 'agTotalRowCountComponent', align: 'left'},
      {statusPanel: 'agFilteredRowCountComponent'},
      {
        key: 'Center Panel',
        statusPanel: 'AdaptableStatusPanel',
        align: 'center',
      },
    ],
  },
};
```

## Styling the Cell

As with all Styled Columns, you can style the **cell chrome** around the chart — companion text, tooltip, and font — separately from the bullet itself (bands, bar, targets, origin, orientation — see [Styling the Chart](#styling-the-chart)).

### Cell Text

Two text tokens can be drawn around the chart — independently, and each at its own position. They are configured under the `CellTextProperties` group:

| Property                       | Type                        | Description                                            |
| ------------------------------ | --------------------------- | ------------------------------------------------------ |
| `CellTextLayout.CellValue`     | `BarChartCellTextPlacement` | Placement of the formatted cell value.                 |
| `CellTextLayout.PercentValue`  | `BarChartCellTextPlacement` | Placement of the percent value (cell value's position along the chart axis, as 0–100%; see [Percentage Value](#percentage-value)). |

A `BarChartCellTextPlacement` is `{ Horizontal, Vertical }` where:

- `Horizontal` — `'Left'` (default), `'Center'` or `'Right'`
- `Vertical` — `'Above'`, `'Below'` (default) or `'Merged'` (overlaid on the chart)

- A token is rendered **only when its key is present** in `CellTextLayout`
- When both tokens land on the **same horizontal slot at the same vertical position** they are concatenated with a single space (Cell Value first, then Percent Value)
- The new model lets you independently place the value above the bar (e.g. `CellValue: { Horizontal: 'Left', Vertical: 'Above' }`) and the percentage merged onto it (e.g. `PercentValue: { Horizontal: 'Right', Vertical: 'Merged' }`)

Leave `CellTextProperties` unset to show the chart only

### Percentage Value

The percent value shows where the cell value sits along the chart axis for that row.

This is calculated using the resolved band scale or column min/max:

```
clamp((value − min) / (max − min), 0, 1) × 100
```

AdapTable displays it as a **whole-number percent** (e.g. `67%`).

- The percentage is **always clamped to 0–100%** for display, even when the bar length is clamped at the axis edge
- It is **not** the column's own min/max percentile (as in the Gradient style); it is always relative to that cell's chart axis

### Tooltip

The cell can also be provided with a configurable AG Grid Cell Tooltip, via the `ToolTipText` property.

It accepts the tokens `'CellValue'` and/or `'PercentageValue'`.

When both are enabled in the tooltip, the percent is appended in parentheses (e.g. `142.50 (67%)`)

### Font

The `Font` property applies optional font styling to companion text rendered through `CellTextProperties`.

- When set, it overrides matching [Format Column](https://www.adaptabletools.com/docs/handbook-column-formatting/index.md) font properties on those cells
- The wizard exposes the shared **Font** editor on the **Style** step whenever at least one **Cell Display** option is checked

**Example: Bullet Chart: Cell Text**

Merged value and tooltip

- **Closed Issues** — `CellTextProperties: { CellTextLayout: { CellValue: { Horizontal: 'Center', Vertical: 'Merged' } } }` (white bold from [Format Column](https://www.adaptabletools.com/docs/handbook-column-formatting/index.md))
- Tooltip shows value and **percentage** along the chart axis (`ToolTipText`)

Demo project (Vanilla); the files without a marker are shown below:

```text
src/
  adaptableOptions.ts
  agGridModules.ts     [template]
  columnDefs.ts        [template]
  gridOptions.ts       [template]
  index.ts             [template]
  onAdaptableReady.ts  [template]
  rowData.ts           [template]
  styles.css           [template]
index.html             [template]
package.json           [template]
tsconfig.json          [template]
```

Files marked `[template]` are the same in every demo; see the [demo template](https://www.adaptabletools.com/llms/demo-template.md#vanilla).

**`src/adaptableOptions.ts`**

```ts
import {AdaptableOptions} from '@adaptabletools/adaptable';
import {WebFramework} from './rowData';

export const adaptableOptions: AdaptableOptions<WebFramework> = {
  primaryKey: 'id',
  adaptableId: 'Bullet Chart Cell Text',
  initialState: {
    Dashboard: {
      ModuleButtons: ['StyledColumn'],
    },
    Theme: {CurrentTheme: 'dark'},
    Layout: {
      CurrentLayout: 'Standard Layout',
      Layouts: [
        {
          Name: 'Standard Layout',
          TableColumns: [
            'name',
            'closed_issues_count',
            'language',
            'github_stars',
            'open_issues_count',
            'closed_pr_count',
            'week_issue_change',
            'has_projects',
            'has_pages',
            'updated_at',
            'pushed_at',
          ],
          AutoSizeColumns: true,
        },
      ],
    },
    StyledColumn: {
      StyledColumns: [
        {
          ColumnId: 'closed_issues_count',
          Name: 'Closed Issues labelled',
          BulletChartStyle: {
            RangeValueType: 'Number',
            CellRanges: [
              {Min: 'Col-Min', Max: 1000, Color: 'rgba(220, 53, 69, 0.35)'},
              {Min: 1000, Max: 5000, Color: 'rgba(255, 193, 7, 0.35)'},
              {Min: 5000, Max: 'Col-Max', Color: 'rgba(40, 167, 69, 0.35)'},
            ],
            TargetProperties: {
              Target: 5000,
              Marker: {Shape: 'Line', Size: 2},
            },
            Bar: {Color: '#07c', Height: 8},
            CellTextProperties: {
              CellTextLayout: {
                CellValue: {Horizontal: 'Center', Vertical: 'Merged'},
              },
            },
            ToolTipText: ['CellValue', 'PercentageValue'],
          },
        },
      ],
    },
    FormatColumn: {
      FormatColumns: [
        {
          Name: 'formatColumn-closed_issues_count',
          Scope: {ColumnIds: ['closed_issues_count']},
          Style: {
            ForeColor: 'var(--ab-color-palette-14)',
            FontWeight: 'Bold',
          },
        },
      ],
    },
  },
};
```

### What happens if AdapTable cannot draw a valid scale in the Cell

The Bullet Chart renderer is **only** used when a valid scale can be drawn and the row is in scope. 

When that is not possible, the cell falls back to plain content.

However what is rendered depends on the reason **why** the Bullet Chart could not be rendered:

| Situation                                                                              | What renders                                                                   |
| -------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------ |
| Row kind **excluded** by [Row Scope](#row-scope)                                       | The formatted **cell value** as plain text                                     |
| Cell value is `null` / `undefined`                                                     | **Nothing** — an empty cell                                                    |
| Cell value is **non-numeric**                                                          | The formatted cell value as **plain text**                                     |
| Chart axis cannot be resolved or `max ≤ min`                                           | The formatted cell value as **plain text**                                     |
| A **target** cannot be resolved (missing column value, unknown column id, non-numeric) | The chart still draws; that **target marker is omitted**                       |
| Styled Column **`IsSuspended`** is `true`                                              | AdapTable stops using the Bullet Chart renderer — normal formatted cell values |

## Row Scope

Users can decide which kinds of rows will include the Bullet Chart Style.

See [Styled Column Row Scope](https://www.adaptabletools.com/docs/handbook-styled-column-overview/index.md#row-scope) for details of the types of rows which can be included / excluded from a Styled Column

## Defining Bullet Charts

Bullet Chart Styles are defined as part of the Styled Column Initial State.

### Providing a Bullet Chart Style in Styled Column State

- Supply `ColumnId` of the Styled Column
- Give the Styled Column a unique `Name`
- Add a `BulletChartStyle` object

The `CellRanges` array defines the qualitative scale behind the bar.

Each band has `Min`, `Max` and `Color`. `Min` / `Max` can be:

- a number
- a percentage (when `RangeValueType` is `'Percentage'`)
- a dynamic endpoint: `Col-Min`, `Col-Max`, `Col-Avg`, `Col-Median`

The `TargetProperties` group places markers on the bar:

- `TargetProperties.Target` — a hard-coded number, a column id, `Col-Avg` / `Col-Median`, or an array of any of these (with optional per-target marker overrides via `BulletChartTargetDefinition`)
- `TargetProperties.Marker` — chart-level default marker style (`{ Shape, Color, Size }`)

- `Bar.Color` — colour of the actual bar (defaults to `var(--ab-color-primary)`)
- `Bar.Height` — bar thickness in px (defaults to 8); interpreted as the bar's **width** in vertical mode
- `BackColor` — flat background when `CellRanges` is omitted

- `CellTextProperties.CellTextLayout` — independent `{ Horizontal, Vertical }` placement for `CellValue` and `PercentValue` tokens
- `ToolTipText` — tokens `'CellValue'` / `'PercentageValue'` (see [Percentage Value](#percentage-value))
- `Font` — optional companion-text styling

```js [[1, 5, "Name"], [1, 6, "ColumnId"], [1, 7, "BulletChartStyle"], [2, 8, "RangeValueType"], [2, 9, "CellRanges"], [3, 26, "TargetProperties"], [4, 34, "Bar"], [5, 38, "CellTextProperties"]]
const initialState: InitialState = {
  StyledColumn: {
    StyledColumns: [
      {
        Name: 'Github Stars KPI',
        ColumnId: 'github_stars',
        BulletChartStyle: {
          RangeValueType: 'Number',
          CellRanges: [
            {
              Min: 'Col-Min',
              Max: 'Col-Avg',
              Color: 'rgba(220, 53, 69, 0.35)',
            },
            {
              Min: 'Col-Avg',
              Max: 100000,
              Color: 'rgba(255, 193, 7, 0.35)',
            },
            {
              Min: 100000,
              Max: 'Col-Max',
              Color: 'rgba(40, 167, 69, 0.35)',
            },
          ],
          TargetProperties: {
            Target: 100000,
            Marker: {
              Shape: 'Line',
              Color: 'var(--ab-color-foreground)',
              Size: 2,
            },
          },
          Bar: {
            Color: '#07c',
            Height: 8,
          },
          CellTextProperties: {
            CellTextLayout: {
              CellValue: { Horizontal: 'Center', Vertical: 'Merged' },
            },
          },
        },
      },
    ],
  },
};
```

## Using Bullet Charts

Bullet Chart Styles can be created at run-time using the Styled Column UI Wizard from:

- the **Styled Column** section in the [Settings Panel](https://www.adaptabletools.com/docs/ui-settings-panel/index.md)
- **Create Bullet Chart** / **Edit Bullet Chart** in the [Column Menu](https://www.adaptabletools.com/docs/ui-column-menu/index.md) on a **numeric** column

### Creating a Bullet Chart Column Style using the Styled Column Wizard

The Wizard guides you through the following steps:

In the **Settings** step:

- Enter a **Name** for the Styled Column (must be unique within the Grid)
- Pick **Bullet Chart** from the list of Styled Column Types (the wizard header becomes **Bullet Chart Column**)

For new Styled Columns the wizard defaults to **Badge Style** (the only type that applies to every column data type), so you need to explicitly select **Bullet Chart** from the type picker.

The **Scope** step is shared by every Styled Column. Use it to:

- Pick the **Column** whose values form the bar — only **numeric** columns without an existing Styled Column are listed
- Choose which **row kinds** the bullet chart renders on: **Data**, **Group**, **Summary** and **Total**

By default **Data**, **Summary** and **Total** are included, and **Group** rows are excluded — see [Row Scope](#row-scope).

The **Ranges** step defines the qualitative scale (`CellRanges`) drawn **behind** the bar. A **live preview** updates as you edit.

Until a column is chosen in **Scope**, the step prompts you to select a column first.

Use the shared band editor (same component as [Percent Bar](https://www.adaptabletools.com/docs/handbook-styled-column-percent-bar/index.md) and [Range Bar](https://www.adaptabletools.com/docs/handbook-styled-column-range-bar/index.md)):

- Add one or more bands with **Min**, **Max** and **Colour**
- Switch **Numeric** vs **Percentage** range value type (`RangeValueType`)
- **Min** / **Max** can be fixed numbers or dynamic endpoints (`Col-Min`, `Col-Max`, `Col-Avg`, `Col-Median`)

The wizard requires **at least one band** before you can finish — you cannot leave `CellRanges` empty when configuring through the UI.

**Create Bullet Chart** from the Column Menu seeds an initial `CellRanges` set derived from the column scope so the **Ranges** step opens with bands you can refine.

The **Style** step configures the bar, target marker, and labelling. The **live preview** continues to update as you edit.

The step is organised into four cards:

- **Target**
  - **Target** mode: **None**, **Fixed Number**, **Column**, **Column Average**, or **Column Median**
  - When a target is set: **Marker Shape** (`Line`, `Triangle`, `Dot`, `Diamond`), **Marker Colour** and **Marker Size**
  - The wizard configures a **single** target; multiple targets per cell are only available via `initialState` / API (see [Targets](#targets))

- **Bar**
  - **Orientation** — **Horizontal** (default) or **Vertical** (the wizard shows a row-height tip when vertical is selected)
  - **Bar Colour** and **Bar Height** (horizontal) or **Bar Width** (vertical)
  - **Origin** — **Auto** (default) or **Zero** for negative / variance-style columns

- **Cell Text**
  - **Cell Value** and **Percent Value** — each token has its own **Horizontal** (`Left` / `Center` / `Right`) and **Vertical** (`Above` / `Below` / `Merged`) placement so they can be positioned independently
  - **Font** editor — styles companion text when at least one cell-display option is enabled (disabled when no text is selected)

- **Tooltip** — **Cell Value** and/or **Percent Value** for the AG Grid hover tooltip

Two further wizard steps — **Tags** (when the workspace has tags enabled) and **Summary** (a read-only review of every previous step, including the live bullet preview) — are shared by all AdapTable modules and not specific to Bullet Chart Columns.

### FAQ

**Can I have multiple targets in one cell?**
Yes - pass an array to `TargetProperties.Target`. 
Each entry can be a number, a Column Id, a dynamic endpoint, or an object with its own `Marker` override.

**Can the target be different per row?**
Yes - pass a column id as `TargetProperties.Target` (or `Value` of a `BulletChartTargetDefinition`). 
The target marker is then resolved from each row's value in that column.

**What happens if a value is outside the band range?**
The bar is **clamped** to the chart axis (lowest resolved band `Min` → highest resolved band `Max`). 
There is no Range Bar-style overflow mode — off-scale values appear as a full-length bar at the nearest edge.

**Can I use the column min/max instead of bands?**
Yes — omit `CellRanges` in `initialState` / API and the bar scales across the column's visible min/max with a `BackColor` background. The **wizard always requires at least one band**; this pattern is API-only.

**What happens if the Styled Column is suspended?**
When `IsSuspended` is `true`, AdapTable stops using the Bullet Chart renderer for that column and shows normal formatted cell values instead.

**What if a target column has no value on a row?**
That target marker is skipped for the row; the bar and bands still render if the axis is valid. See [What happens if AdapTable cannot draw a valid scale in the Cell](#what-happens-if-adaptable-cannot-draw-a-valid-scale-in-the-cell) above.

**Can the underlying cell value still be shown?**
Yes — set `CellTextProperties.CellTextLayout.CellValue` to a `{ Horizontal, Vertical }` placement. Use `Vertical: 'Merged'` to overlay the value on top of the bar.
This pairs well with column formatting that sets the foreground colour.

**Why does my chart look the same in light and dark mode?**
By default `Bar.Color` resolves to `--ab-color-primary` (the theme's brand colour) and `TargetProperties.Marker.Color` resolves to `--ab-color-foreground` (the theme's text colour), so they automatically follow the active theme. 
Override them with explicit colours if you need pixel-exact control.

**Can I draw the chart vertically?**
Yes - set `Orientation` to `'Vertical'` so lower values appear at bottom of the cell and higher values at the top. 
Vertical bullet charts need tall row height to be readable (we recommend at least 60px - see [Vertical Bars](#vertical-bars) above).
