Chart frame

The shell every chart shares: series mapped to colour tokens, locale-aware formatting, an interactive legend, loading / refreshing / empty / error states, a generated text summary and a data-table alternative. Ready charts sit on top of it; raw Recharts works inside it too.

Recharts 3 charts/chart-shell.tsx, charts/chart.tsx, charts/utils.tstheme · material · accent · density · font

Oct 1 – Oct 30. Visits: low 1,180 (Oct 1), high 1,855 (Oct 26), average 1,500.93. Full reads: low 661 (Oct 1), high 1,065 (Oct 26), average 848.47.

Traffic, October
DateVisitsFull reads
Oct 1, 20261,180661
Oct 2, 20261,300760
Oct 3, 20261,412785
Oct 4, 20261,417804
Oct 5, 20261,499812
Oct 6, 20261,560878
Oct 7, 20261,504862
Oct 8, 20261,523835
Oct 9, 20261,431799
Oct 10, 20261,422826
Oct 11, 20261,407781
Oct 12, 20261,300736
Oct 13, 20261,296696
Oct 14, 20261,212734
Oct 15, 20261,241713
Oct 16, 20261,292704
Oct 17, 20261,269706
Oct 18, 20261,360790
Oct 19, 20261,373812
Oct 20, 20261,490840
Oct 21, 20261,610870
Oct 22, 20261,632967
Oct 23, 20261,739989
Oct 24, 20261,8291,003
Oct 25, 20261,8061,005
Oct 26, 20261,8551,065
Oct 27, 20261,7891,043
Oct 28, 20261,7991,011
Oct 29, 20261,794971
Oct 30, 20261,687996

Installation

$ pnpm add @vamos-ui/react

The whole kit as one package — get updates by bumping the version. Add the styles to globals.css: @import "@vamos-ui/react/styles.css";

Usage

import { AreaChart, type ChartConfig } from "@vamos-ui/react/charts";

const config = {
  visits: { label: "Visits" },
  revenue: { label: "Revenue", format: "currency", color: "chart-2" },
} satisfies ChartConfig;

<AreaChart title="Traffic" data={rows} x="date" config={config} height="md" />

Recharts loads only through @vamos-ui/react/charts — pages without charts never ship it.

Tooltips are opaque (--chart-tooltip) and never blur, even in glass mode: they re-render on every pointer move.

Strokes and text never use the raw accent (lime fails contrast on white); chart-1 is accent-ink.

Examples

Chart card with range (CRM)

Card header + 7D / 30D / 90D switch. The chart's title stays the accessible name; the visible title lives in the card.

CRM · Sales

Pipeline value

Open and closed-won deals, daily

Oct 11 – Nov 9. Open pipeline: low $563,610.00 (Oct 11), high $655,702.00 (Oct 24), average $627,344.30. Closed won: low $186,379.00 (Oct 11), high $248,256.00 (Oct 29), average $229,276.67.

Pipeline value, last 30 days
DateOpen pipelineClosed won
Oct 11, 2026$563,610.00$186,379.00
Oct 12, 2026$573,287.00$189,919.00
Oct 13, 2026$583,507.00$193,887.00
Oct 14, 2026$593,978.00$198,214.00
Oct 15, 2026$604,397.00$202,817.00
Oct 16, 2026$614,466.00$207,608.00
Oct 17, 2026$623,899.00$212,493.00
Oct 18, 2026$632,434.00$217,375.00
Oct 19, 2026$639,848.00$222,159.00
Oct 20, 2026$645,960.00$226,749.00
Oct 21, 2026$650,643.00$231,059.00
Oct 22, 2026$653,825.00$235,006.00
Oct 23, 2026$655,495.00$238,521.00
Oct 24, 2026$655,702.00$241,545.00
Oct 25, 2026$654,554.00$244,032.00
Oct 26, 2026$652,211.00$245,954.00
Oct 27, 2026$648,883.00$247,294.00
Oct 28, 2026$644,818.00$248,056.00
Oct 29, 2026$640,295.00$248,256.00
Oct 30, 2026$635,608.00$247,929.00
Oct 31, 2026$631,060.00$247,120.00
Nov 1, 2026$626,948.00$245,893.00
Nov 2, 2026$623,552.00$244,320.00
Nov 3, 2026$621,123.00$242,483.00
Nov 4, 2026$619,872.00$240,471.00
Nov 5, 2026$619,967.00$238,381.00
Nov 6, 2026$621,518.00$236,307.00
Nov 7, 2026$624,579.00$234,347.00
Nov 8, 2026$629,144.00$232,593.00
Nov 9, 2026$635,146.00$231,133.00

Colour tokens

Series reference tokens (chart-1…6, accent-ink, success…), never hex — each scope re-colours itself across accents and materials.

lime · solid

W40 – W45. Sign-ups: low 182 (W40), high 298 (W45), average 232.67. Trials: low 96 (W40), high 177 (W45), average 139.

Sign-ups, lime
WeekSign-upsTrials
W4018296
W41214120
W42198131
W43263148
W44241162
W45298177

ocean · glass

W40 – W45. Sign-ups: low 182 (W40), high 298 (W45), average 232.67. Trials: low 96 (W40), high 177 (W45), average 139.

Sign-ups, ocean
WeekSign-upsTrials
W4018296
W41214120
W42198131
W43263148
W44241162
W45298177

ember · solid

W40 – W45. Sign-ups: low 182 (W40), high 298 (W45), average 232.67. Trials: low 96 (W40), high 177 (W45), average 139.

Sign-ups, ember
WeekSign-upsTrials
W4018296
W41214120
W42198131
W43263148
W44241162
W45298177

rose · glass

W40 – W45. Sign-ups: low 182 (W40), high 298 (W45), average 232.67. Trials: low 96 (W40), high 177 (W45), average 139.

Sign-ups, rose
WeekSign-upsTrials
W4018296
W41214120
W42198131
W43263148
W44241162
W45298177

graphite · solid

W40 – W45. Sign-ups: low 182 (W40), high 298 (W45), average 232.67. Trials: low 96 (W40), high 177 (W45), average 139.

Sign-ups, graphite
WeekSign-upsTrials
W4018296
W41214120
W42198131
W43263148
W44241162
W45298177

Formatters, en-US vs tr-TR

One FormatSpec per series. Axis ticks are compact, tooltip and table use the full form, all from the LocaleProvider.

en-US

Jun – Oct. Revenue: low $1,092,300.00 (Aug), high $1,712,400.00 (Oct), average $1,372,820.00.

Monthly revenue
MonthRevenue
Jun$1,184,000.00
Jul$1,326,500.00
Aug$1,092,300.00
Sep$1,548,900.00
Oct$1,712,400.00

tr-TR

Haz – Eki. Revenue: low ₺1.092.300,00 (Ağu), high ₺1.712.400,00 (Eki), average ₺1.372.820,00.

Monthly revenue
MonthRevenue
Haz₺1.184.000,00
Tem₺1.326.500,00
Ağu₺1.092.300,00
Eyl₺1.548.900,00
Eki₺1.712.400,00

States

Chart-shaped skeletons, a dimmed refresh, an empty state that keeps its height and an error with retry.

loading — chart-shaped, no spinner

Loading chart…

W41 – W44. Tickets: low 128 (W42), high 167 (W43), average 147.

refreshing — old data dimmed, thin bar

Updating…

W41 – W44. Tickets: low 128 (W42), high 167 (W43), average 147.

Tickets
WeekTickets
W41142
W42128
W43167
W44151

empty — keeps its height

No tickets in the last 7 days

error — retry

    View as table

    The data table is always present for screen readers; tableToggle shows it to everyone.

    May – Oct. Starter: low 4.6% (Oct), high 6.4% (Jul), average 5%. Team: low 2.4% (Oct), high 3.4% (May), average 3%. Business: low 0.8% (Oct), high 1.4% (Jun), average 1%.

    Monthly churn by plan
    MonthStarterTeamBusiness
    May6.1%3.4%1.2%
    Jun5.8%3.1%1.4%
    Jul6.4%2.9%1.1%
    Aug5.2%2.7%0.9%
    Sep4.9%2.6%1%
    Oct4.6%2.4%0.8%

    Controlled legend

    Legend items are toggle buttons (aria-pressed). The last visible series cannot be hidden.

    Engineering – Finance. Annual leave: low 37 (Finance), high 142 (Engineering), average 83.4. Sick leave: low 9 (Finance), high 41 (Support), average 24.4.

    Leave days by team, 2026
    TeamAnnual leaveSick leaveParental leave
    Engineering1423860
    Sales962214
    Support884120
    Marketing541230
    Finance3790

    hidden: parental

    Drill-down (CMS)

    onPointClick on a bar, or on a row header in the table view — the keyboard path.

    Product news – Engineering. Posts: low 9 (Case studies), high 31 (Guides), average 18.

    Posts by category
    CategoryPosts
    Product news18
    Guides31
    Case studies9
    Engineering14

    Click a bar (or a row in the table view) to list its posts.

    Raw Recharts

    Any Recharts tree inside <Chart> + <ChartFrame>; use ChartXAxis, ChartYAxis, ChartTooltip and seriesColor(key).

    00:00 – 20:00. p95: low 241 ms (04:00), high 418 ms (12:00), average 333.5 ms. p50: low 98 ms (04:00), high 168 ms (12:00), average 132.83 ms.

    API latency
    Hourp95p50
    00:00284 ms112 ms
    04:00241 ms98 ms
    08:00362 ms141 ms
    12:00418 ms168 ms
    16:00391 ms152 ms
    20:00305 ms126 ms

    API

    Chart (shared by every ready chart)

    PropTypeDefault
    title
    Required. Accessible name of the figure and caption of the data table (not shown visually).
    string—
    data
    Rows. Dates as "YYYY-MM-DD" strings, money as numbers or decimal strings.
    T[]—
    config
    Series key → { label, color?, format?, axis?, pattern?, icon?, hidden? }. Key order = legend and colour order.
    ChartConfig—
    x
    Category / time key.
    keyof T—
    xFormat
    x format. Default: "YYYY-MM-DD" → "Nov 1", everything else as is.
    FormatSpec—
    yFormat
    Value axis format. Default: the first series' format, compact.
    FormatSpec—
    xLabel
    Header of the x column in the data table.
    string—
    height
    160 / 260 / 360 px or pixels. Reserved before the chart measures (no layout shift).
    "sm" | "md" | "lg" | number"md"
    aspect
    Width / height ratio instead of a height.
    number—
    state
    { status: "loading" | "refreshing" | "empty" (message, action) | "error" (message, onRetry) | "ready" }. Default: empty when data is empty.
    ChartState—
    summary
    Text alternative (aria-describedby). Default: generated range, low, high and average per series.
    string | (data) => string—
    tableToggle
    Shows the "View as table" switch.
    booleanfalse
    legend
    Legend position. Items toggle series.
    "top" | "bottom" | "none""top" with 2+ series
    hiddenSeries / defaultHiddenSeries / onHiddenSeriesChange
    Controlled or uncontrolled hidden series.
    string[]—
    tooltipIndicator
    Swatch in tooltip rows.
    "dot" | "line" | "dashed""dot"
    animate
    Transitions on data change; always off under prefers-reduced-motion.
    booleantrue
    onPointClick
    Drill-down. Also turns the table's row headers into buttons.
    (row, seriesKey) => void—
    toolbar
    Extra controls at the end of the legend row.
    ReactNode—
    labels
    Built-in strings (chart namespace): viewTable, empty, error, retry, total, other…
    Partial<ChartLabels>—

    SeriesConfig

    PropTypeDefault
    label
    Legend, tooltip, table header and summary text.
    string—
    color
    "chart-1"…"chart-6" | "accent" | "accent-ink" | "success" | "warning" | "danger" | "info" | "fg-subtle" | "fg-muted". Exposed as --series-<key>.
    ChartColorchart-1…6 in order
    format
    "number" | "integer" | "compact" | "percent" (ratio) | "currency" | "duration" | "bytes" | { currency, compact? } | { date } | { relative: true } | (v, locale) => string.
    FormatSpec"number"
    axis
    Value axis side (Combo). Logical — swaps in RTL.
    "start" | "end"—
    pattern
    Stroke pattern. Auto-assigned with more than 3 series.
    "solid" | "dashed" | "dotted"—
    icon
    Icon in place of the legend swatch.
    ComponentType—
    hidden
    Start hidden.
    boolean—

    Raw-mode parts

    PropTypeDefault
    ChartFrame
    Sizes one Recharts chart to the Chart body.
    component—
    ChartTooltip
    Tooltip with the themed, opaque content (indicator, showTotal, showShare, hideLabel).
    component—
    ChartXAxis / ChartYAxis
    Axes with token ticks, formatters, measured y width (format, keys, stacked).
    component—
    ChartGrid / ChartReferenceLine
    Grid in --chart-grid; dashed reference line with label and colour token.
    component—
    useChart()
    config, series, visibleSeries, isHidden, toggleSeries, formatSeries, formatTick, formatX, f, t.
    hook—
    resolveChartColor / seriesColor / seriesPalette
    "chart-2" → "var(--chart-2)"; key → "var(--series-key)"; first n palette tokens.
    function—