Funnel Chart

A funnel chart shows how values attrit through ordered stages — each bar represents one stage, and widths are proportional to stage values. Trapezoidal connectors between bars make the drop-off visually explicit. A diverging (back-to-back) mode supports side-by-side comparisons such as treatment vs. control arms.

Basic usage

#![allow(unused)]
fn main() {
use kuva::plot::funnel::FunnelPlot;
use kuva::render::{plots::Plot, layout::Layout, render::render_multiple};
use kuva::backend::svg::SvgBackend;

let plot = FunnelPlot::new()
    .with_stage("Screened",   1200)
    .with_stage("Eligible",    800)
    .with_stage("Enrolled",    600)
    .with_stage("Completed",   540);

let plots = vec![Plot::Funnel(plot)];
let layout = Layout::auto_from_plots(&plots).with_title("CONSORT Flow");
let svg = SvgBackend.render_scene(&render_multiple(plots, layout));
std::fs::write("funnel.svg", svg).unwrap();
}

Bulk stages

#![allow(unused)]
fn main() {
use kuva::plot::funnel::FunnelPlot;

let plot = FunnelPlot::new().with_stages([
    ("Awareness", 5000.0),
    ("Interest",  3000.0),
    ("Desire",    2000.0),
    ("Action",    1200.0),
]);
}

Color modes

#![allow(unused)]
fn main() {
use kuva::plot::funnel::{FunnelPlot, FunnelColorMode};

// Each stage gets a distinct category10 color
let plot = FunnelPlot::new()
    .with_stages([("A", 1000.0), ("B", 700.0), ("C", 400.0)])
    .with_color_mode(FunnelColorMode::ByStage);

// Bars progressively darken from top to bottom
let plot = FunnelPlot::new()
    .with_stages([("A", 1000.0), ("B", 700.0), ("C", 400.0)])
    .with_color_mode(FunnelColorMode::Gradient);

// Per-stage explicit colors
let plot = FunnelPlot::new()
    .with_stage_color("Screened", 1200.0, "#2980b9")
    .with_stage_color("Eligible",  800.0, "#27ae60")
    .with_stage_color("Enrolled",  600.0, "#e67e22");
}

Horizontal orientation

#![allow(unused)]
fn main() {
use kuva::plot::funnel::{FunnelPlot, FunnelOrientation};

let plot = FunnelPlot::new()
    .with_stages([("Q1", 1200.0), ("Q2", 950.0), ("Q3", 720.0), ("Q4", 580.0)])
    .with_orientation(FunnelOrientation::Horizontal);
}

Diverging (mirror) mode

Show two parallel funnels side-by-side — useful for treatment vs. control arms in clinical trials.

#![allow(unused)]
fn main() {
use kuva::plot::funnel::FunnelPlot;

let plot = FunnelPlot::new()
    .with_stage("Screened",   1200)
    .with_stage("Eligible",    840)
    .with_stage("Enrolled",    720)
    .with_stage("Completed",   648)
    .with_mirror_stages([
        ("Screened",  1150.0),
        ("Eligible",   810.0),
        ("Enrolled",   690.0),
        ("Completed",  620.0),
    ])
    .with_mirror_labels("Treatment", "Control");
}

Label options

#![allow(unused)]
fn main() {
let plot = FunnelPlot::new()
    .with_stages([("A", 1000.0), ("B", 700.0), ("C", 400.0)])
    .with_show_percents(true)     // show "700 (70.0%)" alongside value
    .with_show_conversion(true)   // show "70.0%" step-to-step rate in connectors
    .with_show_values(true);      // show absolute values (default: true)
}

Builder reference

MethodDefaultDescription
.with_stage(label, value)Append one stage.
.with_stage_color(label, value, css)Stage with explicit CSS fill color.
.with_stages(iter)Append multiple (label, value) stages at once.
.with_mirror(stages)NoneEnable diverging mode with Vec<FunnelStage>.
.with_mirror_stages(iter)NoneEnable diverging mode from (label, value) iterator.
.with_mirror_labels(left, right)Side labels for diverging mode.
.with_orientation(o)VerticalVertical or Horizontal.
.with_connectors(bool)trueDraw trapezoidal connectors between bars.
.with_connector_opacity(f64)0.4Connector fill opacity 0–1. Mirrors SankeyPlot::with_link_opacity.
.with_show_values(bool)trueAbsolute value label on each bar.
.with_show_percents(bool)falseShow percentage-of-first-stage alongside value.
.with_show_conversion(bool)trueStep-to-step conversion rate in connector areas.
.with_color_mode(mode)UniformUniform, ByStage, or Gradient.
.with_stage_gap(f64)4.0Pixel gap between adjacent bars. Mirrors SankeyPlot::with_node_gap.
.with_legend(label)NoneEnable legend with given label. Mirrors SankeyPlot::with_legend.