Chart style

Applies to every chart.

Palette

Theme

Appearance

Depth
Surface
Background

Reading & motion

Axis
Hover
Legend
Numbers
Loading
Empty
Using chart defaults
3,174npm
1stars

Charts

Every chart, by the job it does

Trends3

  • Area
  • Line
  • Combo

Comparison4

  • Bar
  • Horizontal bar
  • Slope
  • RangeNew

Part of a whole4

  • Radial
  • Treemap
  • Funnel
  • Sankey

KPIs3

  • Stat cards
  • Progress
  • Activity ring

Patterns4

  • Heatmap
  • Scatter
  • Radar
  • Box plotNew

Cool23

  • Chord loomNew
  • Rank ribbonsNew
  • Event helixNew
  • Contour islandsNew
  • Parallel ribbonsNew
  • Sunburst terracesNew
  • Cluster constellationNew
  • Ternary prismNew
  • Wind roseNew
  • Marimekko blocksNew
  • Intersection towersNew
  • Horizon foldsNew
  • Circle archipelagoNew
  • Helix ribbonsNew
  • Voxel cloudNew
  • Arc bridgesNew
  • TerrainNew
  • Hex cityNew
  • Voxel waffleNew
  • SkylineNew
  • Block cityNew
  • RidgelineNew
  • Spiral yearNew

Finance6

  • CandlestickNew
  • IndicatorsNew
  • PriceNew
  • DepthNew
  • Order bookNew
  • PortfolioNew

Scatter chart

Campaign results

12,92012,920+19.8%
05001K1.5K2K$0$5K$10K$15K$20K$25K$30K$35KSign-upsSpend

Campaign results
PointGroupSpendSign-upsReach
Brand searchSearch$4,20061018,000
Competitor termsSearch$9,80072022,000
Product searchSearch$14,5001,38031,000
Shopping adsSearch$21,0001,69042,000
Generic termsSearch$27,5001,54050,000
Launch videoSocial$6,40029064,000
Creator seriesSocial$12,80082071,000
Carousel adsSocial$18,20076048,000
Story adsSocial$24,6001,12083,000
Lookalike audiencesSocial$33,8001,47096,000
Welcome seriesEmail$1,8005409,000
Newsletter featureEmail$3,10046014,000
Win-backEmail$5,20083012,000
Partner listEmail$8,60069021,000
Default01 / 06

Usage

Import the component and the stylesheet once. See Getting started to install the package.

import { ScatterChartCard } from '@lilt-ui/charts';
import '@lilt-ui/charts/styles.css';
<ScatterChartCard
  title="Campaign results"
  data={campaignResults}
  x="spend"
  y="signups"
  label="campaign"
/>

Omit title to leave out the visible title. Set header={false} to remove the whole header, and use aria-label to name a chart inside your own card. legend={false} removes the legend independently. surface="ghost" controls only the visual frame. See Card composition.

Data

Each row is one point: x places it across, y places it up and feeds the headline, and TypeScript rejects anything but numeric fields for both. label names a point when it is read; size scales bubbles by area, so a point twice the value covers twice the ink; group colors points and adds a legend. A row missing x or y can’t be placed, so it is left off the plot, counted in the footer, and still listed for screen readers. Axes start at zero when the data starts near it, and at a round number otherwise.

// One row per point.
const campaignResults = [
  { campaign: 'Brand search', channel: 'Search', spend: 4200, signups: 610, reach: 18000 },
  { campaign: 'Story ads', channel: 'Social', spend: 24600, signups: null, reach: 83000 },
  // …
];

<ScatterChartCard
  title="Campaign results"
  data={campaignResults}
  x="spend"        // a numeric field: across
  y="signups"      // a numeric field: up, and the headline
  label="campaign" // a text field that names each point
  size="reach"     // optional: a numeric field for bubble area
  group="channel"  // optional: a text field for color and legend
/>

For a working period select, pass ranges. Each range brings its own data and delta.

<ScatterChartCard
  title="Campaign results"
  x="spend"
  y="signups"
  label="campaign"
  ranges={[
    { id: 'quarter', label: 'This quarter', data: thisQuarter, delta: 0.198 },
    { id: 'last-quarter', label: 'Last quarter', data: lastQuarter, delta: 0.074 },
  ]}
/>

Strip and beeswarm

StripChartCard shows individual measurements within categories, with mean as the default headline. Strip uses stable vertical jitter; beeswarm prevents collisions and expands the plot when needed. Both preserve each numeric position. Missing values are counted and remain in the accessible table. Unique labels preserve pin identity across updates.

import { StripChartCard } from '@lilt-ui/charts';

<StripChartCard data={deliveryTimes} category="carrier" value="hours" label="parcel" display="beeswarm" depth />
PropTypeDescription
titlestringOptional visible title. Omit to render no title. Also supplies the accessible name unless aria-label is set.
headerbooleanDefaults to true. False removes the whole header: title, headline, delta and period controls, with no reserved space. Pass data directly when your app owns period selection.
aria-labelstringAccessible chart name, independent of the visible title. Falls back to title, then a generic chart name.
dataRow[]One row per observation. Omit when using ranges.
heightnumberMinimum plot height, default 260. Dense lanes expand to keep every mark visible.
depthbooleanLit tiles, solid bars, or spheres. Data positions remain exact; loading keeps the same depth treatment.
colorstringOverride the palette with one CSS color.
aggregate'sum' | 'mean' | 'max'Resting headline summary; Strip defaults to mean, the others to sum.
headlinenumberOverride the resting headline.
deltanumberFractional change shown alongside the headline.
deltaTone'default' | 'inverse' | 'neutral'Use inverse when a decrease is good.
rangestringStatic period label.
ranges{ id, label, data, headline?, delta? }[]Period choices with their own data.
defaultRangestringInitial period id.
localestringLocale for numbers and dates; defaults to en-US.
formatValue(value: number) => stringCustom value formatter.
badgeReactNodeContent for the glass tab above the card.
classNamestringClass on the card frame.
styleCSSProperties | ChartStyleInline styles and scoped chart variables.
onSelectionChange(selection | null) => voidReports id, original row (or null for an absent day), value, and pinned state.
renderReadout(selection | null) => ReactNodeConsumer-owned readout, including when the header is hidden.
valueFormatIntl.NumberFormatOptionsFormats the headline, tiles, and axis, e.g. currency.
surface'elevated' | 'outline' | 'ghost'Card treatment. Ghost removes the visual frame only; header and legend are independent controls.
palette'iris' | 'cobalt' | 'emerald'Series colors. Iris by default; explicit series colors still win.
numberStyle'count' | 'pop' | 'slide' | 'roll' | 'flow' | 'scramble'How the headline animates when its value changes. Count (default) counts to the new value.
loadingbooleanShows a neutral loading skeleton, not data.
loadingStyle'shimmer' | 'draw' | 'breathe'Shimmer sweeps a highlight over still shapes; draw reveals them in order; breathe lets them settle. Follows the selected look, including depth, and respects reduced motion.
empty'dots' | 'shape' | ReactElement | nullWhat the card shows when the period has no data. Dots (default) gathers a quiet dot field around the message; shape lays the chart’s own outline behind it. Pass `<ChartEmpty>` with your own words, any element of your own, or null for nothing. Both looks stay still, so empty never reads as loading, and the headline reads a dash, never zero.
motion'auto' | 'none'None turns off decorative motion; reduced motion does this automatically.
categorytext key of RowCategory lane for each point.
valuenumeric key of RowMeasured horizontal position.
labeltext key of RowUnique accessible point name; falls back to row index.
display'strip' | 'beeswarm'Stable jitter (default) or collision-free placement.
radiusnumberPoint radius in pixels, 2–12; default 4.
onKeyDownKeyboardEventHandler<HTMLElement>Keyboard handler on the card frame. Chart inspection also has its own keyboard handling.

Props

Every prop ScatterChartCard accepts, in the order you are likely to reach for them.

PropTypeDescription
titlestringOptional visible title. Omit to render no title. Also supplies the accessible name unless aria-label is set.
headerbooleanDefaults to true. False removes the whole header: title, headline, delta and period controls, with no reserved space. Pass data directly when your app owns period selection.
aria-labelstringAccessible chart name, independent of the visible title. Falls back to title, then a generic chart name.
trailsbooleanJoin successive observations within each group. Gaps break trails; duplicate or missing time keys suppress that group’s trail.
trailOrdernumeric key of RowExplicit numeric time order. Otherwise follows input row order.
dataRow[]One row per point. Omit when using ranges.
xnumeric key of RowPlaces each point across.
ynumeric key of RowPlaces each point up, and drives the headline.
labeltext key of RowNames a point when it is read. Unique labels also keep points steady across periods.
sizenumeric key of RowSizes points by area, turning dots into bubbles.
sizeLabelstringNames the size measure, e.g. “Reach”.
grouptext key of RowColors points by group and adds a legend with each group’s value.
xLabelstringTitle for the horizontal axis.
yLabelstringTitle for the vertical axis.
xFormatIntl.NumberFormatOptionsFormats x values and ticks, e.g. currency or seconds.
trendbooleanA least-squares line through the points, with its r² in the footer.
aggregate'sum' | 'mean' | 'max'How the resting headline sums up every plotted y. Sum by default.
heightnumberPlot height in pixels, including the axes. Defaults to 260.
headlinenumberResting headline. Defaults to the aggregate of every plotted y.
deltanumberFractional change for the chip.
deltaTone'default' | 'inverse' | 'neutral'Use inverse when a decrease is good.
rangestringStatic period label in the header.
ranges{ id, label, data, delta?, headline? }[]Turns the period label into a select. Each range swaps the points.
colorstringAny CSS color for points without a group.
depthbooleanDraws each point as a lit sphere with a soft contact shadow. Centers and sizes stay exact.
legendfalse | 'tiles' | 'inline' | 'list' | 'pills' | 'bars'False removes the legend without changing the data. How the group legend lays out: value tiles (default), a compact inline key, list rows, tinted pills, or bars against the largest.
legendSwatch'square' | 'dot' | 'line'The mark beside each legend label. Defaults to a rounded square.
valueFormatIntl.NumberFormatOptionsFormats the headline, tiles, and axis, e.g. currency.
surface'elevated' | 'outline' | 'ghost'Card treatment. Ghost removes the visual frame only; header and legend are independent controls.
palette'iris' | 'cobalt' | 'emerald'Series colors. Iris by default; explicit series colors still win.
numberStyle'count' | 'pop' | 'slide' | 'roll' | 'flow' | 'scramble'How the headline animates when its value changes. Count (default) counts to the new value.
loadingbooleanShows a neutral loading skeleton, not data.
loadingStyle'shimmer' | 'draw' | 'breathe'Shimmer sweeps a highlight over still shapes; draw reveals them in order; breathe lets them settle. Follows the selected look, including depth, and respects reduced motion.
empty'dots' | 'shape' | ReactElement | nullWhat the card shows when the period has no data. Dots (default) gathers a quiet dot field around the message; shape lays the chart’s own outline behind it. Pass `<ChartEmpty>` with your own words, any element of your own, or null for nothing. Both looks stay still, so empty never reads as loading, and the headline reads a dash, never zero.
motion'auto' | 'none'None turns off decorative motion; reduced motion does this automatically.
defaultRangestringInitial range id; defaults to the first supplied range.
localestringLocale used for number formatting. Defaults to en-US.
formatValue(value: number) => stringCustom value text, taking precedence over valueFormat.
formatX(value: string | number | Date) => stringCustom x label text.
badgeReactNodeContent in the tab above the card. A comparison badge can replace this while a comparison is active.
classNamestringAdditional class on the card frame.
styleCSSProperties | ChartStyleInline styles and scoped --lilt-* chart variables.