import { Component } from '@angular/core';
import { WrFunnelChart } from 'ngwr/charts/funnel-chart';
@Component({ imports: [WrFunnelChart] })
export class MyComponent {}Funnel Chart
How many reached each stage of a sequence, and how many of them went on to the next.
Installation
Import the class, and @use the entry point in your global styles: without it the component renders unstyled.
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.
<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.
<wr-funnel-chart ariaLabel="Sign-up funnel" [stages]="signups" [conversion]="conversion()" />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.
<wr-funnel-chart ariaLabel="Email campaign" [stages]="campaign" />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.
<!-- 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.
<wr-funnel-chart ariaLabel="Orders by value" [stages]="orders" [valueFormat]="money" [percentFormat]="percent" />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.
<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
interface WrFunnelStage {
label: string;
value: number;
color?: string;
}
type WrFunnelConversion = 'both' | 'previous' | 'first' | 'none';| Name | Description | Type | Default |
|---|---|---|---|
WrFunnelStage | One stage. | interface | — |
labelrequired | Beside the bar, in the tooltip and in the table. | string | — |
valuerequired | 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 |
|---|
See also
Component Bar ChartBars for one or several series, grouped or stacked, standing up or lying along a row, with lines over them.Component Meter GroupOne bar split into labelled parts, with a legend.Component Donut ChartParts of a whole as a ring or a pie.Component Line ChartOne or more series of values as lines or as an area chart, overlaid or stacked.Component PopoverShow a template, or a text tooltip, next to its trigger.