Chart
What every chart shares: the config and its colors, the tooltip, the legend, the palette and the data table.
Tooltip - Default
The category heading, then one row per series: swatch, name, value. Hover, tab in, tap, or drag a finger across the plot.
tooltip={true}
| Date | Running | Swimming |
|---|---|---|
| 2024-07-15 | 450 | 300 |
| 2024-07-16 | 380 | 420 |
| 2024-07-17 | 520 | 120 |
| 2024-07-18 | 140 | 550 |
| 2024-07-19 | 600 | 350 |
| 2024-07-20 | 480 | 400 |
Code
<BarChart data={activities} config={activityConfig} x="date" stack="normal"
categoryAxis={{ tickFormatter: weekday }}
tooltip={{ /* the options below */ }} />Usage
import type { ChartConfig } from "@elements/ui/chart";
import { BarChart } from "@elements/ui/chart-bar";
const config: ChartConfig = {
desktop: { label: "Desktop", color: "var(--chart-1)", icon: "monitor" },
mobile: { label: "Mobile", theme: { light: "var(--chart-2)", dark: "var(--chart-5)" } },
};
<BarChart data={rows} {config} x="month"
tooltip={{ indicator: "line", labelFormatter: (label) => label.slice(0, 3) }}
legend={{ position: "top", toggle: true }} />Examples
Tooltip - Line Indicator
indicator=line draws a bar for each swatch.
tooltip={{ indicator: "line" }}
| Date | Running | Swimming |
|---|---|---|
| 2024-07-15 | 450 | 300 |
| 2024-07-16 | 380 | 420 |
| 2024-07-17 | 520 | 120 |
| 2024-07-18 | 140 | 550 |
| 2024-07-19 | 600 | 350 |
| 2024-07-20 | 480 | 400 |
Code
tooltip={{ indicator: "line" }}Tooltip - No Indicator
hideIndicator leaves names and values.
tooltip={{ hideIndicator: true }}
| Date | Running | Swimming |
|---|---|---|
| 2024-07-15 | 450 | 300 |
| 2024-07-16 | 380 | 420 |
| 2024-07-17 | 520 | 120 |
| 2024-07-18 | 140 | 550 |
| 2024-07-19 | 600 | 350 |
| 2024-07-20 | 480 | 400 |
Code
tooltip={{ hideIndicator: true }}Tooltip - Color
color paints every swatch one color, over the config.
tooltip={{ color: "var(--chart-5)" }}
| Date | Running | Swimming |
|---|---|---|
| 2024-07-15 | 450 | 300 |
| 2024-07-16 | 380 | 420 |
| 2024-07-17 | 520 | 120 |
| 2024-07-18 | 140 | 550 |
| 2024-07-19 | 600 | 350 |
| 2024-07-20 | 480 | 400 |
Code
tooltip={{ color: "var(--chart-5)" }}Tooltip - Custom Label
labelKey heads the tooltip with a config label.
tooltip={{ labelKey: "activities", indicator: "line" }}
| Date | Running | Swimming |
|---|---|---|
| 2024-07-15 | 450 | 300 |
| 2024-07-16 | 380 | 420 |
| 2024-07-17 | 520 | 120 |
| 2024-07-18 | 140 | 550 |
| 2024-07-19 | 600 | 350 |
| 2024-07-20 | 480 | 400 |
Code
tooltip={{ labelKey: "activities", indicator: "line" }}Tooltip - Label Formatter
labelFormatter writes the heading.
tooltip={{ labelFormatter: (label) => fullDate(label) }}
| Date | Running | Swimming |
|---|---|---|
| 2024-07-15 | 450 | 300 |
| 2024-07-16 | 380 | 420 |
| 2024-07-17 | 520 | 120 |
| 2024-07-18 | 140 | 550 |
| 2024-07-19 | 600 | 350 |
| 2024-07-20 | 480 | 400 |
Code
tooltip={{ labelFormatter: (label) => fullDate(label) }}Tooltip - No Label
hideLabel drops the heading.
tooltip={{ hideLabel: true }}
| Date | Running | Swimming |
|---|---|---|
| 2024-07-15 | 450 | 300 |
| 2024-07-16 | 380 | 420 |
| 2024-07-17 | 520 | 120 |
| 2024-07-18 | 140 | 550 |
| 2024-07-19 | 600 | 350 |
| 2024-07-20 | 480 | 400 |
Code
tooltip={{ hideLabel: true }}Tooltip - Formatter
A unit after each value in the muted ink; formatter rewrites a value's text.
tooltip={{ hideLabel: true, hideIndicator: true, unit: "kcal" }}
| Date | Running | Swimming |
|---|---|---|
| 2024-07-15 | 450 | 300 |
| 2024-07-16 | 380 | 420 |
| 2024-07-17 | 520 | 120 |
| 2024-07-18 | 140 | 550 |
| 2024-07-19 | 600 | 350 |
| 2024-07-20 | 480 | 400 |
Code
tooltip={{ hideLabel: true, hideIndicator: true, unit: "kcal" }}Tooltip - Icons
Config icons replace swatches; defaultIndex shows the tooltip from the start.
defaultIndex: 1
| Date | Running | Swimming |
|---|---|---|
| 2024-07-15 | 450 | 300 |
| 2024-07-16 | 380 | 420 |
| 2024-07-17 | 520 | 120 |
| 2024-07-18 | 140 | 550 |
| 2024-07-19 | 600 | 350 |
| 2024-07-20 | 480 | 400 |
Code
const config = {
running: { label: "Running", color: "var(--chart-1)", icon: "heart" },
swimming: { label: "Swimming", color: "var(--chart-2)", icon: "waves" },
};
tooltip={{ hideLabel: true, cursor: false, defaultIndex: 1 }}Tooltip - Advanced
A footer line under the items: here the day's total.
footer: (row) => total
| Date | Running | Swimming |
|---|---|---|
| 2024-07-15 | 450 | 300 |
| 2024-07-16 | 380 | 420 |
| 2024-07-17 | 520 | 120 |
| 2024-07-18 | 140 | 550 |
| 2024-07-19 | 600 | 350 |
| 2024-07-20 | 480 | 400 |
Code
tooltip={{ hideLabel: true, unit: "kcal", footerLabel: "Total", footer: (row) => String(row.running + row.swimming) }}Config and colors
Each config key becomes --color-<key> on the chart: a token, any CSS color (hex, hsl, oklch), or a theme pair for light and dark.
- Desktop
- Mobile
| Month | Desktop | Mobile |
|---|---|---|
| January | 186 | 80 |
| February | 305 | 200 |
| March | 237 | 120 |
| April | 73 | 190 |
| May | 209 | 130 |
| June | 214 | 140 |
Code
const config: ChartConfig = {
desktop: { label: "Desktop", theme: { light: "var(--chart-1)", dark: "var(--chart-7)" } },
mobile: { label: "Mobile", color: "oklch(0.62 0.13 175)" }, // hex, hsl or oklch work too
};
// each key becomes --color-desktop, --color-mobile on the chartLegend
Above or below the plot, with or without swatches.
- Desktop
- Mobile
| Month | Desktop | Mobile |
|---|---|---|
| January | 186 | 80 |
| February | 305 | 200 |
| March | 237 | 120 |
| April | 73 | 190 |
| May | 209 | 130 |
| June | 214 | 140 |
- Desktop
- Mobile
| Month | Desktop | Mobile |
|---|---|---|
| January | 186 | 80 |
| February | 305 | 200 |
| March | 237 | 120 |
| April | 73 | 190 |
| May | 209 | 130 |
| June | 214 | 140 |
Code
<LineChart data={rows} {config} x="month" legend={{ position: "top" }} />
<LineChart data={rows} {config} x="month" legend={{ hideIcon: true }} />Palette
--chart-1 to --chart-8, in the order a config takes them. Switch the look and mode to see each theme's set.
-
--chart-1 -
--chart-2 -
--chart-3 -
--chart-4 -
--chart-5 -
--chart-6 -
--chart-7 -
--chart-8
Code
/* the theme's series colors; a config entry with no color takes the next slot */
color: var(--chart-1) ... var(--chart-8)Long names
Long series names wrap inside the tooltip and the legend; nothing leaves the chart.
- Returning desktop visitors from Western Europe
- First-time mobile visitors from North America
| Month | Returning desktop visitors from Western Europe | First-time mobile visitors from North America |
|---|---|---|
| January | 186 | 80 |
| February | 305 | 200 |
| March | 237 | 120 |
| April | 73 | 190 |
| May | 209 | 130 |
| June | 214 | 140 |
Code
const config = {
desktop: { label: "Returning desktop visitors from Western Europe" },
mobile: { label: "First-time mobile visitors from North America" },
};
<BarChart data={rows} {config} x="month" legend />Tooltip - Options
itemSorter orders the rows; includeHidden keeps a hidden series; active keeps the tooltip shown; position fixes its spot; reverseDirection puts it left of the pointer.
| Month | Desktop | Mobile | Other |
|---|---|---|---|
| January | 186 | 80 | 45 |
| February | 305 | 200 | 100 |
| March | 237 | 120 | 150 |
| April | 73 | 190 | 50 |
| May | 209 | 130 | 100 |
| June | 214 | 140 | 160 |
| Month | Desktop | Mobile |
|---|---|---|
| January | 186 | 80 |
| February | 305 | 200 |
| March | 237 | 120 |
| April | 73 | 190 |
| May | 209 | 130 |
| June | 214 | 140 |
| Month | Desktop | Mobile |
|---|---|---|
| January | 186 | 80 |
| February | 305 | 200 |
| March | 237 | 120 |
| April | 73 | 190 |
| May | 209 | 130 |
| June | 214 | 140 |
| Month | Desktop | Mobile |
|---|---|---|
| January | 186 | 80 |
| February | 305 | 200 |
| March | 237 | 120 |
| April | 73 | 190 |
| May | 209 | 130 |
| June | 214 | 140 |
| Month | Desktop | Mobile |
|---|---|---|
| January | 186 | 80 |
| February | 305 | 200 |
| March | 237 | 120 |
| April | 73 | 190 |
| May | 209 | 130 |
| June | 214 | 140 |
Code
<BarChart ... tooltip={{ itemSorter: "value" }} /> <!-- smallest value first -->
<LineChart ... hidden={["mobile"]} tooltip={{ includeHidden: true }} legend={{ toggle: true }} />
<LineChart ... tooltip={{ active: true, defaultIndex: 1 }} /> <!-- always shown -->
<BarChart ... tooltip={{ position: { x: 8, y: 8 } }} /> <!-- a fixed corner -->
<BarChart ... tooltip={{ reverseDirection: { x: true } }} /> <!-- left of the pointer -->Legend - Options
verticalAlign and align place the legend; iconType picks the swatch; formatter writes each name; itemSorter orders them.
- Desktop
- Mobile
- Other
| Month | Desktop | Mobile | Other |
|---|---|---|---|
| January | 186 | 80 | 45 |
| February | 305 | 200 | 100 |
| March | 237 | 120 | 150 |
| April | 73 | 190 | 50 |
| May | 209 | 130 | 100 |
| June | 214 | 140 | 160 |
- OTHER
- MOBILE
- DESKTOP
| Month | Desktop | Mobile | Other |
|---|---|---|---|
| January | 186 | 80 | 45 |
| February | 305 | 200 | 100 |
| March | 237 | 120 | 150 |
| April | 73 | 190 | 50 |
| May | 209 | 130 | 100 |
| June | 214 | 140 | 160 |
Code
<BarChart ... legend={{ verticalAlign: "top", align: "left", iconType: "circle" }} />
<!-- itemSorter: "name", "key", or a sort value per item: here the config order reversed -->
<BarChart ... legend={{ formatter: (name) => name.toUpperCase(), itemSorter: (item) => -keys.indexOf(item.key), iconType: "line" }} />Legend - Size, offset and position
iconSize sets the swatch size; offset moves the legend away from the plot; position puts it beside the plot, over it at an inside spot, or at a point.
- Desktop
- Mobile
| Month | Desktop | Mobile |
|---|---|---|
| January | 186 | 80 |
| February | 305 | 200 |
| March | 237 | 120 |
| April | 73 | 190 |
| May | 209 | 130 |
| June | 214 | 140 |
- Desktop
- Mobile
| Month | Desktop | Mobile |
|---|---|---|
| January | 186 | 80 |
| February | 305 | 200 |
| March | 237 | 120 |
| April | 73 | 190 |
| May | 209 | 130 |
| June | 214 | 140 |
- Desktop
- Mobile
| Month | Desktop | Mobile |
|---|---|---|
| January | 186 | 80 |
| February | 305 | 200 |
| March | 237 | 120 |
| April | 73 | 190 |
| May | 209 | 130 |
| June | 214 | 140 |
- Desktop
- Mobile
| Month | Desktop | Mobile |
|---|---|---|
| January | 186 | 80 |
| February | 305 | 200 |
| March | 237 | 120 |
| April | 73 | 190 |
| May | 209 | 130 |
| June | 214 | 140 |
Code
<BarChart ... legend={{ iconSize: 14 }} />
<BarChart ... legend={{ offset: 16 }} /> <!-- 16px further from the plot -->
<BarChart ... legend={{ position: "insideTopRight", offset: 8, layout: "vertical" }} />
<BarChart ... legend={{ position: { x: 48, y: 8 } }} /> <!-- a point in the chart box -->Synced charts
Charts with one syncId share the active item and its tooltip, by position or, with syncMethod value, by name.
| Month | Desktop | Mobile |
|---|---|---|
| January | 186 | 80 |
| February | 305 | 200 |
| March | 237 | 120 |
| April | 73 | 190 |
| May | 209 | 130 |
| June | 214 | 140 |
| Month | Desktop | Mobile |
|---|---|---|
| January | 186 | 80 |
| February | 305 | 200 |
| March | 237 | 120 |
| April | 73 | 190 |
| May | 209 | 130 |
| June | 214 | 140 |
| Month | Desktop | Mobile |
|---|---|---|
| January | 186 | 80 |
| February | 305 | 200 |
| March | 237 | 120 |
| April | 73 | 190 |
| May | 209 | 130 |
| June | 214 | 140 |
| Browser | Visitors |
|---|---|
| Chrome | 275 |
| Safari | 200 |
| Firefox | 187 |
| Edge | 173 |
| Other | 90 |
| Browser | Visitors |
|---|---|
| Other | 90 |
| Edge | 173 |
| Firefox | 187 |
| Safari | 200 |
| Chrome | 275 |
Code
<!-- one syncId: a move in any chart shows the same month in the others -->
<BarChart data={rows} {config} x="month" syncId="months" />
<AreaChart data={rows} {config} x="month" syncId="months" />
<RadarChart data={rows} {config} x="month" syncId="months" />
<!-- syncMethod value: matched by the browser's name, not its position -->
<PieChart data={browsers} {config} value="visitors" name="browser" syncId="browsers" syncMethod="value" />
<RadialChart data={reversed} {config} value="visitors" name="browser" syncId="browsers" syncMethod="value" />In a dialog
Escape on a focused chart hides its tooltip and leaves the dialog open; the next Escape closes the dialog.
Code
<Dialog title="Traffic">
<Button slot="trigger">Open chart</Button>
<BarChart data={rows} {config} x="month" />
</Dialog>
<!-- Escape on the focused chart hides its tooltip; the next Escape closes the dialog -->Tooltip - Motion
animationDuration and animationEasing time how the tooltip follows the pointer; isAnimationActive false moves it at once.
| Month | Desktop | Mobile |
|---|---|---|
| January | 186 | 80 |
| February | 305 | 200 |
| March | 237 | 120 |
| April | 73 | 190 |
| May | 209 | 130 |
| June | 214 | 140 |
| Month | Desktop | Mobile |
|---|---|---|
| January | 186 | 80 |
| February | 305 | 200 |
| March | 237 | 120 |
| April | 73 | 190 |
| May | 209 | 130 |
| June | 214 | 140 |
Code
<!-- the tooltip glides 600ms with a linear easing; the second moves at once -->
<BarChart data={rows} {config} x="month" tooltip={{ animationDuration: 600, animationEasing: "linear" }} />
<BarChart data={rows} {config} x="month" tooltip={{ isAnimationActive: false }} />Data table
Every chart carries its data as a table: visually hidden, read by screen readers, shown with showTable.
| Date | Running | Swimming |
|---|---|---|
| 2024-07-15 | 450 | 300 |
| 2024-07-16 | 380 | 420 |
| 2024-07-17 | 520 | 120 |
| 2024-07-18 | 140 | 550 |
| 2024-07-19 | 600 | 350 |
| 2024-07-20 | 480 | 400 |
Code
<BarChart data={rows} {config} x="month" showTable={state.table} />