Chart
Anatomy
Section titled “Anatomy”<Chart> <DonutChart /> <Legend> <LegendItem /> <LegendItem /> </Legend></Chart>Chart: Wraps and provides shared context (colors, currently hovered item, etc.) to child components. Not required if using a display-only chart without user interactions. ForBarChartandLineChart, passcategories(the series keys) so theChart,Legend, andChartTooltipshare one color per series.DonutChart: Displays data as parts of a whole, with an optional center value and label.BarChart: Compares categories, stacks composition, and renders pre-binned histograms.LineChart: Shows one or more series as a trend over time.ChartTooltip: A tokenized hover tooltip used byBarChartandLineChart.Legend: Displays a list of labels and values related to the chart.
import { Chart, DonutChart, Legend, LegendItem } from "stylus-ui/Chart";
const data = [ { name: "Notion", amount: 281 }, { name: "VS Code", amount: 142 }, { name: "Slack", amount: 50 },];
export default () => ( <Chart className="flex items-center gap-8" data={data} category="name"> <DonutChart value="amount" /> <Legend className="w-48"> {data.map((item) => ( <LegendItem key={item.name} dataKey={item.name} label={item.name} value={item.amount} /> ))} </Legend> </Chart>);Donut chart
Section titled “Donut chart”For the simplest display, you can use DonutChart standalone, without the Chart wrapper.
import { DonutChart } from "stylus-ui/Chart";
const data = [ { name: "Notion", amount: 281 }, { name: "VS Code", amount: 142 }, { name: "Slack", amount: 50 },];
export default () => ( <DonutChart category="name" value="amount" data={data} className="size-16" />);Colors
Section titled “Colors”Each slice takes a color from the accent palette positionally. The available colors are accent-1, accent-2, accent-3, accent-4, accent-5, and accent-neutral (reserved for the "Other" / long-tail bucket).
Specific colors only
Section titled “Specific colors only”By default, the chart cycles through the accent series in order. To pin or reorder, pass an array of accent keys to the colors prop:
import { DonutChart } from "stylus-ui/Chart";
const data = [ { name: "Primary", value: 40 }, { name: "Secondary", value: 30 }, { name: "Tertiary", value: 20 }, { name: "Other", value: 10 },];
export default () => ( <DonutChart category="name" value="value" data={data} colors={["accent-1", "accent-3", "accent-5", "accent-neutral"]} />);Display value
Section titled “Display value”Display a sum of all chart segments in the center by setting showValue to true.
import { DonutChart } from "stylus-ui/Chart";import { formatIntlNumber } from "scribe-web-shared/functions";
const data = [ { name: "Notion", amount: 2814 }, { name: "VS Code", amount: 142 }, { name: "Slack", amount: 503 },];
export default () => ( <DonutChart category="name" value="amount" data={data} showValue />);Long values will automaically shrink to fit.
import { DonutChart } from "stylus-ui/Chart";import { formatIntlNumber } from "scribe-web-shared/functions";
const data = [ { name: "Notion", revenue: 4500600 }, { name: "VS Code", revenue: 3200800 }, { name: "Slack", revenue: 1800900 },];
export default () => ( <DonutChart category="name" value="revenue" data={data} showValue />);Formatting values
Section titled “Formatting values”Use the valueFormatter prop to format the display value. For example, you can use the formatIntlNumber helper to display locale-appropriate thousands separators for long numbers.
import { DonutChart } from "stylus-ui/Chart";import { formatIntlNumber } from "scribe-web-shared/functions";
const data = [ { name: "Notion", amount: 2814 }, { name: "VS Code", amount: 142 }, { name: "Slack", amount: 503 },];
export default () => ( <DonutChart category="name" value="amount" data={data} showValue valueFormatter={(v) => formatIntlNumber(v)} />);With label
Section titled “With label”To display text above the value, set label.
import { DonutChart } from "stylus-ui/Chart";import { formatIntlNumber } from "scribe-web-shared/functions";
const data = [ { name: "Notion", hours: 281 }, { name: "VS Code", hours: 142 }, { name: "Slack", hours: 50 },];
export default () => ( <DonutChart category="name" value="hours" data={data} showValue valueFormatter={(v) => `${formatIntlNumber(v)} hrs`} label="Yearly average" />);No data
Section titled “No data”When there's no data or all values are zero, the chart displays a full ring with a - in the center:
import { DonutChart } from "stylus-ui/Chart";
export default () => ( <DonutChart category="name" value="amount" data={[]} showValue label="Yearly average" />);With legend
Section titled “With legend”Combine the DonutChart with the Legend component for interactive data visualization. The Chart wrapper automatically synchronizes hover and click states between components with zero boilerplate:
import React from "react";import { Chart, DonutChart, Legend, LegendItem } from "stylus-ui/Chart";
const data = [ { name: "Notion", amount: 281 }, { name: "VS Code", amount: 142 }, { name: "GitHub", amount: 87 }, { name: "Slack", amount: 50 }, { name: "Figma", amount: 43 },];
export default () => { const total = data.reduce((sum, item) => sum + item.amount, 0);
return ( <Chart className="flex items-center gap-8" data={data} category="name"> <DonutChart value="amount" label="Yearly average" valueFormatter={(v) => `${v} hrs`} showValue /> <Legend> {data.map((item) => ( <LegendItem key={item.name} dataKey={item.name} label={item.name} value={`${item.amount} hrs (${((item.amount / total) * 100).toFixed(1)}%)`} /> ))} </Legend> </Chart> );};With click interactions
Section titled “With click interactions”Add click handling to toggle selection by providing an onClick callback to the Chart component:
import React from "react";import { Chart, DonutChart, Legend, LegendItem } from "stylus-ui/Chart";
const data = [ { name: "Notion", amount: 281 }, { name: "VS Code", amount: 142 }, { name: "GitHub", amount: 87 }, { name: "Slack", amount: 50 }, { name: "Figma", amount: 43 },];
export default () => { const [selected, setSelected] = React.useState(null); const total = data.reduce((sum, item) => sum + item.amount, 0);
return ( <Chart className="flex items-center gap-8" data={data} category="name" selectedDataKey={selected} onClick={setSelected} > <DonutChart value="amount" label="Yearly average" valueFormatter={(v) => `${v} hrs`} showValue /> <Legend> {data.map((item) => ( <LegendItem key={item.name} dataKey={item.name} label={item.name} value={`${item.amount} hrs (${((item.amount / total) * 100).toFixed(1)}%)`} /> ))} </Legend> </Chart> );};With icons and logos
Section titled “With icons and logos”Each legend item accepts an icon prop for FontAwesome icons or a logo prop (with name and src) to display a favicon.
import React from "react";import { Chart, DonutChart, Legend, LegendItem } from "stylus-ui/Chart";import { getFavicon } from "stylus-ui/utils/getFavicon";import { faGrid2 } from "@fortawesome/pro-regular-svg-icons";
const data = [ { name: "Notion", url: "https://notion.so", amount: 281 }, { name: "Slack", url: "https://slack.com", amount: 142 }, { name: "Figma", url: "https://figma.com", amount: 87 }, { name: "Others", amount: 50 },];
export default () => { const total = data.reduce((sum, item) => sum + item.amount, 0);
return ( <Chart className="flex items-center gap-8" data={data} category="name" colors={["accent-1", "accent-3", "accent-5", "accent-neutral"]} > <DonutChart value="amount" label="Yearly average" valueFormatter={(v) => `${v} hrs`} showValue /> <Legend> {data.map((item) => { const logoObj = item.url && { name: item.name, src: getFavicon({ url: item.url }), };
return ( <LegendItem key={item.name} dataKey={item.name} logo={logoObj} icon={!item.url ? faGrid2 : undefined} label={item.name} value={`${((item.amount / total) * 100).toFixed(1)}%`} /> ); })} </Legend> </Chart> );};API reference
Section titled “API reference”A donut chart component for visualizing proportional data with an optional center label. Automatically syncs with Chart context when used inside a Chart component.
value
The key in data objects to use as values (e.g., ‘amount’, ‘count’)
category
The key in data objects to use as category names (e.g., ‘name’, ‘app’). If not provided, will use category from Chart context.
colors
Accent color keys assigned positionally to slices (accent-1..accent-5, accent-neutral). Defaults to the accent series.
data
Array of data objects to display in the chart. Each object should have properties matching the category and value keys. If not provided, will use data from Chart context.
label
Optional label text to display above the center value
labelFontSize
Optional font size for the label text (in SVG viewBox units). Default: 15
showValue
Whether to hide the center value label
valueFontSize
Optional font size for the center value text (in SVG viewBox units).
valueFormatter
Function to format the center label value. Receives the total sum of all values.
Bar chart
Section titled “Bar chart”Pass index (the x-axis key) and categories (one or more series keys). Each series becomes a bar, colored positionally from the palette.
import { BarChart } from "stylus-ui/Chart";
const data = [ { team: "Store Ops", clean: 62, manual: 24, rework: 14 }, { team: "Distribution", clean: 44, manual: 34, rework: 22 }, { team: "Merchandising", clean: 58, manual: 30, rework: 12 }, { team: "Planning", clean: 70, manual: 22, rework: 8 },];
export default () => ( <div className="h-64 w-full"> <BarChart data={data} index="team" categories={["clean", "manual", "rework"]} /> </div>);Each series takes an accent color positionally (accent-1, accent-2, …). To pin or reorder, pass colors with accent keys — accent-1, accent-2, accent-3, accent-4, accent-5, or accent-neutral:
<BarChart data={data} index="team" categories={["clean", "manual", "rework"]} colors={["accent-3", "accent-1", "accent-neutral"]} />Stacked
Section titled “Stacked”Set stacked to combine every series into one bar per x position. Set layout="vertical" for horizontal bars.
import { BarChart } from "stylus-ui/Chart";
const data = [ { team: "Store Ops", clean: 62, manual: 24, rework: 14 }, { team: "Distribution", clean: 44, manual: 34, rework: 22 }, { team: "Planning", clean: 70, manual: 22, rework: 8 },];
export default () => ( <div className="h-64 w-full"> <BarChart data={data} index="team" categories={["clean", "manual", "rework"]} stacked /> </div>);Histogram
Section titled “Histogram”Use variant="histogram" to render pre-binned distribution data as gap-free bars. Compute the bins in your data adapter; the chart renders the buckets it is given.
import { BarChart } from "stylus-ui/Chart";
const bins = [ { bucket: "0–1m", count: 8 }, { bucket: "1–2m", count: 22 }, { bucket: "2–5m", count: 41 }, { bucket: "5–10m", count: 27 }, { bucket: "10m+", count: 12 },];
export default () => ( <div className="h-64 w-full"> <BarChart data={bins} index="bucket" categories={["count"]} variant="histogram" /> </div>);API reference
Section titled “API reference”A bar chart for categorical comparison, stacked composition, and pre-binned histograms. Colors and hover state sync with Chart context when nested inside a Chart component; otherwise it renders standalone from its own props.
categories
Series keys to render as bars. Each becomes one <Bar>, colored positionally.
index
The key in each data object used for the x-axis (category or histogram bin label).
colors
Accent color keys assigned positionally to categories (accent-1..accent-5, accent-neutral). Pass an explicit list to pin a color per series. Defaults to the accent series.
data
Array of data objects, one per x-axis position. Falls back to Chart context data.
desc
Accessible description, rendered into the SVG <desc>.
emptyMessage
Content shown when there is no data. Default: “No data”.
height
Fixed chart height in px. Omit to fill the container responsively.
layout
'horizontal' renders vertical bars (x = category); 'vertical' renders horizontal bars (y = category). Default: 'horizontal'.
loading
Render a loading skeleton instead of the chart.
showGrid
Show the cartesian grid. Default: true.
showTooltip
Show the hover tooltip. Default: true.
showXAxis
Show the x-axis. Default: true.
showYAxis
Show the y-axis. Default: true.
stacked
Stack all series into a single bar per x position.
ticks
Explicit value-axis tick positions. Omit to let recharts choose. Pass this to control the ticks when the raw domain (e.g. seconds) formats to awkward labels.
title
Accessible name for the chart, rendered into the SVG <title>. Give charts a title so assistive tech can name the graphic.
valueFormatter
Formats axis + tooltip values.
variant
'histogram' renders adjacent, gap-free, square bars for pre-binned distribution data. Binning happens upstream in the data adapter, not here. Default: 'bar'.
width
Fixed chart width in px. Omit to fill the container responsively (required for tests/jsdom, which size the container to 0).
Line chart
Section titled “Line chart”Pass index (the x-axis key) and categories (the series keys). Lines interpolate as a monotone curve by default; set curveType="linear" or showDots to change the look.
import { LineChart } from "stylus-ui/Chart";
const data = [ { week: "W1", runs: 40, edits: 12 }, { week: "W2", runs: 55, edits: 18 }, { week: "W3", runs: 48, edits: 22 }, { week: "W4", runs: 63, edits: 20 }, { week: "W5", runs: 71, edits: 28 },];
export default () => ( <div className="h-64 w-full"> <LineChart data={data} index="week" categories={["runs", "edits"]} showDots /> </div>);With a coordinated legend
Section titled “With a coordinated legend”Wrap either chart in Chart with categories so the Legend and hover state stay in sync, exactly like DonutChart.
import { BarChart, Chart, Legend, LegendItem } from "stylus-ui/Chart";
const data = [ { team: "Store Ops", clean: 62, manual: 24, rework: 14 }, { team: "Distribution", clean: 44, manual: 34, rework: 22 }, { team: "Planning", clean: 70, manual: 22, rework: 8 },];
const categories = ["clean", "manual", "rework"];
export default () => ( <Chart categories={categories} className="flex flex-col gap-4"> <div className="h-56 w-full"> <BarChart data={data} index="team" categories={categories} stacked /> </div> <Legend className="flex-row flex-wrap gap-2"> <LegendItem dataKey="clean" label="Flows cleanly" value="" /> <LegendItem dataKey="manual" label="Manual handling" value="" /> <LegendItem dataKey="rework" label="Rework" value="" /> </Legend> </Chart>);API reference
Section titled “API reference”A line chart for trends and time series with one or more series. Set area to
fill under each line with a soft gradient. Colors and hover state sync with
Chart context when nested inside a Chart component; otherwise it renders
standalone from its own props.
categories
Series keys to render as lines. Each becomes one line, colored positionally.
index
The key in each data object used for the x-axis (e.g. a date or week label).
area
Fill the space under each line with a soft gradient of its accent color, fading to transparent. Default: false.
colors
Accent color keys assigned positionally to categories (accent-1..accent-5, accent-neutral). Pass an explicit list to pin a color per series. Defaults to the accent series.
connectNulls
Bridge gaps where a series value is null/undefined. Default: false.
curveType
Line interpolation. Default: 'monotone'.
data
Array of data objects, one per x-axis position. Falls back to Chart context data.
desc
Accessible description, rendered into the SVG <desc>.
emptyMessage
Content shown when there is no data. Default: “No data”.
height
Fixed chart height in px. Omit to fill the container responsively.
loading
Render a loading skeleton instead of the chart.
showDots
Draw a dot at each data point. Default: false.
showGrid
Show the cartesian grid. Default: true.
showTooltip
Show the hover tooltip. Default: true.
showXAxis
Show the x-axis. Default: true.
showYAxis
Show the y-axis. Default: true.
ticks
Explicit value-axis tick positions. Omit to let recharts choose. Pass this to control the ticks when the raw domain (e.g. seconds) formats to awkward labels.
title
Accessible name for the chart, rendered into the SVG <title>. Give charts a title so assistive tech can name the graphic.
valueFormatter
Formats axis + tooltip values.
width
Fixed chart width in px. Omit to fill the container responsively (required for tests/jsdom, which size the container to 0).
A tokenized tooltip for cartesian charts. Replaces recharts’ hardcoded white
box with a surface that follows the brand-refresh + dark-mode token system.
Each series swatch uses the accent fill class the chart passes in
colorClassNames, so it matches the rendered bar/line exactly.
active
Whether the tooltip is active (hovering a data point). Injected by recharts.
colorClassNames
Map of series key → accent fill class, so each swatch matches its bar/line.
label
The x-axis label for the hovered point. Injected by recharts.
payload
The hovered payload entries (one per series). Injected by recharts.
valueFormatter
Formats each series value shown in the tooltip.
Shared API
Section titled “Shared API”Chart (and its useChartContext hook), Legend, and LegendItem are shared across every chart type.
Chart wrapper component that provides interaction state management and color coordination for child components. Automatically synchronizes hover, click states, and colors between DonutChart, Legend, and other chart components.
children
Child components (DonutChart, Legend, etc.)
categories
Explicit list of series keys for series charts (BarChart, LineChart). Each
key is a dataKey shared across every data point. When provided, colors are
assigned positionally to these keys and take precedence over data/category
derivation — this is the series model, as opposed to the slice model that
data + category expresses for DonutChart.
category
The key in data objects to use as category names. Required when data is provided.
colors
Accent color keys (accent-1..accent-5, accent-neutral) assigned
positionally to slices (DonutChart) or series (BarChart/LineChart). Defaults
to the accent series.
Legacy hue keys are still accepted for backward compatibility with un-migrated consumers, but they are ignored — the default accent cycle is used instead, so nothing renders a legacy color. Prefer accent keys.
data
Array of data objects. When provided with category, enables automatic color coordination between child components.
hoveredDataKey
Controlled hover state (dataKey of the hovered item). When provided, the component operates in controlled mode.
onClick
Callback fired when an item is clicked. Use this for controlled selection state.
onHover
Callback fired when an item is hovered. Use this for controlled hover state.
selectedDataKey
Controlled selection state (dataKey of the selected item). When provided, the component operates in controlled mode.
useChartContext
Section titled “useChartContext”Hook to access chart interaction context. Returns null if used outside of a Chart provider. Components can use this to sync their interaction state automatically.
Legend
Section titled “Legend”A legend component for charts that displays colored indicators with labels and values. Automatically syncs with Chart context when used inside a Chart component.
children
Child LegendItem components to display
LegendItem
Section titled “LegendItem”A single item in a Legend component. Must be used as a child of Legend. Automatically syncs with Chart context when available.
dataKey
Unique dataKey for the legend item. Used for tracking active/highlighted state and syncing with chart data.
label
The label text to display (e.g., “Notion”, “Slack”, “Revenue”)
value
The value text to display (e.g., “35%”, “1,234”, “$50K”)
icon
Optional icon to display before the label
labelClassName
Optional className to apply to the label text
logo
Optional logo to display before the label
onClick
Callback fired when clicking the legend item. Receives the item dataKey.
size
Text size for label and value. Default: ‘sm’.