VennChart
Overlapping sets as area-proportional circles, for how much two or three audiences, tags or cohorts share. Exact at three sets and no further — four circles can't draw all fifteen of their regions, so a `BarChart` of the combinations is the honest chart past that.
<UiVennChart :data="..." :setsKey="..." :valueKey="..." :ariaLabel="..." />Needs the charts module
This component ships in @jgastager/bundy-ui/charts, a separate module so projects that never draw a chart don't carry the paint classes these need. Nothing extra to install — just add the module entry. See Charts.
Options
One block per prop — what it does, and what it looks like set.
Data
requireddata is one row per combination: which sets it's about, and how many things are in it. A single-set row is that set's whole total, overlaps included — so Email is all 4,200 people, 1,100 of whom also get push.
Areas are the encoding, not decoration: each circle is sized from its total and each pair is moved until the lens between them measures the overlap exactly. That holds for two and three sets. Four circles cannot draw all fifteen of their regions at any sizes, so past three the layout is the closest least-squares compromise it can find and several overlaps it shows are wrong by construction — rank the combinations as a horizontal BarChart instead.
A single-set row is that set's whole total, overlaps included — the venn.js convention. exclusive flips it to one disjoint count per row. Either way, the number printed in a region is the value you gave for that combination, so under the default reading an A ∩ B of 1,100 includes whatever also falls in C, even though it is printed in the crescent that excludes C.
Rows naming a set that has no total of its own still work: the largest row mentioning it becomes its total, which is the smallest size consistent with the data. A set another one completely swallows loses its name, though — it has no crescent to write it in, so turn the legend on for that case.
Sets key
requiredsetsKey sets which field lists a row's sets. An array for a combination, or a bare name for a single set.
Value key
requiredvalueKey sets which field holds the number. Every circle's area is that number, which is what makes the picture readable as a measurement rather than a diagram.
ARIA label
requiredariaLabel is required, and the place to put the finding. A Venn is read for one thing — how much of one set the other takes — and the geometry never says it out loud.
ARIA description
ariaDescription is the detail the label shouldn't carry.
Exclusive
exclusive switches how a row's number is read. Both charts below are the same data: on the left each row is a set's total, on the right each row is the count in only that region — the shape a GROUP BY over set membership returns.
Colors
colors pins a paint per set, matched to the order the sets first appear in data.
Fill opacity
fillOpacity is how solid each circle is. The overlaps are those fills stacking up, so it's also how strongly a shared region reads — past about 0.5 three circles blend into one dark mass.
The overlaps are the translucent fills stacking up, not paths of their own. That is why there is no per-region color: a region is bounded by however many arcs the circles happen to give it, and the blend already paints it correctly.
Stroke width
strokeWidth is the outline, which is what keeps each set's own edge findable through the fills. Zero drops it and leaves the blend to do all the work.
Labels
labels writes each set's name inside the part of it no other set reaches. Turn it off when the names are longer than the regions have room for and let the legend carry them instead.
- Weekly digest readers
- In-app announcement viewers
- Release notes subscribers
Every label is dropped rather than drawn when it is wider than the region it sits in — a number that crosses an arc reads as the neighbouring region's number. So a long valueFormat, a small height, or a nearly-covered region will quietly thin the labels out. The tooltip always has the full figures.
Values
values writes each row's number in its own region. Off leaves the areas to speak for themselves, with the exact figures in the tooltip.
Value format
valueFormat formats every number the chart shows. A label wider than its own region is dropped rather than drawn across an arc, so a long format quietly thins out the small regions.
Tooltip
tooltip shows the panel on hover: the focused sets with their totals, and the size of the overlap itself set off below them.
Animate
animate is on by default: the circles grow out of their centres on the first draw and ease to their new sizes and positions. Roll new values to compare it with the chart on the right, which has it off. Only the marks move — axes, tooltips and labels always read the real numbers — and a reader who has asked their system for reduced motion gets no motion either way.
Legend
legend is off by default, because labels already names each circle in place — which needs no matching of swatch to shape. It's the fallback when the names don't fit.
- Push
- SMS
Legend position
legendPosition sets where the legend sits relative to the plot.
- Push
- SMS
Legend align
legendAlign places the key horizontally. Centre reads well under a diagram that's itself centred in its box.
- Trial
- Paid
- Trial
- Paid
- Trial
- Paid
Legend orientation
legendOrientation flows the entries in a row or a column.
- Email · 4,200 people
- Push · 2,600 people
- SMS · 1,800 people
Legend group
legendGroup names a group that a standalone UiChartLegend with the same group belongs to, so the legend can sit anywhere — here above the plot — and still dim the other circles while you hover an entry. The chart hands the legend its entries, colors included, so it needs no items. Switch the chart's own legend off if you don't want two.
Height
height is the plot's height in pixels; the diagram is scaled to fit inside it. It's ignored once aspectRatio is set.
Aspect ratio
aspectRatio sizes the chart from its container's width instead of a fixed height.
Loading
loading is a skeleton at the chart's exact height; an empty data array gets the empty state instead.
Examples
The component in real use: compositions that answer "how do I build X with this", rather than one prop at a time.
Past three sets, use a bar chart
Three circles is the limit of the shape, not of the component. Four can't draw all fifteen of their regions at any sizes, so the layout below is the closest compromise it can find — several of the overlaps it shows are wrong by construction. Rank the combinations as bars instead.
API Reference
Generated from the component's source — props, slots and emits as the component actually declares them.
Props
datarequiredRecord<string, unknown>[]setsKeyrequiredstringariaLabelrequiredstringbooleanfalsenumber0.35number1.5(value: number) => stringbooleantruebooleanfalsestringEmits
focusChange[members: string[] | null]Types
interface ChartPlot {
x: number;
y: number;
width: number;
height: number;
/** Right edge, since marks need it more often than the width. */
right: number;
/** Bottom edge, which is also the x axis' baseline. */
bottom: number;
}type ChartColorName = ChartColorFamily | `${ChartColorFamily}-${ChartColorShade}` | `[${string}]`;