# Funnel Chart

> How many reached each stage of a sequence, and how many of them went on to the next.

Source: https://ngwr.dev/charts/funnel-chart  
Added in v15.1

## Installation

Import the class, and `@use` the entry point in your global styles: without it the component renders unstyled.

```typescript
import { Component } from '@angular/core';
import { WrFunnelChart } from 'ngwr/charts/funnel-chart';

@Component({ imports: [WrFunnelChart] })
export class MyComponent {}
```

```scss
@use 'ngwr/charts/funnel-chart';
```

## Basic

One centred bar per stage, as wide as its share of the largest. Between two bars, the share of the stage above; under each value, the share of the first.

```angular-html
<wr-funnel-chart
  ariaLabel="Sign-up funnel"
  [stages]="[
    { label: 'Visited', value: 12000 },
    { label: 'Signed up', value: 4800 },
    { label: 'Activated', value: 2880 },
    { label: 'Paid', value: 720 }
  ]"
/>
```

## Conversions

`conversion` picks what the drawing prints. The tooltip, each stage's accessible name and the data table keep both.

```angular-html
<wr-funnel-chart ariaLabel="Sign-up funnel" [stages]="signups" [conversion]="conversion()" />
```

```angular-ts
conversion = signal<WrFunnelConversion>('both'); // 'previous' | 'first' | 'none'
```

## Growth, gaps and zeros

Nothing is sorted or clamped. A stage larger than the one before is drawn wider, above 100%. A missing value draws no bar and no band. Zero is a hairline.

```angular-html
<wr-funnel-chart ariaLabel="Email campaign" [stages]="campaign" />
```

```angular-ts
campaign: WrFunnelStage[] = [
  { label: 'Emailed', value: 5000 },
  { label: 'Opened', value: 2100 },
  // More than the stage before: drawn wider, 123.8% of previous.
  { label: 'Shared', value: 2600 },
  // Not measured: no bar, no band, no conversion to or from it.
  { label: 'Clicked', value: NaN },
  // A real zero: a hairline.
  { label: 'Converted', value: 0 },
];
```

## Shared scale

With no `max` the largest stage is the full width. Give two funnels the same `max` to compare them.

```angular-html
<!-- One full width for both: the larger funnel's top stage -->
<wr-funnel-chart ariaLabel="Web" [stages]="web" [max]="9000" />
<wr-funnel-chart ariaLabel="Mobile" [stages]="mobile" [max]="9000" />
```

## Number format

Values and conversions follow `LOCALE_ID`. `valueFormat` replaces the values; `percentFormat` the conversions, and it takes a ratio, so 0.25 is a quarter.

```angular-html
<wr-funnel-chart ariaLabel="Orders by value" [stages]="orders" [valueFormat]="money" [percentFormat]="percent" />
```

```angular-ts
usd = new Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD', notation: 'compact' });
money = (value: number) => this.usd.format(value);

// A ratio in, as Intl's percent style takes it: 0.25 is a quarter.
percents = new Intl.NumberFormat('en-US', { style: 'percent', minimumFractionDigits: 2 });
percent = (ratio: number) => this.percents.format(ratio);
```

## Data table

`dataTable` adds a table only screen readers reach: a row per stage, with its value and both conversions. Nothing changes on screen.

```angular-html
<wr-funnel-chart [stages]="signups" ariaLabel="Sign-up funnel" dataTable />
```

## Accessibility

| Part | Behaviour |
| --- | --- |
| Chart | A `role="group"`, named by `ariaLabel`. |
| Stage | A `role="img"` named with its label, value and both conversions. |
| Bands | Hidden. The share between two bars is already in the lower stage's name. |
| Colour | Carries nothing. Every stage is named in text, so one colour is the default. |
| Keyboard | No tab stop. The tooltip is pointer only and repeats the names. |

## API

| Name | Description | Type | Default |
| --- | --- | --- | --- |
| `stages` | The stages, from the top of the funnel down. Nothing is sorted: a stage is drawn where it is given, so a stage larger than the one before it shows as one. | `readonly WrFunnelStage[]` | `[]` |
| `ariaLabel` | Accessible name of the chart, and the data table's caption. Each stage names itself, so this says what the funnel is about. Falls back to `funnelChart.label`, then `'Funnel chart'`. | `string \| null` | `null` |
| `color` | The colour of a stage with no `color` of its own. One colour by default, because the stages are one quantity measured along a sequence, not categories: every stage is named in text beside its bar, so colour tells nothing apart here. | `string` | `'var(--wr-color-primary)'` |
| `max` | The value of a full-width bar. `0` makes it the largest stage shown; a value above `0` fixes it, so two funnels side by side share one scale. A stage above a fixed `max` is drawn full width, and its figure, the tooltip and the data table still give its own value. | `number` | `0` |
| `conversion` | Which conversions are printed on the drawing: each stage's share of the stage before it, between the two bars (`previous`), its share of the first stage, under its value (`first`), `both` or `none`. The tooltip, every stage's accessible name and the data table carry both whatever this says. | `WrFunnelConversion` | `'both'` |
| `showValues` | Print each stage's value beside its bar. | `boolean` | `true` |
| `valueLabel` | What the values count — visitors, orders — as the heading of the data table's value column. Falls back to `funnelChart.value` ("Value"). | `string \| null` | `null` |
| `tooltip` | Show a tooltip with the stage's label, value and both conversions on hover. Pointer only — each stage's accessible name already carries the same figures. | `boolean` | `true` |
| `valueFormat` | Writes every value the chart shows: beside the bars, in the tooltip, in the accessible names and in the data table. `null` uses `Intl.NumberFormat` in `LOCALE_ID`. | `((value: number) => string) \| null` | `null` |
| `percentFormat` | Writes every conversion the chart shows, given as a RATIO — `0.25` for a quarter, `1.2` for a stage that grew by a fifth — the number `Intl.NumberFormat`'s `percent` style takes, so one of those can be passed as it is. `null` uses that style in `LOCALE_ID`, with up to one fraction digit, and two significant digits below 1% so a small real conversion is not printed as `0%`. | `((ratio: number) => string) \| null` | `null` |
| `dataTable` | Render the chart's figures as a table that only assistive technology reaches: a row per stage, headed by its label, with its value and both conversions, each written as the tooltip writes it. A figure the stage does not have is an empty cell. Each stage's accessible name already carries its figures; the table is for a reader who wants to move through them cell by cell, with the column headings read out. It is rendered beside the stages, clipped to nothing: it takes no room, draws nothing and holds no tab stop. | `boolean` | `false` |

## Types

```angular-ts
interface WrFunnelStage {
  label: string;
  value: number;
  color?: string;
}

type WrFunnelConversion = 'both' | 'previous' | 'first' | 'none';
```

| Name | Description | Type | Default |
| --- | --- | --- | --- |
| `WrFunnelStage` | One stage. | `interface` | — |
| `label`required | Beside the bar, in the tooltip and in the table. | `string` | — |
| `value`required | How many reached it. A non-finite value is missing. | `number` | — |
| `color` | CSS colour for the bar and its bands. | `string` | ``the chart's `color``` |
| `WrFunnelConversion` | Which conversions the drawing prints. | `'both' \| 'previous' \| 'first' \| 'none'` | — |

## CSS variables

Set these on the component's selector or a wrapper, not on `:root`: the defaults are declared on `ngwr/charts/funnel-chart`'s own element.

| Variable | Default | Declared on |
| --- | --- | --- |
| `--wr-funnel-chart-active-bg` | `var(--wr-color-hover)` | `.wr-funnel-chart` |
| `--wr-funnel-chart-band-opacity` | `0.2` | `.wr-funnel-chart` |
| `--wr-funnel-chart-bar-height` | `calc(var(--wr-funnel-chart-value-line-height) + var(--wr-funnel-chart-conversion-line-height))` | `.wr-funnel-chart` |
| `--wr-funnel-chart-bar-radius` | `var(--wr-border-radius-sm)` | `.wr-funnel-chart` |
| `--wr-funnel-chart-color` | `var(--wr-color-on-surface)` | `.wr-funnel-chart` |
| `--wr-funnel-chart-column-gap` | `var(--wr-space-md)` | `.wr-funnel-chart` |
| `--wr-funnel-chart-conversion-color` | `var(--wr-color-on-surface-muted)` | `.wr-funnel-chart` |
| `--wr-funnel-chart-conversion-font-size` | `var(--wr-text-xs)` | `.wr-funnel-chart` |
| `--wr-funnel-chart-conversion-line-height` | `var(--wr-text-xs-leading)` | `.wr-funnel-chart` |
| `--wr-funnel-chart-font-family` | `var(--wr-font-family-base)` | `.wr-funnel-chart` |
| `--wr-funnel-chart-gap-height` | `1.5rem` | `.wr-funnel-chart` |
| `--wr-funnel-chart-gap-label-bg` | `color-mix(in srgb, var(--wr-color-surface) 85%, transparent)` | `.wr-funnel-chart` |
| `--wr-funnel-chart-gap-label-padding-x` | `var(--wr-space-xs)` | `.wr-funnel-chart` |
| `--wr-funnel-chart-gap-label-radius` | `var(--wr-border-radius-sm)` | `.wr-funnel-chart` |
| `--wr-funnel-chart-label-color` | `var(--wr-color-on-surface)` | `.wr-funnel-chart` |
| `--wr-funnel-chart-label-font-size` | `var(--wr-text-sm)` | `.wr-funnel-chart` |
| `--wr-funnel-chart-label-line-height` | `var(--wr-text-sm-leading)` | `.wr-funnel-chart` |
| `--wr-funnel-chart-label-max-width` | `12rem` | `.wr-funnel-chart` |
| `--wr-funnel-chart-stage-padding-x` | `var(--wr-space-xs)` | `.wr-funnel-chart` |
| `--wr-funnel-chart-stage-radius` | `var(--wr-border-radius-sm)` | `.wr-funnel-chart` |
| `--wr-funnel-chart-track-min-width` | `40%` | `.wr-funnel-chart` |
| `--wr-funnel-chart-value-color` | `var(--wr-color-on-surface)` | `.wr-funnel-chart` |
| `--wr-funnel-chart-value-font-size` | `var(--wr-text-sm)` | `.wr-funnel-chart` |
| `--wr-funnel-chart-value-font-weight` | `var(--wr-font-weight-medium)` | `.wr-funnel-chart` |
| `--wr-funnel-chart-value-line-height` | `var(--wr-text-sm-leading)` | `.wr-funnel-chart` |

## Dependencies

| Built on | [Popover](https://ngwr.dev/reference/components/popover) |
| --- | --- |

## See also

- [Bar Chart](https://ngwr.dev/charts/bar-chart) — Bars for one or several series, grouped or stacked, standing up or lying along a row, with lines over them.
- [Meter Group](https://ngwr.dev/charts/meter-group) — One bar split into labelled parts, with a legend.
- [Donut Chart](https://ngwr.dev/charts/donut-chart) — Parts of a whole as a ring or a pie.
- [Line Chart](https://ngwr.dev/charts/line-chart) — One or more series of values as lines or as an area chart, overlaid or stacked.
- [Popover](https://ngwr.dev/reference/components/popover) — Show a template, or a text tooltip, next to its trigger.
