Skip to content

Funnel Chart

ChartsSince 0.4.0
import { FunnelChart } from '@absoluteui/components/funnel-chart';
import '@absoluteui/components/funnel-chart/style.css';

Using Angular? See Funnel Chart for Angular.

An accessible SVG funnel chart for sequential stages (for example visits, sign-ups, purchases). It provides:

  • one centered bar per stage, its width proportional to the stage value (at least 2 px), joined to the next stage by a decorative trapezoid connector;
  • the stage name and value beside the funnel, plus the conversion from the previous stage (showConversion);
  • keyboard-reachable stages (one tab stop, arrow keys) whose accessible names read the value and the conversion from the first and the previous stage, with a tooltip on focus and hover;
  • a generated summary (overall conversion and the largest drop) and a data table fallback (Stage, Value, Of first stage, Of previous stage).

No animation.

  • A fixed sequence of stages where each stage is a subset of the previous one (conversion or pipeline funnels).
  • Showing where the largest drop-off happens between consecutive stages.
  • Categories without an order or without a subset relationship: use Bar Chart.
  • Comparing several funnels side by side: use a grouped Bar Chart.
  • Values that can be negative (rejected by validation).
import { FunnelChart } from '@absoluteui/components/funnel-chart';
const host = document.createElement('div');
document.body.append(host);
FunnelChart.create(host, {
label: 'Checkout funnel',
description: 'Last 30 days',
stages: [
{ id: 'visits', label: 'Visits', value: 12000 },
{ id: 'cart', label: 'Added to cart', value: 4100 },
{ id: 'checkout', label: 'Checkout', value: 1900 },
{ id: 'paid', label: 'Paid', value: 1350 },
],
color: 3,
});

Compact numbers, no printed conversion, live update

Section titled “Compact numbers, no printed conversion, live update”
import { FunnelChart, type FunnelStage } from '@absoluteui/components/funnel-chart';
const host = document.querySelector<HTMLElement>('#pipeline');
if (host) {
const stages: FunnelStage[] = [
{ id: 'leads', label: 'Leads', value: 5400 },
{ id: 'qualified', label: 'Qualified', value: 2100 },
{ id: 'won', label: 'Won', value: null },
];
const chart = FunnelChart.create(host, {
label: 'Sales pipeline',
stages,
showConversion: false,
valueFormat: { notation: 'compact' },
});
chart.update({ stages: stages.map((s) => (s.id === 'won' ? { ...s, value: 640 } : s)) });
}
import { FunnelChart } from '@absoluteui/components/funnel-chart';
import '@absoluteui/components/funnel-chart/style.css';
const host = document.querySelector<HTMLElement>('#signup');
if (host) {
FunnelChart.create(host, {
label: 'Sign-up funnel',
stages: [
{ id: 'landing', label: 'Landing page', value: 8200 },
{ id: 'form', label: 'Form started', value: 3100 },
{ id: 'done', label: 'Account created', value: 2400 },
],
valueFormat: { notation: 'compact' },
});
}
import { FunnelChart } from '@absoluteui/components/funnel-chart';
const host = document.createElement('div');
document.body.append(host);
FunnelChart.create(host, {
label: 'Checkout',
stages: [
{ id: 'visits', label: 'Visits', value: 1000 },
{ id: 'cart', label: 'Cart', value: 400 },
{ id: 'paid', label: 'Paid', value: 100 },
],
});
<div class="aui-chart aui-funnel-chart [aui-chart--patterns]">
<div class="aui-chart__header">title, description</div>
<ul class="aui-chart__legend" hidden/>
<div class="aui-chart__body">
<svg class="aui-chart__svg" role="group" aria-roledescription="funnel chart" aria-labelledby="T" aria-describedby="S">
<g class="aui-funnel-chart__joins" aria-hidden="true"><path class="aui-funnel-chart__join aui-chart__series--c1"/></g>
<g class="aui-funnel-chart__stages">
<g class="aui-funnel-chart__stage aui-chart__series--c1">
<rect class="aui-chart__bar aui-chart__mark" role="img" aria-label="Cart: 400, 40% of Visits, 40% of previous stage"/>
</g>
</g>
<g class="aui-funnel-chart__labels" aria-hidden="true">
<text class="aui-funnel-chart__name">Cart</text> <text class="aui-chart__tick-label">400 | 40%</text>
</g>
</svg>
<div class="aui-chart__tooltip" aria-hidden="true" hidden/>
</div>
<p class="aui-chart__summary" hidden/> <div class="aui-chart__data">Show data table, table</div>
</div>
FunnelChart.create(host: HTMLElement, options: FunnelChartOptions): FunnelChart

Global configuration key: funnelChart (see Configuration).

Option Type Default Description
label string Chart title: visible text and the accessible name of the plot (aria-labelledby). Must not be blank. Required.
hideLabel boolean false Visually hide the title; it stays the accessible name.
description string Visible description below the title, also referenced by aria-describedby (text only).
summary string Replaces the generated accessible summary (stage count, first and last stage, overall conversion, largest drop).
valueFormat Intl.NumberFormatOptions Number format for stage values in labels, accessible names, the tooltip, the data table, and the summary. Conversions are always percentages with at most one fraction digit.
formatValue (value: number) => string Custom value text; overrides valueFormat.
hiddenSeries readonly string[] Shared chart option; validated but has no effect (a funnel has no toggleable series).
stages readonly FunnelStage[] Stages in order: { id, label, value: number | null, color?: 1-8 }. Ids are unique and non-empty; values are non-negative; null, NaN, and infinities are a missing stage (read “No value”). Required.
color ChartColor (1-8) 1 Palette slot for every stage without its own color.
legend 'auto' | 'show' | 'hide' "auto" Shared chart configuration; a funnel never shows a legend (stages are labelled beside their bars). Globally configurable.
dataTable 'toggle' | 'visible' | 'hidden' | 'none' "toggle" Data table fallback (Stage, Value, Of first stage, Of previous stage): behind a Show data table button (built on first open), always visible, visually hidden (screen readers only), or none. Globally configurable.
tooltip boolean true Tooltip on pointer hover and keyboard focus (value, of first stage, of previous stage). Globally configurable.
patterns boolean false Shared chart configuration; toggles the aui-chart–patterns host class. Funnel stages draw no textures (there is one series). Globally configurable.
height number 300 Plot height in CSS px (integer 120-2000), divided evenly between stages; the width follows the container. Globally configurable.
showConversion boolean true Print the conversion from the previous stage next to each stage value (“400 | 40%”). Accessible names, the tooltip, and the table always include conversions. Globally configurable.
locale string "en-US" Number, percent, and message locale. Globally configurable. Inherited from common configuration.
direction 'ltr' | 'rtl' "ltr" rtl moves stage names and values to the right side and swaps ArrowLeft/ArrowRight. Globally configurable. Inherited from common configuration.
Property Type Description
host HTMLElement The host element.
element SVGSVGElement The plot (svg role=“group”), named by the title and described by the summary.
summary string The generated (or custom) accessible summary.
hiddenSeries string[] Always empty (a funnel has no toggleable series).
Method Description
update(options: Partial<FunnelChartOptions>): void Merge, validate, and re-render. Invalid options throw and keep the previous state. Keyboard focus stays on the same stage (by id).
setSeriesVisible(id: string, visible: boolean): void Shared chart method; re-renders but has no visible effect on a funnel. Emits no event.
destroy(): void Remove rendering, host classes, listeners, the resize observer, and the configuration subscription.

All rules live in the aui.components cascade layer, so application styles override them without specificity battles.

Class Purpose
aui-chart Host (shared chart chrome).
aui-funnel-chart Host modifier for this chart type.
aui-chart--patterns patterns option on (no textures are drawn by a funnel).
aui-chart__header / __title / __title--hidden / __description / __toolbar Title and description; the toolbar stays hidden.
aui-chart__legend Legend list; always hidden for a funnel.
aui-chart__body / __svg / __layer Plot container (height from –aui-chart-height), SVG, and drawing layer.
aui-chart__series--c1 ... --c8 Palette slot classes on each stage and connector; set –aui-chart-color from color-chart-<n>.
aui-chart__mark / __mark--active / __mark--hover Focusable stage bar and its states.
aui-chart__bar Stage bar paint (series color with a surface-colored separator; text-colored outline on hover and focus).
aui-chart__tick-label Value (and conversion) text under each stage name.
aui-chart__tooltip / __tooltip-title / __tooltip-rows / __tooltip-row / __tooltip-label / __tooltip-value / __swatch Tooltip (aria-hidden; the focused stage carries the same text).
aui-chart__empty Empty state text (No data).
aui-chart__summary Hidden summary referenced by aria-describedby.
aui-chart__data / __data--hidden / __table-toggle / __table-scroll / __table / __cell--numeric Data table fallback.
aui-funnel-chart__stages / __stage Stage bar group and one group per stage.
aui-funnel-chart__missing Missing stage (null value): dashed outline, no fill.
aui-funnel-chart__joins / __join Decorative (aria-hidden) trapezoid connectors between consecutive stages with values.
aui-funnel-chart__labels / __name Stage names and values beside the funnel (aria-hidden).

Tokens this stylesheet reads (verified in CI). Override them in a theme or with the Theme Designer.

--aui-border-width | --aui-color-border | --aui-color-border-strong | --aui-color-chart-1 | --aui-color-chart-2 | --aui-color-chart-3 | --aui-color-chart-4 | --aui-color-chart-5 | --aui-color-chart-6 | --aui-color-chart-7 | --aui-color-chart-8 | --aui-color-chart-axis | --aui-color-chart-grid | --aui-color-focus-ring | --aui-color-hover | --aui-color-link | --aui-color-surface | --aui-color-surface-sunken | --aui-color-text | --aui-color-text-muted | --aui-duration-fast | --aui-easing-standard | --aui-focus-ring-offset | --aui-focus-ring-width | --aui-font-family-sans | --aui-font-size-md | --aui-font-size-sm | --aui-font-size-xs | --aui-font-weight-medium | --aui-font-weight-regular | --aui-font-weight-semibold | --aui-line-height-normal | --aui-line-height-tight | --aui-radius-md | --aui-radius-sm | --aui-shadow-md | --aui-space-1 | --aui-space-2 | --aui-space-3 | --aui-tooltip-max-width | --aui-touch-target-min

interface FunnelChartConfig extends ChartBaseConfig { showConversion: boolean }
interface FunnelStage { id: string; label: string; value: number | null; color?: ChartColor }
interface FunnelChartOptions extends ChartCommonOptions, Partial<FunnelChartConfig> {
stages: readonly FunnelStage[];
color?: ChartColor;
}

Pattern: SVG funnel chart as a named, described group (role=“group” with aria-roledescription “funnel chart”) of role=“img” stage bars with roving focus; data table fallback (native table: Stage, Value, Of first stage, Of previous stage).

Key Context Action
ArrowDown / ArrowRight Stage focused Next stage (ArrowLeft in RTL).
ArrowUp / ArrowLeft Stage focused Previous stage (ArrowRight in RTL).
Home / End Stage focused First / last stage.
PageUp / PageDown Stage focused Ten stages back / forward (clamped to the first / last).
Escape Tooltip visible Hides the tooltip without moving focus (WCAG 1.4.13); it returns on the next move.
Tab / Shift+Tab Any One tab stop for the plot (the active stage), then the Show data table button and table region.
Element Role
svg.aui-chart__svg group with aria-roledescription (localized “funnel chart”), named by the title
stage bars img with an aria-label: stage name, formatted value, and conversion from the first and previous stage
data table table (native) with caption, column headers, and row headers; wide tables scroll in a focusable region
connectors, stage names and values beside the funnel, tooltip aria-hidden (decorative duplicates of the accessible data)
Attribute Element Value
aria-labelledby svg id of the visible (or visually hidden) title
aria-describedby svg description id (when provided) and the generated summary id
aria-roledescription svg Localized chart type, “funnel chart”
aria-label each stage bar First stage: “{label}: {value}”; later stages: “{label}: {value}, {ofFirst} of {first}, {ofPrevious} of previous stage” (for example “Cart: 400, 40% of Visits, 40% of previous stage”); missing: “{label}: No value”
tabindex active stage bar 0 on exactly one stage (roving); other stages have none
aria-expanded / aria-controls Show data table button Table open state and the table region id

Roving focus: one stage is in the tab sequence and remembers the last visited stage (by id), also across re-renders (resize, update). Toggling the table keeps focus on the table button.

Criterion Level Note
1.1.1 Non-text Content A
1.3.1 Info and Relationships A
1.4.1 Use of Color A
1.4.3 Contrast (Minimum) AA
1.4.10 Reflow AA
1.4.11 Non-text Contrast AA
1.4.13 Content on Hover or Focus AA
2.1.1 Keyboard A
2.4.3 Focus Order A
2.4.7 Focus Visible AA
4.1.2 Name, Role, Value A

Automated test results: Accessibility and compliance.

  • Each stage name reads its value and its conversion from the first and the previous stage; the data table lists the same conversions.
  • The generated summary names the stage count, the first and last stage with values, the overall conversion, and the largest drop between consecutive stages (for example “Largest drop: Cart to Paid (25% kept).”).
  • Meaning never depends on color: bar width, position, and printed names and values carry the data; per-stage colors are optional decoration.
  • Forced colors: bars keep a Canvas separator, connectors use GrayText, names CanvasText; focus and hover use Highlight.
  • No animation: nothing moves on render; hover and focus emphasis transitions are removed under prefers-reduced-motion and animation: none.
  • Manual screen-reader verification (NVDA, JAWS, VoiceOver, TalkBack) is pending.
  • aria-roledescription support varies; screen readers without it announce “group”.

default, empty (“No data”: no stages, or no stage above zero), stage missing, stage focused (tooltip), stage hovered, table open.

From Trigger To
plot not focused Tab active stage focused, tooltip shown
stage focused arrow keys, Home, End, PageUp, PageDown another stage focused
tooltip visible Escape tooltip hidden
table closed Show data table table shown
any update({ stages }) re-rendered; focus stays on the same stage id
  • Stage names read the value and the conversion from the first and previous stage, so the funnel’s meaning is available without seeing bar widths.
  • Summary example: “funnel chart, 3 stages, from Visits (1,000) to Paid (100), 10% overall. Largest drop: Cart to Paid (25% kept).”
  • Meaning never depends on color: widths, order, and printed text carry the data.
  • Forced colors: connectors GrayText, names CanvasText, bars keep a Canvas separator; focus and hover use Highlight.
  • No animation. Manual screen-reader verification is pending.

Width follows the container. Stage names and values take up to 40% of the width and are truncated with an ellipsis; full text stays in names, tooltip, and table. Reflows at 320 CSS px.

Stage names and values move to the right of the funnel and are right-aligned; ArrowLeft/ArrowRight swap. The funnel shape is symmetric.

Stages use color-chart-1..8 (3:1 against surfaces in all themes); connectors use the stage color at low opacity (decorative). Missing stages are a dashed color-chart-axis outline.

configureAbsoluteUI({ components: { funnelChart: { showConversion: false, dataTable: 'visible' } } });

Instance options override global and theme defaults. All options are dynamic.

Not applicable (stages are text).

All text uses textContent; SVG uses createElementNS; colors are classes; the height and tooltip position use the CSSOM. No innerHTML or style attributes.

No persistence, network access, or telemetry. Values are not logged; validation errors name the option, never its value.

Budget: create with 20 stages < 20 ms in headless Chromium (e2e performance test), including the first render.

Up to about 20 stages (each is one focusable node, one connector, two labels). Aggregate longer sequences first.

  • No stages, or every stage zero or missing: “No data”.
  • Missing stage (null, NaN, infinity): an empty, zero-width dashed placeholder (still focusable), “No value” in its name and the table, no connectors to it; the next stage’s conversion uses the previous stage that has a value.
  • First (or previous) stage zero or missing: conversions are not computed; the stage reads only its value and the table shows “-”.
  • A zero stage is drawn 2 px wide so it stays focusable and visible.
  • A later stage larger than an earlier one reads conversions above 100%.
  • Long stage names are truncated with an ellipsis beside the funnel.
Code Category Cause
FUNNEL_CHART_OPTIONS configuration Blank label; stages not an array; stage not an object; empty or duplicate id; label not a string; negative value; color not 1-8.
CFG_INVALID_VALUE / CFG_UNKNOWN_OPTION configuration Invalid or unknown configuration (for example height outside 120-2000).

A failed construction leaves the host untouched; a failed update() keeps the previous state.

  • One funnel per chart, no legend.
  • legend, patterns, and hiddenSeries are shared chart options without effect here.
  • Manual screen-reader verification is pending.
  • Negative values; several funnels or stage segments in one chart.
  • Curved, pyramid, or proportional-area shapes.
  • HTML in labels.