A four-state ternary plot in plain SVG — three-part compositions normalised to 100% on an equilateral triangle, with three gridline families, edge-rotated axis ticks, shaded classification zones and a keyboard-reachable mark per row.
Preview in your theme
Loading preview…
This component is part of Pro
The install command, prompt and concept map are included with a Pro license — one purchase unlocks every Pro component, forever.
status="ready" · 24 cores across 3 fields, each one weighed in grams rather than percentages — the chart divides every row by its own total, so a 48 g jar and a 53 g jar with the same mix land on the same spot. Three series, three mark shapes, five named texture zones. Hover a mark or tab into the plot and use the arrow keys; Enter pins.
Topsoil texture by field
24 compositions · average mix 18% Clay, 44% Sand, 38% Silt · 5 named zones
North terrace(9)
South slope(8)
Riverbank(7)
Point at a mark, or tab into the plot and use the arrow keys, for the mix behind it. Click or press Enter to pin one; Escape releases it.
Read a mark by following the gridline that runs parallel to each axis back to its ticks — Clay on the left edge, Sand along the bottom, Silt on the right. The three shares always add to 100%, so the position is the mix and nothing else: two rows with the same mix land together whatever the sample behind them weighed.
Topsoil texture by field— one row per composition. Every position on the triangle is the mix and only the mix: the three shares are normalised by each row's own total and add to 100%. Rows that could not be normalised are listed at the end with the reason.
Sample
Series
Clay
Sand
Silt
Zone
North terrace 08
North terrace
36%
27%
37%
Clay loam
North terrace 07
North terrace
36%
29%
35%
Clay loam
North terrace 01
North terrace
34%
34%
32%
Clay loam
North terrace 04
North terrace
31%
26%
43%
Clay loam
North terrace 02
North terrace
27%
41%
32%
Clay loam
North terrace 06
North terrace
26%
43%
31%
Loam
North terrace 03
North terrace
25%
32%
43%
Loam
North terrace 05
North terrace
24%
42%
34%
Loam
North terrace 09
North terrace
23%
40%
37%
Loam
South slope 04
South slope
20%
62%
18%
Loam
South slope 07
South slope
19%
61%
20%
Loam
South slope 06
South slope
18%
57%
25%
Loam
South slope 05
South slope
16%
68%
16%
Loam
South slope 08
South slope
15%
67%
18%
Loam
South slope 03
South slope
12%
65%
23%
Loam
South slope 01
South slope
10%
76%
14%
Loam
South slope 02
South slope
10%
64%
26%
Sandy loam
Riverbank 05
Riverbank
15%
25%
60%
Silt loam
Riverbank 06
Riverbank
11%
23%
66%
Silt loam
Riverbank 01
Riverbank
8%
32%
60%
Silt loam
Riverbank 02
Riverbank
7%
23%
70%
Silt loam
Riverbank 03
Riverbank
3%
37%
60%
Silt loam
Riverbank 07
Riverbank
2%
30%
68%
Silt loam
Riverbank 04
Riverbank
1%
42%
57%
Silt loam
onPointSelect → nothing pinned
status="empty" · a valid payload with no compositions in it, kept apart from "still loading" and from a failed fetch. The zones stay described in words rather than drawn.
Topsoil texture by field
Three shares of one whole: Clay, Sand and Silt always add to 100%
No compositions to place
A ternary plot needs rows that carry all three parts of the same whole — Clay, Sand and Silt together. One share on its own has no position on the triangle.
status="loading" · a skeleton triangle with fixed mark positions, aria-hidden, honouring prefers-reduced-motion, with no timer behind it.
Topsoil texture by field
Three shares of one whole: Clay, Sand and Silt always add to 100%
Loading compositions
status="error" · the fetch failed. Try again only appears when onRetry is passed, and pressing it flips this card to ready.
Topsoil texture by field
Three shares of one whole: Clay, Sand and Silt always add to 100%
Couldn't load these compositions
The data source didn't respond.
One composition · the grid, the rotated ticks, the axis arrows and the zones are all still there, and the single mark is classified like any other. A chart with n=1 is a readable chart, not an empty state.
Single reference core
1 composition · average mix 18% Clay, 46% Sand, 36% Silt · 5 named zones
Point at a mark, or tab into the plot and use the arrow keys, for the mix behind it. Click or press Enter to pin one; Escape releases it.
Read a mark by following the gridline that runs parallel to each axis back to its ticks — Clay on the left edge, Sand along the bottom, Silt on the right. The three shares always add to 100%, so the position is the mix and nothing else: two rows with the same mix land together whatever the sample behind them weighed.
Single reference core— one row per composition. Every position on the triangle is the mix and only the mix: the three shares are normalised by each row's own total and add to 100%. Rows that could not be normalised are listed at the end with the reason.
Sample
Clay
Sand
Silt
Zone
Reference core
18%
46%
36%
Loam
gridSteps={5} · acquisition mix per landing page, percentages that already add to 100, one series and no zones. A 20% grid is a deliberately coarser answer: the question is which corner a page leans towards, and the readout still carries the exact mix.
Point at a mark, or tab into the plot and use the arrow keys, for the mix behind it. Click or press Enter to pin one; Escape releases it.
Read a mark by following the gridline that runs parallel to each axis back to its ticks — Organic on the left edge, Paid along the bottom, Referral on the right. The three shares always add to 100%, so the position is the mix and nothing else: two rows with the same mix land together whatever the sample behind them weighed.
Acquisition mix by landing page— one row per composition. Every position on the triangle is the mix and only the mix: the three shares are normalised by each row's own total and add to 100%. Rows that could not be normalised are listed at the end with the reason.
Sample
Organic
Paid
Referral
/security
64%
19%
17%
/integrations/slack
63%
18%
19%
/blog/hiring
60%
23%
17%
/docs/api
56%
30%
14%
/pricing
52%
19%
29%
/blog/ternary-plots
48%
9%
43%
/compare/alternatives
47%
38%
15%
/templates
31%
10%
59%
/changelog
29%
9%
62%
/docs/quickstart
20%
36%
44%
/customers/northwind
14%
5%
81%
/webinars/spring
12%
44%
44%
Awkward data, exactly placed · four cores share one rounded mix and draw as a single mark, one core sits on the 100% clay vertex, and three lab rows (a negative part, three zeros, an unparsed cell) are dropped, counted in a note under the plot and named in the screen-reader table — never quietly. The long zone name truncates with its full text in the tooltip.
Awkward compositions
6 compositions · average mix 45% Clay, 30% Sand, 25% Silt · 2 named zones · 3 unplaceable
Point at a mark, or tab into the plot and use the arrow keys, for the mix behind it. Click or press Enter to pin one; Escape releases it.
Read a mark by following the gridline that runs parallel to each axis back to its ticks — Clay on the left edge, Sand along the bottom, Silt on the right. The three shares always add to 100%, so the position is the mix and nothing else: two rows with the same mix land together whatever the sample behind them weighed.
3 of 9 rows could not be placed — 1 carried a part that is not a number, 1 carried a negative part, 1 summed to zero. A composition needs three finite, non-negative parts with a positive total; these are counted here and named for screen readers rather than coerced onto the triangle.
Awkward compositions— one row per composition. Every position on the triangle is the mix and only the mix: the three shares are normalised by each row's own total and add to 100%. Rows that could not be normalised are listed at the end with the reason.
Sample
Clay
Sand
Silt
Zone
Kiln clay standard
100%
0%
0%
outside every zone
Duplicate core 1
40%
40%
20%
Extremely fine-textured heavy alluvial clay
Duplicate core 2
40%
40%
20%
Extremely fine-textured heavy alluvial clay
Duplicate core 3
40%
40%
20%
Extremely fine-textured heavy alluvial clay
Duplicate core 4
40%
40%
20%
Extremely fine-textured heavy alluvial clay
Riverbank drawdown transect, station 14b (upstream of the weir)
12%
21%
67%
outside every zone
Lab sheet row 22
not placed — a negative part
Lab sheet row 23
not placed — the three parts sum to zero
Lab sheet row 24
not placed — a part that is not a number
jitter={5} on the same data · only the four coincident cores move, by a hash of their own id, so the stack becomes countable and the picture is identical on every render and on the server. The card says out loud that those marks are no longer exactly on their composition — which is why the default is 0.
Awkward compositions
6 compositions · average mix 45% Clay, 30% Sand, 25% Silt · 2 named zones · 3 unplaceable
Point at a mark, or tab into the plot and use the arrow keys, for the mix behind it. Click or press Enter to pin one; Escape releases it.
Read a mark by following the gridline that runs parallel to each axis back to its ticks — Clay on the left edge, Sand along the bottom, Silt on the right. The three shares always add to 100%, so the position is the mix and nothing else: two rows with the same mix land together whatever the sample behind them weighed.
4 marks share a position with another mark and are drawn up to 5px off it so the stack can be counted. The offset is a hash of each row's id, so it is the same on every render — but those marks are no longer exactly on their own composition. Set jitter to 0 for an exact plot.
3 of 9 rows could not be placed — 1 carried a part that is not a number, 1 carried a negative part, 1 summed to zero. A composition needs three finite, non-negative parts with a positive total; these are counted here and named for screen readers rather than coerced onto the triangle.
Awkward compositions— one row per composition. Every position on the triangle is the mix and only the mix: the three shares are normalised by each row's own total and add to 100%. Rows that could not be normalised are listed at the end with the reason.
Sample
Clay
Sand
Silt
Zone
Kiln clay standard
100%
0%
0%
outside every zone
Duplicate core 1
40%
40%
20%
Extremely fine-textured heavy alluvial clay
Duplicate core 2
40%
40%
20%
Extremely fine-textured heavy alluvial clay
Duplicate core 3
40%
40%
20%
Extremely fine-textured heavy alluvial clay
Duplicate core 4
40%
40%
20%
Extremely fine-textured heavy alluvial clay
Riverbank drawdown transect, station 14b (upstream of the weir)