Skip to content
Folio

Charts

Facet

The same chart once per group, on one shared scale.

Usage

When to reach for it

Eight series overplot into a hairball in one frame. Two or three series that genuinely need comparing point-for-point still belong in one chart.

Ships from@misoto22/design/charts

TSX
import { Facet } from '@misoto22/design/charts'

Notes

The same chart repeated once per group, on one shared scale — small multiples.

The answer to the question a multi-series chart stops being able to answer at around five series: eight lines in one plot is a hairball, and the reader spends their attention untangling strands instead of reading them. Splitting the same lines across eight small plots costs the direct overlay comparison and buys back every individual shape, which is almost always the better trade — this is Tufte's small multiple, and Observable Plot and Vega-Lite both treat it as a first-class operation for the same reason.

Reach for a multi-series <LineChart> or <AreaChart> instead when the series really do have to be read AGAINST each other — crossovers, gaps, shares of one total — and when there are few enough of them to tell apart. Reach for this when each group has its own shape worth seeing, when the groups are many, or when the reader's question is "which of these is different".

What it gets right that a hand-rolled data.map() does not: the panels share one domain by default. On independent scales every panel fills its own plot, so a group peaking at 40 and a group peaking at 4,000 come out the same shape — the comparison is not merely lost, it is inverted, and nothing on screen says so. panel.domain is that shared scale; the render function has to pass it to the panel's value axis. It also caps the grid, folds or counts the tail, and prints one legend and one axis label instead of twelve.

Anatomy

Facet anatomy
ElementDescription
Figure framerequiredChartFigure’s <figure> around the whole grid, named by title. Its empty state has no false escape hatch, unlike a single chart’s: an empty pair of axes is at least a chart, and an empty grid is nothing.
Panel gridrequiredA role="list" of <li> panels on a CSS auto-fit track. list-none strips the list role in Safari, so it is set back by hand — a grid of twelve plots that does not announce twelve items takes away the reader’s only cue for how far they have to go.
Panel nameA <p> above each plot. showPanelNames={false} makes it sr-only rather than removing it, because a grid whose panels a screen reader cannot tell apart is one figure with twelve anonymous plots in it.
Shared domainrequiredpanel.domain, computed across the panels that survive the cap and handed to the render function. It is not applied for you: a panel that does not pass it to its own value axis has opted back into independent scales.
Grid-level legend and axis labelslegend, yLabel and xLabel, printed once above and below the grid. A legend inside every panel is the same three swatches twelve times, restating what the reader learned from the first one.
Overflow noteThe line under a capped grid, saying how many groups were left out or folded. limit is 12 by default and nothing is ever dropped in silence.

Best practices

Do

  • Pass panel.domain to the panel chart’s value axis. That one line is what the component is for: on independent scales a group peaking at 40 and one peaking at 4,000 draw the same shape, so the comparison is not merely lost, it is inverted — and nothing on screen says so.
  • Name every plotted field in value. It is what the shared domain is measured over, so a panel that draws a series value does not mention can overflow its own axis while the rest of the grid looks correct.
  • Set hideDataTable when the panel charts carry their own. Every chart in the package renders its rows as an sr-only table, so a twelve-panel grid otherwise puts thirteen tables into the accessibility tree.
  • Keep sort="max" for a grid a reader scans and switch to "name" for one they look things up in. Reading order is what a reader takes as ranking, so it is a decision in either direction.

Don’t

  • overflow="fold" is not free. The folded panel is the SUM of the tail at each category and its statistics go into the shared domain, so folding twenty-eight small groups can produce one tall panel that squashes the twelve the reader came for. Use note when the tail is numerous rather than large.
  • Do not turn includeZero off under bars or areas. It is on by default for the same reason a bar axis is anchored at zero — a length read against a truncated baseline overstates every difference, and a grid exists to have its differences compared.
  • Do not reach for it at two or three series. Faceting buys back every individual shape and pays for it with the direct overlay, and crossovers, gaps and shares of one total are exactly what the overlay was for.

Examples

default

Six channels as six small plots on ONE domain, which is the entire reason the component exists. Social peaks at 410 and Organic at 6,400, and that ratio is the first thing the grid says — on independent scales the two would draw the same rising line, so the comparison the reader came to make would be inverted rather than merely lost. The shared scale is computed across the panels and handed to the render function as panel.domain; a panel that does not pass it to its value axis has opted back into the broken version. yLabel and xLabel are printed once for the whole grid rather than once per panel, which is five axis titles the reader does not have to read again.

Visitors by channel, 2026
Visitors
  • Organic search

    Organic search visitors
  • Direct

    Direct visitors
  • Paid search

    Paid search visitors
  • Referral

    Referral visitors
  • Email

    Email visitors
  • Social

    Social visitors
Jan – Aug 2026
Visitors by channel, 2026
monthchannelvisitors
JanOrganic search4,200
JanPaid search2,100
JanDirect1,800
JanReferral620
JanEmail430
JanSocial210
FebOrganic search4,400
FebPaid search2,000
FebDirect1,850
FebReferral700
FebEmail460
FebSocial260
MarOrganic search4,100
MarPaid search1,900
MarDirect1,900
MarReferral880
MarEmail450
MarSocial240
AprOrganic search4,800
AprPaid search1,750
AprDirect1,950
AprReferral810
AprEmail470
AprSocial320
MayOrganic search5,200
MayPaid search1,600
MayDirect2,000
MayReferral1,020
MayEmail520
MaySocial300
JunOrganic search5,600
JunPaid search1,400
JunDirect2,050
JunReferral1,180
JunEmail610
JunSocial280
JulOrganic search6,100
JulPaid search1,250
JulDirect2,100
JulReferral1,260
JulEmail590
JulSocial350
AugOrganic search6,400
AugPaid search1,100
AugDirect2,150
AugReferral1,400
AugEmail640
AugSocial410

overplotted vs faceted

The same six channels twice, and the trade stated as a picture. Above, one plot and six lines: the legend has to be carried back into the hairball, and Paid search falling by half is invisible under Organic's rise. Below, six small plots on one domain, where that fall is the second thing you see and the ranking is the first. Faceting costs the direct overlay — crossovers and gaps between two named series are gone, and past about five series that is a trade worth making every time — and it costs a reshape, since a multi-series chart eats one row per month with a column per channel while a facet eats one row per month PER channel.

Overplotted — six series, one plot

Visitors by channel, overplotted
Visitors by channel, overplotted
monthOrganic searchPaid searchDirectReferralEmailSocial
Jan4,2002,1001,800620430210
Feb4,4002,0001,850700460260
Mar4,1001,9001,900880450240
Apr4,8001,7501,950810470320
May5,2001,6002,0001,020520300
Jun5,6001,4002,0501,180610280
Jul6,1001,2502,1001,260590350
Aug6,4001,1002,1501,400640410

Faceted — six plots, one domain

Visitors by channel, faceted
Visitors
  • Organic search

    Organic search visitors
  • Direct

    Direct visitors
  • Paid search

    Paid search visitors
  • Referral

    Referral visitors
  • Email

    Email visitors
  • Social

    Social visitors
Visitors by channel, faceted
monthchannelvisitors
JanOrganic search4,200
JanPaid search2,100
JanDirect1,800
JanReferral620
JanEmail430
JanSocial210
FebOrganic search4,400
FebPaid search2,000
FebDirect1,850
FebReferral700
FebEmail460
FebSocial260
MarOrganic search4,100
MarPaid search1,900
MarDirect1,900
MarReferral880
MarEmail450
MarSocial240
AprOrganic search4,800
AprPaid search1,750
AprDirect1,950
AprReferral810
AprEmail470
AprSocial320
MayOrganic search5,200
MayPaid search1,600
MayDirect2,000
MayReferral1,020
MayEmail520
MaySocial300
JunOrganic search5,600
JunPaid search1,400
JunDirect2,050
JunReferral1,180
JunEmail610
JunSocial280
JulOrganic search6,100
JulPaid search1,250
JulDirect2,100
JulReferral1,260
JulEmail590
JulSocial350
AugOrganic search6,400
AugPaid search1,100
AugDirect2,150
AugReferral1,400
AugEmail640
AugSocial410

scale and overflow

Two grids one prop apart, and then the cap. On the right every panel fills its own box, so Social — which never clears 410 — draws the same climb as Organic's 6,400: not a harder comparison but a false one, and nothing on the page admits it, which is why scales independent is never the safe answer. Below, fourteen channels drawn as six panels and an Other — sort last orders them by where each ended up, limit caps the grid before it becomes fourteen postage stamps, and overflow fold sums the tail into that last panel so the total stays true. Either way the grid prints a line under itself saying what it left out, because a grid that quietly stops at six is a grid the reader cannot audit.

Visitors by channel, shared scalescales="shared" — the default
  • Organic search

    6,400
  • Direct

    2,150
  • Referral

    1,400
  • Social

    410
Visitors by channel, independent scalesscales="independent" — every panel lies about its size
  • Organic search

    6,400
  • Direct

    2,150
  • Referral

    1,400
  • Social

    410
Visitors by channel, top sixFourteen channels, sorted by where each ended up
  • Organic search

    6,400
  • Direct

    2,150
  • Paid search

    2,100
  • Referral

    1,400
  • Email

    640
  • Social

    410
  • Other

    1,165
6 of 14 shown; the other 8 are summed into Other.
Visitors by channel, top six
monthchannelvisitors
M1Organic search3,968
M2Organic search3,712
M3Organic search4,544
M4Organic search4,224
M5Organic search5,120
M6Organic search5,632
M7Organic search6,080
M8Organic search6,400
M1Direct1,333
M2Direct1,247
M3Direct1,527
M4Direct1,419
M5Direct1,720
M6Direct1,892
M7Direct2,043
M8Direct2,150
M1Paid search1,302
M2Paid search1,218
M3Paid search1,491
M4Paid search1,386
M5Paid search1,680
M6Paid search1,848
M7Paid search1,995
M8Paid search2,100
M1Referral868
M2Referral812
M3Referral994
M4Referral924
M5Referral1,120
M6Referral1,232
M7Referral1,330
M8Referral1,400
M1Email397
M2Email371
M3Email454
M4Email422
M5Email512
M6Email563
M7Email608
M8Email640
M1Social254
M2Social238
M3Social291
M4Social271
M5Social328
M6Social361
M7Social390
M8Social410
M1Affiliate198
M2Affiliate186
M3Affiliate227
M4Affiliate211
M5Affiliate256
M6Affiliate282
M7Affiliate304
M8Affiliate320
M1Display161
M2Display151
M3Display185
M4Display172
M5Display208
M6Display229
M7Display247
M8Display260
M1Podcast112
M2Podcast104
M3Podcast128
M4Podcast119
M5Podcast144
M6Podcast158
M7Podcast171
M8Podcast180
M1Newsletter87
M2Newsletter81
M3Newsletter99
M4Newsletter92
M5Newsletter112
M6Newsletter123
M7Newsletter133
M8Newsletter140
M1Partner68
M2Partner64
M3Partner78
M4Partner73
M5Partner88
M6Partner97
M7Partner105
M8Partner110
M1Print QR43
M2Print QR41
M3Print QR50
M4Print QR46
M5Print QR56
M6Print QR62
M7Print QR67
M8Print QR70
M1Events34
M2Events32
M3Events39
M4Events36
M5Events44
M6Events48
M7Events52
M8Events55
M1SMS19
M2SMS17
M3SMS21
M4SMS20
M5SMS24
M6SMS26
M7SMS29
M8SMS30

Accessibility

  • The shared domain is the default and the whole point: on independent scales a group peaking at 40 and one peaking at 4,000 draw the same shape, and the comparison the reader came for is not merely lost but inverted.
  • Every panel is a figure with its own accessible name, so a screen reader walks eight named charts rather than one unnamed grid.
  • Panels beyond max fold into a stated overflow rather than being dropped, and the count is printed — a grid silently missing four groups is not something a reader can detect.
  • Panel order is a choice the call site makes explicitly through sort, because reading order is what a reader takes as ranking.