PAT-43 · Catalog 4 · Visualization
Visualization Catalog — sample renders
Internal preview. 51 templates, each rendered with its getSampleData() export. Templates back the InsightCard.visual.kind enum and are queryable via the toolbox.viz.* MCP tools.
viz.index-meter
Index Meter (decomposable 0–100)
percentile-calloutcompositioncategorical-breakdownA headline 0–100 index that opens to weighted component sub-scores, each opening to the measures beneath. Components read as dots on a shared 0–100 axis (not bars) with the headline as a reference marker. The canonical composite-of-composites display (Leadership Quality, CAMS) — the Index Doctrine made visible.
Input: { headline: { label: string; value: number; band?: string }; components: Array<{ label: string; value: number; weight: number; measures?: Array<{ label: string; value: number; unit?: string; n?: number }> }>; asOf?: string }
No renderer wired.
When to use / when not to use
When to use: Use for a 0–100 composite that must stay decomposable: a headline score, ~3 weighted components, and the measures + data under each. Reading: each component is a dot on a shared 0–100 axis vs the headline marker; the measures open beneath it.
When not to use: Do not use a gauge/speedometer or a full-width single-value bar for an index (retired — they exaggerate and don't decompose). For a single value vs a target use `viz.bullet-bar`; for an estimate with uncertainty use `viz.forest-plot`.
Renderer:
src/lib/visualizations/templates/IndexMeter.tsxviz.severity-heat-table
Severity Heat-Table (measures × segments)
matrix-gridcategorical-breakdowncompensationThe locked measures × segments 'where is it bad' heat-table — the HR-analytics workhorse. Rows = measures, columns = segments (or flipped); each cell's color encodes severity on a status-hue ramp (pale → amber → red, never rainbow) so the worst intersections pop. Rows order most-signal-first (worst / widest-spread on top); per-cell min-N suppression redacts thin cells visibly. The locked composition of viz.heatmap + in-table severity, not a freeform heatmap.
Input: { measures: string[]; segments: string[]; cells: Array<{ measure: string; segment: string; severity: number /* 0-1 */; value?: number; unit?: string; n?: number; suppressed?: boolean }>; orientation?: "measures-as-rows" | "measures-as-cols"; orderBy?: "most-signal" | "none"; minN?: number; severityLabel?: string }
No renderer wired.
When to use / when not to use
When to use: Use to scan many measures across many segments for WHERE the problem concentrates: pay-below-market by department, attrition risk by level × tenure, compliance gaps by jurisdiction. Reading: color = severity (darker/redder = worse), rows sorted worst-first, redacted cells = below min-N. Color is the only encoded signal — pair with the value read-out and a legend.
When not to use: Do not use when you have one measure vs a target (use viz.bullet-bar) or an estimate with uncertainty (use viz.forest-plot). Do not use rainbow / categorical hues for severity, and never read an exact value from a shade — the cell carries its own number.
Renderer:
src/lib/visualizations/templates/SeverityHeatTable.tsxviz.forest-plot
Forest Plot (dot + interval)
estimate-intervalcomparison-pairdistributionSorted dot-and-confidence-interval (point-range) chart against a reference line. Reads estimate + uncertainty at once; the canonical chart for pay-equity / fairness gaps, segment scores with CIs, and any ranking where intervals must be honored.
Input: { rows: Array<{ label: string; estimate: number; ciLower: number; ciUpper: number; n?: number }>; reference?: number }
No renderer wired.
When to use / when not to use
When to use: Use when the answer is an ESTIMATE WITH UNCERTAINTY across groups or against a reference: pay-equity gaps centered on zero, engagement/manager scores with CIs, ranking-with-uncertainty. Reading: dot = estimate, line = 95% CI, vertical rule = no-effect/reference; judge each interval against the rule.
When not to use: Do not use mirrored/butterfly bars or two-bar comparisons when the output is a gap estimate — they force mental subtraction and hide uncertainty. Do not use for a single value vs a target (use `viz.bullet-bar`). Do not infer significance from two overlapping group CIs; this chart shows the difference's own interval.
Renderer:
src/lib/visualizations/templates/ForestPlot.tsxviz.waterfall
Waterfall (bridge)
flow-bridgecategorical-breakdownBridge chart showing how a starting total reaches an ending total through sequential positive and negative contributions. Canonical for headcount reconciliation (start → hires → exits → end) and compensation walks.
Input: { stages: Array<{ label: string; delta: number; isTotal?: boolean }> }
No renderer wired.
When to use / when not to use
When to use: Use when the question is how a net total is built from additive/subtractive contributors over an ordered sequence. Reading: totals anchor at zero; each rise/fall is a signed delta off the running level.
When not to use: Do not use for simple stage-count comparison (use ranked bars) or for pure part-to-whole at one point in time (use a 100% stacked bar). Do not use a stacked bar to fake a bridge — same-category segments can't be compared.
Renderer:
src/lib/visualizations/templates/Waterfall.tsxviz.slopegraph
Slopegraph (before / after)
before-aftertrendcomparison-pairTwo-point slopegraph — each item is a line from its before value to its after value. The slope encodes direction and magnitude of change; crossings show rank changes.
Input: { items: Array<{ label: string; before: number; after: number }>; beforeLabel?: string; afterLabel?: string }
No renderer wired.
When to use / when not to use
When to use: Use for before/after change or rank change across exactly two ordered points (then vs now, pre/post program), especially across many items where a full line chart would be empty between endpoints.
When not to use: Do not use for a full time trajectory (use a line chart) or for start-vs-end magnitude where absolute bars are the point (use `viz.slope-comparison`). Keep item count modest or lines overlap.
Renderer:
src/lib/visualizations/templates/Slopegraph.tsxviz.trend-with-ci-bands
Trend with CI Bands
trendlongitudinalLine chart over time with a shaded pointwise confidence-interval band. The CI band reads as methodology-rigorous attention to uncertainty.
Input: Array<{ period: string; value: number; ciLower?: number; ciUpper?: number }>
When to use / when not to use
When to use: Use when showing a single metric trending over time AND you have pointwise CIs (e.g., Wilson CIs for a proportion). Reading: the band's width tells the reader how much noise vs signal sits in any one period.
When not to use: Do not use without CIs — `viz.sparkline-grid` is a better fit for bare lines, or just use a plain line chart. Do not use for cohort-by-cohort trends — use `viz.small-multiples-by-segment` (one trend per cohort, shared y-axis).
Renderer:
src/lib/visualizations/templates/TrendWithCiBands.tsxviz.small-multiples-by-segment
Small Multiples by Segment
cohort-comparisonlongitudinaltrendResponsive grid of mini line charts — one panel per segment, shared y-axis range — so cross-segment trend comparisons are visually honest.
Input: Array<{ segment: string; points: Array<{ period: string; value: number }> }>
Engineering
Sales
Marketing
Finance
Customer Success
Operations
Product
Legal
y: Voluntary turnover rate
When to use / when not to use
When to use: Use when comparing the SHAPE of a trend across many segments (5+ panels). Shared y-axis range matters: a segment that looks like it's dropping fast might just have a different baseline.
When not to use: Do not use for 1-2 segments — `viz.comparison-pair` or two overlaid trend lines read better. Do not use when you want to compare ONE segment's absolute level to a baseline — use `viz.percentile-callout`.
Renderer:
src/lib/visualizations/templates/SmallMultiplesBySegment.tsxviz.cohort-survival-curve
Cohort Survival Curve
survivallongitudinalcohort-comparisonKaplan-Meier-style step survival curves per cohort with shaded pointwise CI bands. Compares retention trajectories across cohorts using the same methodology demographers use.
Input: Array<{ cohort: string; period: number; survivalProbability: number; ciLower: number; ciUpper: number }>
0-1 yr tenure1-3 yr tenure3+ yr tenureWhen to use / when not to use
When to use: Use when answering 'how long do people last' across two or more cohorts (tenure bands, hire years, manager type, comp percentile). The step-function shape reads as serious survival analysis.
When not to use: Do not use for non-survival metrics. Do not use for a single cohort with no comparison group — `viz.trend-with-ci-bands` is cleaner. Do not use for very short period grids (<4 points) — the steps will look noisy.
Renderer:
src/lib/visualizations/templates/CohortSurvivalCurve.tsxviz.funnel-stage-conversion
Funnel Stage Conversion
funnel-conversioncategorical-breakdownHorizontal funnel bars by stage with prior-stage conversion percentages. Reads top-to-bottom as the recruiting / pipeline reader expects.
Input: Array<{ stage: string; count: number; conversionFromPrior?: number }>
Screened: 28% from prior stage
Phone Screen: 52% from prior stage
Onsite: 41% from prior stage
Offer Extended: 37% from prior stage
Offer Accepted: 77% from prior stage
When to use / when not to use
When to use: Use for any sequential-stage conversion analysis: hiring funnel, performance-review participation, training completion, exit-process steps. Prior-stage % is the diagnostic; absolute counts give scale.
When not to use: Do not use for non-sequential category breakdowns — use a bar chart. Do not use when stages aren't strictly nested (every stage-N person must have been a stage-N-1 person).
Renderer:
src/lib/visualizations/templates/FunnelStageConversion.tsxviz.compa-ratio-distribution
Compa-Ratio Distribution by Level
distributioncomparison-paircohort-comparisonPer-level histogram with median dashed-line + p25 / p75 markers. Stacked one row per level to compare distribution shapes (skew, spread) across the comp hierarchy.
Input: Array<{ level: string; compaRatios: number[] }>
IC1
n=40 · median 0.94 · p25 0.89 · p75 1.00
IC2
n=58 · median 1.00 · p25 0.95 · p75 1.07
IC3
n=72 · median 1.01 · p25 0.97 · p75 1.07
IC4
n=48 · median 1.04 · p25 0.97 · p75 1.11
M1
n=22 · median 1.08 · p25 1.03 · p75 1.14
When to use / when not to use
When to use: Use when showing comp-band positioning per level: where employees sit relative to the band's p50, and how wide each level's spread actually is. The median tick + IQR markers make the diagnostic explicit.
When not to use: Do not use for a single level — `viz.distribution-histogram` is the simpler choice. Do not use for non-compensation distributions — the methodology framing (medians/IQR vs means/SD) is specifically a comp pattern.
Renderer:
src/lib/visualizations/templates/CompaRatioDistribution.tsxviz.sparkline-grid
Sparkline Grid
trendmatrix-gridDense table of mini trends (one row per metric) with latest value and total delta. Designed for at-a-glance scanning of 8-30+ metrics in one frame.
Input: Array<{ label: string; points: number[] }>
Metric Trend Latest Δ vs first Voluntary turnover 0.11 +0.03 Time to fill (days) 36.98 -3.92 Engagement score 4.06 +0.29 Span of control 7.58 +0.30 Promotion velocity 0.13 +0.02 Internal mobility 0.09 +0.03 Regrettable attrition 0.05 +0.01 Headcount growth (yoy) 0.08 -0.06 When to use / when not to use
When to use: Use for the 'monitor everything' surface: an executive scorecard, a metric-explorer index, a daily ops digest. Cells are intentionally small so density doesn't drop with row count.
When not to use: Do not use as a primary chart — sparklines aren't meant for inspection. Do not use when you need axes (no axis labels per cell). Do not use for single-metric deep dives — promote to `viz.trend-with-ci-bands`.
Renderer:
src/lib/visualizations/templates/SparklineGrid.tsxviz.kt-grid
Kepner-Tregoe Decision Grid
matrix-gridDecision matrix: weighted criteria rows × options columns, scored 0-10 with neutral-shade cell color encoding. Bottom row sums the weighted total and highlights the winner.
Input: { criteria: string[]; options: string[]; scores: number[][]; weights: number[] }
Criterion Weight Buy vendor X Buy vendor Y Build in-house Cost to implement 0.25 8.0 6.5 4.0 Time to value 0.20 7.5 7.0 5.5 Org disruption 0.15 6.0 5.5 8.0 Methodology rigor 0.20 6.5 7.5 9.0 Adoption likelihood 0.20 7.5 7.0 5.0 Weighted total 7.20★ 6.75 6.10 When to use / when not to use
When to use: Use for any explicit Kepner-Tregoe-style decision: vendor evaluation, comp-strategy choice, intervention selection. The weighted-sum row makes the tradeoff explicit instead of hand-wavy.
When not to use: Do not use for non-decision contexts. Do not use when criteria aren't independent (entangled criteria need a different scoring frame). Do not use with >10 options or >12 criteria — the matrix becomes unreadable.
Renderer:
src/lib/visualizations/templates/KepnerTregoeGrid.tsxviz.distribution-histogram
Distribution Histogram
distributionSingle-variable histogram with mean and median reference lines. Reads as the canonical 'shape of this column' visualization.
Input: { values: number[]; binCount?: number; xLabel?: string }
When to use / when not to use
When to use: Use for any one-dimensional continuous distribution: tenure, salary, engagement scores, time-to-fill. Mean + median together flag skew at a glance (large gap = skewed).
When not to use: Do not use for split distributions — promote to `viz.compa-ratio-distribution` (per-level panels). Do not use for categorical breakdowns — use a bar chart. Do not use with n<30 — the bins will be noise.
Renderer:
src/lib/visualizations/templates/DistributionHistogram.tsxviz.comparison-pair
Comparison Pair
comparison-paircohort-comparisonSide-by-side bar comparison with CI whiskers, delta callout, and a coarse significance flag (overlapping vs non-overlapping 95% CIs).
Input: Array<{ group: string; value: number; ciLower?: number; ciUpper?: number; n: number }>
Manager: Tenured: 0.082 (95% CI 0.072–0.092) · n=1,840
Manager: New (<1yr): 0.137 (95% CI 0.121–0.154) · n=920
Δ = +0.055 — Significant (non-overlapping 95% CIs)
When to use / when not to use
When to use: Use for binary-cohort comparisons: control vs treatment, manager-tenured vs manager-new, before vs after. CIs and the sig flag prevent the 'two bars look different but aren't actually different' failure mode.
When not to use: Do not use for >3 groups — bars get cluttered and the pairwise sig flag stops being useful. Use a small-multiples or distribution view instead. Do not use without n — the sig flag depends on sample-size-aware CIs.
Renderer:
src/lib/visualizations/templates/ComparisonPair.tsxviz.percentile-callout
Percentile Callout
percentile-calloutSingle-stat hero with the value, its percentile band (visual marker on a 0–100 axis), and a baseline comparison with absolute and percent delta.
Input: { value: number; percentile: number; baseline: number; baselineLabel: string; unit?: string; metricLabel?: string }
Engineering 12-month voluntary turnover
0.087
vs Industry median (US tech, 100-500 emp): 0.142 (-38.7%)
p0Percentilep100You are at p28 of the comparison cohort.
When to use / when not to use
When to use: Use for an 'answer-first' summary card: 'your value is X, that's pNN of the comparison cohort, baseline is Y'. The percentile band gives shape context without a full distribution chart.
When not to use: Do not use for a trend over time — promote to `viz.trend-with-ci-bands`. Do not use when there's no baseline cohort — a plain metric tile is cleaner. Do not use when percentile is unknown — drop to a simple delta tile.
Renderer:
src/lib/visualizations/templates/PercentileCallout.tsxviz.range-strip
Range Strip
compensationcomparison-paircategorical-breakdownDiscrete-block compensation range visualization on a shared dollar scale. Each row is a labeled set of fixed-width boxes; highlighted segments form the active pay range with optional reference marker.
Input: { rows: Array<{ label: string; segments: Array<{ value: number; highlighted?: boolean; label?: string }>; markerPosition?: number; markerLabel?: string }> }
No renderer wired.
When to use / when not to use
When to use: Use for pay-structure visualization: where a band actually sits on a shared dollar scale, level-over-level overlap, market positioning. The shared step-sized boxes (e.g., $10K each) keep cross-row comparison honest in a way that bar widths can't.
When not to use: Do not use for continuous distributions — use `viz.compa-ratio-distribution`. Do not use when ranges aren't on the same numeric scale (e.g., one row is salary, another is bonus pct). Do not use with >12 rows — the strip table loses readability.
Renderer:
src/lib/visualizations/render/templates/range-strip.tsxviz.confidence-band
Confidence Band
forecasttrendlongitudinalLine chart over a numeric x-axis with inner and outer pointwise prediction bands (e.g., 1-sigma and 2-sigma). Reads as forecast-with-uncertainty.
Input: { data: Array<{ x: number; y: number; lo1?: number; hi1?: number; lo2?: number; hi2?: number }>; xLabel?: string; yLabel?: string }
No renderer wired.
When to use / when not to use
When to use: Use for metric forecasting with explicit uncertainty: attrition forecast, headcount projection, comp-cost forward look. Inner band typically represents 1-sigma, outer band 2-sigma — the wider band signals the methodologically honest answer.
When not to use: Do not use without bands — degrade to a plain trend line or `viz.trend-with-ci-bands` (period-string axis + single CI band). Do not use for historical-only series — the band framing implies a forecast.
Renderer:
src/lib/visualizations/render/templates/confidence-band.tsxviz.box-whisker
Box & Whisker
distributioncohort-comparisonPer-group five-number-summary candlestick: min / Q1 / median / Q3 / max. Compares spread + central tendency across categories at a glance.
Input: Array<{ label: string; min: number; q1: number; median: number; q3: number; max: number }>
No renderer wired.
When to use / when not to use
When to use: Use when comparing distribution shape across 2–12 groups (departments, levels, hire cohorts): the box's IQR shows spread, the whiskers show range, the median tick shows skew. Pre-aggregate to the five-number summary before plotting — this template doesn't compute quartiles.
When not to use: Do not use for raw points — use `viz.distribution-histogram` (single group) or `viz.compa-ratio-distribution` (per-level histograms). Do not use with n<10 per group — the quartiles are noise. Do not use without the 5-number summary computed upstream.
Renderer:
src/lib/visualizations/render/templates/box-whisker.tsxviz.bullet-bar
Bullet Bar
target-vs-actualcomparison-paircategorical-breakdownHorizontal bars with quantitative background bands, an actual-value bar, and a target marker. Compact target-vs-actual view per KPI row.
Input: Array<{ label: string; ranges: number[]; value: number; marker: number }>
No renderer wired.
When to use / when not to use
When to use: Use for KPI scorecards where each metric has a target + qualitative bands (e.g., poor/satisfactory/good). The marker (target) and value-bar render side by side so 'are we hitting it' reads at a glance. Stephen Few's bullet-chart shape; replaces gauge dials.
When not to use: Do not use without a target — degrade to a plain bar chart. Do not use when ranges don't share a unit. Do not use for time-series — promote to `viz.trend-with-ci-bands`.
Renderer:
src/lib/visualizations/render/templates/bullet-bar.tsxviz.sparkline-rows
Sparkline Rows
trendmatrix-gridcategorical-breakdownLabeled rows with individual sparklines for compact multi-metric overview. Each row shows a label, formatted current value, and trend mini-chart.
Input: { rows: Array<{ label: string; value: string; data: number[] }> }
No renderer wired.
When to use / when not to use
When to use: Use for compact dashboard summaries and watchlists: 5–20 metrics, each with its own trend. Reads more like a 'list' than a 'chart'; sibling of `viz.sparkline-grid` but optimized for narrower frames (one trend per row, formatted value as text).
When not to use: Do not use for single-metric deep dives — promote to `viz.trend-with-ci-bands`. Do not use for very dense scoreboards (30+ rows) — use `viz.sparkline-grid` with table layout. Do not use when values aren't pre-formatted strings.
Renderer:
src/lib/visualizations/render/templates/sparkline-rows.tsxviz.driver-quadrant
Driver Quadrant (Importance × Performance)
matrix-gridcomparison-paircategorical-breakdownKey drivers plotted as an Importance × Performance matrix: x = importance (correlation with the outcome), y = score vs expected. Two thresholds split the plane into four triage zones — Fix first (important & weak), Maintain (important & strong), Monitor, Low priority. Bubble size = sample size; a dashed ring marks importance sourced from a research PRIOR rather than sufficient client data. The canonical key-driver triage chart (the BubbleScatter has no quadrant framing).
Input: { points: Array<{ id: string; label: string; importance: number /* 0-100 */; score: number /* 0-100 */; n?: number; basis?: "data" | "prior" }>; scoreThreshold?: number; importanceThreshold?: number; xLabel?: string; yLabel?: string }
No renderer wired.
When to use / when not to use
When to use: Use to turn a key-driver / relative-importance analysis into an action list: which sub-measures are both important AND underperforming (fix first) vs important-and-strong (maintain). Reading: top-right quadrant = act now; bubble size = n; dashed ring = importance from a prior, not enough client data yet.
When not to use: Do not use for a plain two-variable scatter without a priority interpretation (use `viz.bubble-scatter`), or for an estimate-with-uncertainty across groups (use `viz.forest-plot`). Do not read importance as causal — it is correlation strength.
Renderer:
src/lib/visualizations/templates/DriverQuadrant.tsxviz.strategy-map
Strategy Map (objective → driver → measure cascade)
compositioncategorical-breakdownThe objective → driver → measure cascade (Minto-pyramid flavor) — a Strategy/Concept template for executive argument, not data display. Ties the arcane measures a leader cannot intuit (compa-ratio, NA%, connectivity index) UP to a driver and up to the strategic objective they serve. Three tiers read top-down: one objective → a row of ~2–4 drivers → the measures beneath each, each with an optional value vs benchmark + status (on-track / watch / off-track). An off-track measure lights the alert hue and ladders visibly to the objective it threatens. Mined from the Periodic Table of Visualization Methods — the 'Strategy' category the data-only catalog lacked.
Input: { objective: string; drivers: Array<{ id: string; label: string; measures: Array<{ id: string; label: string; value?: number; benchmark?: number; unit?: string; status?: "on-track" | "watch" | "off-track" }> }>; asOf?: string }
No renderer wired.
When to use / when not to use
When to use: Use to answer 'why does this number matter?' — connecting measures to the strategy they serve for an exec audience (the comp/PA explanation-and-reporting layer, PAT-171; the metric↔strategy mapping, PAT-176). Reading: top-down objective → drivers → measures; status hue + value-vs-benchmark flag the measures threatening the objective.
When not to use: Do not use for a quantitative relationship or trend (use a data chart). Do not use to triage drivers by importance × performance (use `viz.driver-quadrant`), or to decompose a single composite score (use `viz.index-meter`). It argues a hierarchy; it does not plot data.
Renderer:
src/lib/visualizations/templates/StrategyMap.tsxviz.bridge
Bridge (before → after gap + path across)
flow-bridgebefore-afterThe before → after gap and the path that closes it — a Metaphor/Strategy template for any remediation narrative (pay-fairness gap closure, comp-waste reduction, attrition turnaround). A START anchor bar, optional intermediate STEP bars that build (or draw down) toward the END anchor bar, and the gap called out as the span between start and end. `goalDirection` decides whether closing the gap is an increase or a decrease. The exec one-liner: where we are, where we need to be, and how we get there.
Input: { start: { label: string; value: number }; end: { label: string; value: number }; steps?: Array<{ label: string; delta: number }>; unit?: string; goalDirection?: "increase" | "decrease" }
No renderer wired.
When to use / when not to use
When to use: Use to tell a remediation / change story to an exec: the current state, the target, the gap, and the contributing moves that bridge it. Reading: left = today, right = target; the dashed deck connects the steps; the right-edge bracket calls out the gap.
When not to use: Do not use for a full account-level variance decomposition (use `viz.waterfall`), a time series (use a trend), or a single value vs target (use `viz.bullet-bar`). It argues a two-anchor journey, not a continuous trend.
Renderer:
src/lib/visualizations/templates/BridgeGap.tsxviz.strategy-2x2
Strategic 2×2 (labeled positioning matrix)
matrix-gridcomparison-pairA labeled, annotated strategic 2×2 (magic-quadrant / BCG / portfolio) — the Strategy-category generalization of the analytic `viz.driver-quadrant`. Two arbitrary 0–100 axes split at the midpoint into four NAMED quadrants; items plotted as points (size = optional n, emphasis = the highlighted item). Unlike driver-quadrant there is no fixed fix-first semantics — the caller names each quadrant and the axes' low/high poles. The exec framing device for portfolio/prioritization arguments: program cost × activation impact (PAT-177), flight-risk × value, build/buy/partner.
Input: { xAxis: { label: string; lowLabel?: string; highLabel?: string }; yAxis: { label: string; lowLabel?: string; highLabel?: string }; quadrantLabels?: { tl?: string; tr?: string; bl?: string; br?: string }; points: Array<{ id: string; label: string; x: number /* 0-100 */; y: number /* 0-100 */; n?: number; emphasis?: boolean }>; emphasizeTopRight?: boolean }
No renderer wired.
When to use / when not to use
When to use: Use to position items in a named 2×2 for a prioritization / portfolio argument with arbitrary axes. Reading: each point sits in a named quadrant; emphasis = the item the argument is about; bubble size = n.
When not to use: Do not use for the specific Importance × Performance key-driver triage (use `viz.driver-quadrant`), or for a plain two-variable scatter (use `viz.bubble-scatter`).
Renderer:
src/lib/visualizations/templates/Strategy2x2.tsxviz.cause-effect
Cause-Effect (Ishikawa / fishbone)
compositioncategorical-breakdownThe Ishikawa / fishbone cause-effect diagram — a Concept-category root-cause decomposition. The EFFECT sits at the head of a horizontal spine; each driver CATEGORY is a diagonal bone off the spine; the contributing CAUSES are twigs along each bone. Purpose-built for diagnosis arguments — decompose attrition, comp-waste, or a quality failure into the categories that drive it, so a leader sees the structure of the problem, not just the number.
Input: { effect: string; categories: Array<{ label: string; causes: string[] }> }
No renderer wired.
When to use / when not to use
When to use: Use to argue the structure of a problem's causes for a diagnosis / root-cause discussion (~2–6 categories, a few causes each). Reading: spine points into the effect; bones = categories; twigs = causes.
When not to use: Do not use to quantify how much each driver contributes (use `viz.driver-quadrant` or `viz.waterfall`), or to cascade a strategy down to measures (use `viz.strategy-map`). It is qualitative structure, not magnitude.
Renderer:
src/lib/visualizations/templates/CauseEffect.tsxviz.roadmap
Roadmap (phased rollout / dissipation forecast)
forecastbefore-afterA horizontal phased plan over a time axis — each phase a bar spanning start→end, colored by status (done / active / planned), with an optional 'now' marker. A Strategy-category template for the rollout narrative and — directly — the comp-waste DISSIPATION forecast (PAT-175: how long until the waste self-corrects via market movement + attrition); the phases become the self-correction horizon.
Input: { phases: Array<{ label: string; start: number; end: number; status?: "done" | "active" | "planned"; items?: string[] }>; axisLabel?: string; nowMarker?: number }
No renderer wired.
When to use / when not to use
When to use: Use for a phased plan or a self-correction / dissipation horizon on a shared time axis. Reading: each row is a phase bar from start to end; hue = status; the dashed line marks now.
When not to use: Do not use for a measured time series (use a trend / `viz.multi-line`) or discrete dated events without duration (use `viz.timeline-milestone`). It shows spans, not points or values.
Renderer:
src/lib/visualizations/templates/Roadmap.tsxviz.decision-tree
Decision Tree (recommendation branch logic)
compositioncategorical-breakdownThe branch logic behind a recommendation: DECISION nodes (a choice the org controls), CHANCE nodes (an uncertain event), and OUTCOME leaves (where a branch lands, with an optional EV / probability / payoff annotation). A Concept/Strategy template that pairs with forecasting EVPI/EVSI + the K–T grid, and renders the comp address-now-vs-do-no-harm fork as a decision under uncertainty. Left→right tidy layout (depth = x, leaves get y slots, internal nodes center on their children).
Input: { root: { label: string; kind?: "decision" | "chance" | "outcome"; detail?: string; branches?: Array<{ label: string; node: <recursive> }> } }
No renderer wired.
When to use / when not to use
When to use: Use to argue the structure of a recommendation under uncertainty — choices, chance events, and the outcomes each path reaches. Reading: squares = decisions, circles = chance, pills = outcomes; branch labels sit on the connectors.
When not to use: Do not use to cascade a strategy to measures (use `viz.strategy-map`) or to decompose causes (use `viz.cause-effect`). It is a forward branch structure, not a hierarchy of contributions.
Renderer:
src/lib/visualizations/templates/DecisionTree.tsxviz.iceberg
Iceberg (visible tip vs hidden mass)
compositioncategorical-breakdownThe iceberg metaphor — a small visible tip above a waterline, a large hidden mass below — for 'what you see is a fraction of what's there'. Purpose-built for comp waste you can't see (a small reported overspend above a large hidden geo / level / misclassification mass) and stored-vs-activated value. When both sides carry a value, the tip's height reflects the visible fraction (honest sizing). Each side has a label, optional value, and a few itemized drivers.
Input: { title?: string; waterline?: string; above: { label: string; value?: number; items?: string[] }; below: { label: string; value?: number; items?: string[] }; unit?: string }
No renderer wired.
When to use / when not to use
When to use: Use to argue that a visible number is a fraction of a larger hidden total (hidden comp waste, stored vs activated value, true vs reported cost). Reading: the tip = visible, the submerged mass = hidden; tip height ∝ visible share when values are given.
When not to use: Do not use for a precise part-to-whole breakdown (use `viz.waterfall` or a composition chart) or more than two layers. It is a two-layer visible/hidden metaphor, not a quantitative decomposition.
Renderer:
src/lib/visualizations/templates/IcebergLayers.tsxviz.chord-diagram
Chord Diagram (group→group aggregated flow)
flow-bridgecompositionA group→group aggregated flow ring — the PRIVACY-SAFE ONA view (groups, never individuals; fits the spoke's k-anonymity posture). Groups sit on a circle; ribbons between them encode aggregated tie volume (stroke width ∝ value). Use for department→department collaboration, cross-team handoffs, transition flows between named states. Group order is the only layout freedom and is caller-controlled (no stochastic layout → no seed). SECONDARY (per Mike): prefer the network's summary measures + benchmarks (network-analysis spoke) for 'is this good or bad'; this is a catalog option for when an aggregated-flow picture is genuinely warranted.
Input: { groups: Array<{ id: string; label?: string }>; flows: Array<{ source: string; target: string; value: number }>; title?: string }
No renderer wired.
When to use / when not to use
When to use: Use to show aggregated flow/affinity between a small set of GROUPS (departments, locations, job families) without exposing individuals. Reading: groups on the ring; a thicker ribbon = more aggregated volume between that pair.
When not to use: Do not use to show individual-level ties (privacy) or to answer 'is the network healthy?' — boil the graph down to summary measures + benchmarks instead (viz.bullet-bar / viz.forest-plot / viz.severity-heat-table; network-analysis spoke). Do not use for >~10 groups (the ring gets unreadable).
Renderer:
src/lib/visualizations/templates/ChordDiagram.tsxviz.node-link-network
Node-Link Network (deterministic seeded layout)
matrix-gridcompositionA node-link network with a DETERMINISTIC seeded layout. Node SIZE ∝ centrality by AREA (not radius); node COLOR = community; edge width ∝ tie weight. A FIXED SEED is REQUIRED — the layout is stochastic, so the seed makes it reproducible (the book insists: 'always set the same seed'). SECONDARY by design (per Mike): node-link 'hairballs' rarely answer 'is this good or bad' — the headline ONA deliverable is the network boiled down to summary measures + benchmarks (network-analysis spoke). This exists because the research (McNulty Ch.3) lists it as a catalog option; above a density threshold the caller should fall back to chord / adjacency-matrix (hairball guard).
Input: { nodes: Array<{ id: string; label?: string; centrality?: number; community?: string | number }>; edges?: Array<{ source: string; target: string; weight?: number }>; seed: number; title?: string }
No renderer wired.
When to use / when not to use
When to use: Use only when a node-link picture is genuinely warranted (small, sparse network; an exploratory or illustrative view). Reading: bigger node = more central (by AREA); same color = same community; thicker edge = stronger tie. Always pass a fixed seed.
When not to use: Do not use as the default ONA deliverable — boil the network down to summary measures + benchmarks (network-analysis spoke; viz.bullet-bar / viz.forest-plot / viz.severity-heat-table). Do not use on a dense network (hairball) — fall back to viz.chord-diagram or an adjacency matrix. Layout position carries no meaning unless attribute-anchored.
Renderer:
src/lib/visualizations/templates/NodeLinkNetwork.tsxviz.salary-jitterplot
Salary Jitterplot (pay by level + band overlay)
compositioncomparison-pairThe compensation workhorse: individual pay (y) by level / job-family (x) as jittered points, with the per-group pay-band (min / mid / max) overlaid per column. Jittered dots show the real spread of individuals inside (and outside) the band — compression, over/under-band placement, dispersion — read across levels on a shared y-scale. Jitter is DETERMINISTIC (SSR-reproducible, no client randomness). MIN-N: any group below `minN` (default 5) is SUPPRESSED — its individual dots never plot; a visible placard stands in its column (individual-pay privacy is non-negotiable).
Input: { groups: Array<{ id: string; label: string; band?: { min: number; mid?: number; max: number }; points: number[] }>; minN?: number; unit?: string; asOf?: string }
No renderer wired.
When to use / when not to use
When to use: Use to show where people actually sit inside their pay bands across levels/families — the single most-requested comp picture (PAT-171 dashboard deliverable). Reading: each dot = a person's pay; the box = the policy band (dashed line = midpoint); dots above/below the box = out-of-band placement; spread = dispersion.
When not to use: Do not use for a single value vs a target (use `viz.bullet-bar`), an estimate-with-uncertainty across groups (use `viz.forest-plot`), or a smoothed shape of one distribution (use `viz.distribution-histogram` / `viz.compa-ratio-distribution`). Never plot individual-pay dots for a group below the min-N floor — the template suppresses them; do not work around it.
Renderer:
src/lib/visualizations/templates/SalaryJitterplot.tsxviz.population-pyramid
Population Pyramid (headcount by level × binary split)
compositioncomparison-pairHeadcount by level (rows) split into a binary comparison (two groups), drawn as mirrored horizontal bars off a shared center spine. Levels stack base-to-apex; the LEFT group grows leftward, the RIGHT group rightward. Reads workforce structure (healthy taper vs top-heavy), representation, and per-level group imbalance at a glance — gender by level, hires-vs-exits by level, two-org comparison. MIN-N: each cell (a level × one group) below `minN` (default 5) is SUPPRESSED per-cell (its count never renders; a hatched stub + 'n<minN' mark stands in) so one small side never hides the other.
Input: { rows: Array<{ level: string; left: number; right: number }>; leftLabel: string; rightLabel: string; minN?: number; asOf?: string }
No renderer wired.
When to use / when not to use
When to use: Use to compare two groups' headcount across levels — the workforce-structure / representation picture (PAT-171 dashboard). Reading: rows = levels (base at the bottom); left/right bar length = each group's count; symmetry = parity, skew = imbalance.
When not to use: Do not use for more than two groups (use a grouped bar or small multiples), for a part-to-whole composition (use `viz.waffle-bar`), or for a flow between states (use `viz.alluvial`). Never expose a below-min-N cell count — the template suppresses it per-cell.
Renderer:
src/lib/visualizations/templates/PopulationPyramid.tsxviz.range-target-bullet
Range Target Bullet
compensationtarget-vs-actualcomparison-pairHorizontal pay band with gradient track, triangular target cue, and a vertical bullet for observed pay — compact comp-strip diagnostic.
Input: { rows: Array<{ label: string; floor: number; target: number; ceiling: number; current: number }>; formatValue?: (n: number) => string }
IC3 Engineer
now $126K · tgt $130K
IC4 Engineer
now $160K · tgt $152K
When to use / when not to use
When to use: Show where an employee or cohort lands inside an explicit dollar floor/target/ceiling trio on one scale per row.
When not to use: When you lack explicit numeric band edges — degrade to percentile callout instead.
Renderer:
src/lib/visualizations/templates/RangeTargetBullet.tsxviz.range-dot-plot
Range Dot Plot
compensationdistributionPer-role horizontal band shading with jitter-free dot markers showing each employee observation on a shared monetary axis.
Input: { bands: Array<{ label: string; min: number; max: number }>; dots: Array<{ id: string; label: string; value: number }> }
IC2
IC3
When to use / when not to use
When to use: Compare employee placement inside multiple pay bands simultaneously while keeping outliers visible.
When not to use: When you only have aggregates — reuse `viz.box-whisker` histogram instead.
Renderer:
src/lib/visualizations/templates/RangeDotPlot.tsxviz.range-strip-aligned
Range Strip Aligned By Midpoint
compensationcomparison-pairEach row aligns its midpoint vertically so overlaps and skew versus policy targets read quickly across roles.
Input: { rows: Array<{ label: string; midpoint: number; halfWidth: number }>; formatTick?: (n: number) => string }
Junior PM$120KSenior PM$150KStaff PM$178KWhen to use / when not to use
When to use: Structural compensation comparisons emphasizing midpoint alignment versus absolute dollar spread.
When not to use: Situations needing shared absolute min/max anchors — reuse `viz.range-strip`.
Renderer:
src/lib/visualizations/templates/RangeStripAligned.tsxviz.interactive-range-strip
Interactive Range Strip
compensationdistributionScatter rendition of dots per labeled band with Recharts Tooltip for employee-level hovering.
Input: { bands: Array<{ label: string; min: number; max: number }>; dots: Array<{ id: string; band: string; value: number }> }
When to use / when not to use
When to use: Operator desktops where hovered employee detail complements static range charts.
When not to use: SSR-only thumbnails without hydration — PNG render path unsupported today.
Renderer:
src/lib/visualizations/templates/InteractiveRangeStrip.tsxviz.multi-line
Multi-Line Forecast Compare
forecasttrendlongitudinalOverlaid monotone lines keyed by categorical series with shared categorical x-axis ticks.
Input: { data: Record<string,string|number>[]; categoryKey: string; seriesKeys: string[]; yLabel?: string }
When to use / when not to use
When to use: When two or more related metrics evolve over identical calendar periods.
When not to use: When envelopes include CI overlays — reuse `viz.trend-with-ci-bands` or `viz.confidence-band`.
Renderer:
src/lib/visualizations/templates/MultiLine.tsxviz.slope-comparison
Slope Comparison
comparison-pairtrendPer-item delta bars from start-vs-end checkpoints with proportional percent deltas labeled.
Input: { items: Array<{ label: string; start: number; end: number }>; valueLabel?: string }
When to use / when not to use
When to use: Headline comparisons of KPI directionality between two audited periods.
When not to use: Fine-grained chronological noise — reuse line trends instead.
Renderer:
src/lib/visualizations/templates/SlopeComparison.tsxviz.bump
Bump Chart
cohort-comparisonmatrix-gridLine chart encoding ordinal ranks per entity across ordered periods.
Input: { periods: string[]; ranksByEntity: Record<string, number[]> /* rank positions 1=best aligned to periods */ }
When to use / when not to use
When to use: Demonstrate leaderboard churn for a handful of named entities.
When not to use: When raw magnitudes matter more than ordinal ordering.
Renderer:
src/lib/visualizations/templates/Bump.tsxviz.bubble-scatter
Bubble Scatter
distributionTwo-dimensional quantitative scatter scaled by bubble size for a third latent dimension.
Input: { points: Array<{ id?: string; label?: string; x: number; y: number; z: number }>; xLabel?: string; yLabel?: string }
When to use / when not to use
When to use: Correlation storytelling with tertiary emphasis via diameter (e.g., headcount-adjusted outliers).
When not to use: When z-dimension unreliable — degrade to planar scatter equivalents.
Renderer:
src/lib/visualizations/templates/BubbleScatter.tsxviz.heatmap
Categorical Heatmap
matrix-gridcategorical-breakdownRow/column lattice with continuous encodings derived from pairwise categorical intersections.
Input: { rows: string[]; cols: string[]; cells: Array<{ row: string; col: string; value: number }>; valueSuffix?: string }
FemaleMaleNon-binaryPrefer notEngineering0.941.060.880.75Sales1.120.981.041.09G&A1.031.080.910.87When to use / when not to use
When to use: Intersectional breakdowns across two nominal dimensions.
When not to use: Temporal ordering priorities — transpose to longitudinal strip instead.
Renderer:
src/lib/visualizations/templates/VizHeatmap.tsxviz.strip-dot
Strip Dot Beeswarm Sketch
distributionSingle-axis jittered scatter approximating bee-swarm readability without force simulation.
Input: { points: Array<{ id?: string; value: number; lane?: number }>; lanes?: number; stripLabel?: string }
Compa-ratio sample (pseudo beeswarm jitter)
When to use / when not to use
When to use: Showing raw observations per metric where density matters more than quartiles.
When not to use: When strict statistical jitter is mandated — escalate to analytic pipeline.
Renderer:
src/lib/visualizations/templates/StripDot.tsxviz.stacked-area
Stacked Area Composition
compositionlongitudinalLayered monotone areas conveying cumulative composition across ordered periods.
Input: { periods: string[]; series: Record<string /* category */, number[]> /* aligned arrays */ }
When to use / when not to use
When to use: Demonstrate mixes (cost components, HC mix) accumulating over FY slices.
When not to use: Additive constraint violated (negative contributors) unless pre-normalized upstream.
Renderer:
src/lib/visualizations/templates/StackedArea.tsxviz.waffle-bar
Waffle Bar Tiles
compositioncategorical-breakdownMultiple horizontal waffle strips whose block counts summarize percentage shares.
Input: { buckets: Array<{ label: string; value: number; color?: string }>; cols?: number }
IC46.0%M28.0%Dir+18.0%Support8.0%When to use / when not to use
When to use: Audience-friendly composition tiles when donut charts feel too sugary.
When not to use: >8 buckets — degrade to stacked bar percentages.
Renderer:
src/lib/visualizations/templates/WaffleBar.tsxviz.waffle-percent
Waffle Percent
percentile-callout10×100 grid emphasizing a solitary percentage storyline.
Input: { percent: number; label?: string }
Offer acceptance · rolling 180d
64.3%
When to use / when not to use
When to use: Executive tiles that must telegraph attainment vs nominal 100 baseline.
When not to use: Fractional composites — use waffle-bar buckets instead.
Renderer:
src/lib/visualizations/templates/WafflePercent.tsxviz.alluvial
Alluvial Flow
compositionTwo-column chord-like flows between categorical cohorts annotated with Bézier ribbons sized by weights.
Input: { leftTitle?: string; rightTitle?: string; lanes: Array<{ from: string; to: string; weight: number }> }
When to use / when not to use
When to use: Transitions such as leveling mix before/after restructuring.
When not to use: Ultra-dense pairwise exchanges — escalate to analytic tables.
Renderer:
src/lib/visualizations/templates/AlluvialFlow.tsxviz.dendrogram
Dendrogram List
matrix-gridIndented tree layout approximating dendrogram readability without D3 clustering.
Input: { root: { id: string; label: string; children?: same[] } }
Org / taxonomy tree · MVP indented layout (not-force layout)
root: CEOnode: Engnode: Platformnode: Paymentsnode: Productnode: Revenuenode: SMBnode: EnterpriseWhen to use / when not to use
When to use: Executive drill-down previews of hierarchies.
When not to use: Large balanced trees (>50 nodes interactive) needing force layout fidelity.
Renderer:
src/lib/visualizations/templates/DendrogramTree.tsxviz.radial-bar
Radial Bar
matrix-gridPolar bar wedges comparing ordinal KPI readiness scores normalized to common radial domain.
Input: { rows: Array<{ category: string; value: number; fill?: string }> }
When to use / when not to use
When to use: Circular scoreboards with ≤8 spokes.
When not to use: Temporal analysis — radial layout loses chronology fidelity.
Renderer:
src/lib/visualizations/templates/RadialBarViz.tsxviz.tile-cartogram
Tile Cartogram Sketch
categorical-breakdownSquare-tile schematic map keyed by abbreviated regions with choropleth fill.
Input: { tiles: Array<{ id: string; value: number }> }
When to use / when not to use
When to use: Rough geographic variance without projecting GeoJSON payloads.
When not to use: District-level precision mandates — escalate to Vega/D3 choropleths.
Renderer:
src/lib/visualizations/templates/TileCartogram.tsxviz.timeline-milestone
Timeline Milestone
longitudinalHorizontal axis with tonal milestone markers keyed by fractional completion positions.
Input: { milestones: Array<{ id?: string; label: string; t: number; tone?: "neutral"|"warn"|"critical" }> }
When to use / when not to use
When to use: Compliance or launch programs tracking ordered gate reviews.
When not to use: When timelines need duration-length segments — escalate to gantts.
Renderer:
src/lib/visualizations/templates/TimelineMilestone.tsxviz.control
Control Chart
trendShewhart-inspired line run with annotated median/UCL/LCL reference rails.
Input: { data: Array<{ index:number; value:number }>; median: number; ucl: number; lcl: number; yLabel?: string }
When to use / when not to use
When to use: Operational metrics where breaches beyond control rails matter.
When not to use: Insufficient samples for meaningful control limits (<20 obs).
Renderer:
src/lib/visualizations/templates/ControlChart.tsx