Charts

BarChart

Vertical or horizontal bars, grouped or stacked, in pure SVG. The value axis always includes zero - a bar chart that starts anywhere else misrepresents its own data - and negative bars extend the other side of the zero line.

Server safePure SVG0 dependenciesSource

Import

Charts live at their own subpath with their own stylesheet, so an app that never draws one pays nothing for them.

import { BarChart } from '@the_viveksingh/vivek-ui/charts'
import '@the_viveksingh/vivek-ui/charts.css'

Vertical bars

`showValues` prints the number on each bar, so the chart is readable without hovering.

Revenue by month01000200030004000JanFebMarAprMayJun120026002450380032004600
Revenue by month
CategoryValue
Jan1200
Feb2600
Mar2450
Apr3800
May3200
Jun4600
<BarChart
  data={[
    { x: 'Jan', y: 1200 },
    { x: 'Feb', y: 2600 },
    { x: 'Mar', y: 2450 },
    { x: 'Apr', y: 3800 },
  ]}
  title="Revenue by month"
  height={240}
  showValues
/>

Grouped and stacked

New customers by plan0204060Q1Q2Q3
New customers by plan
CategoryProTeam
Q14012
Q26218
Q37124
<BarChart
  series={[
    { name: 'Pro', data: [{ x: 'Q1', y: 40 }, { x: 'Q2', y: 62 }, { x: 'Q3', y: 71 }] },
    { name: 'Team', data: [{ x: 'Q1', y: 12 }, { x: 'Q2', y: 18 }, { x: 'Q3', y: 24 }] },
  ]}
  mode="grouped"   /* or "stacked" */
  showLegend
  title="New customers by plan"
  height={260}
/>

Horizontal, for long labels

Category names that would collide on a vertical axis read fine horizontally.

Sessions by sourceOrganic searchDirect trafficSocial referralPaid campaigns01000200030004000420031001800900
Sessions by source
CategoryValue
Organic search4200
Direct traffic3100
Social referral1800
Paid campaigns900
<BarChart
  data={[
    { x: 'Organic search', y: 4200 },
    { x: 'Direct traffic', y: 3100 },
    { x: 'Social referral', y: 1800 },
    { x: 'Paid campaigns', y: 900 },
  ]}
  horizontal
  showValues
  title="Sessions by source"
  height={260}
/>

Accessibility

A chart is not an image

Every chart carries role="img" with a generated accessible name, and renders a real <table> alongside it — visually hidden, never display: none — so a screen-reader user gets the actual numbers instead of "chart". Set accessibleTable={false} only if you have provided the data in a table elsewhere on the page.

No series is ever encoded by colour alone: each carries a distinct dash pattern and marker shape on top of its colour, so the chart survives greyscale printing and every common form of colour blindness.

The palette is verified rather than asserted. Each theme has its own six colours, because one set cannot clear the 3:1 non-text contrast threshold against both a white and a near-black plot surface. Separation is measured under simulated protanopia, deuteranopia and tritanopia: for one to four series — which is nearly every chart — the closest pair sits at ΔE 17, slightly better than the unmodified Okabe-Ito palette it is derived from. Past four series the dash patterns and marker shapes carry the distinction.

Hostile data

Real series contain gaps and rubbish. Every chart is tested against empty arrays, a single point, all-equal values, negatives crossing zero, NaN, ±Infinity, 1e308 and 1e-320, with an assertion that no rendered SVG attribute ever contains NaN. A bad row degrades the chart; it does not blank your page.


Props

Generated from the package's own type declarations, so this table cannot drift from the code.

Props for BarChart
PropTypeDefaultDescription
dataChartDataSingle series. `[{ x, y }]`, or a bare `number[]` where the index is the x.
seriesreadonly ChartSeries[]Multiple series. Takes precedence over `data` when both are given.
heightnumber
showGridboolean
showAxesboolean
showLegendboolean
interactiveLegendbooleanTurn each legend entry into a checkbox that shows and hides its series, with a fade. No JavaScript and no client boundary: the entries are real checkboxes and the chart reacts with `:has()`. Off by default, because it makes the legend a set of controls - right for a dashboard, wrong for a figure in a report.
mode'grouped' | 'stacked'`grouped` puts a series side by side inside each category; `stacked` piles them, with negatives stacking downward from zero.
horizontalbooleanBars run left to right, with the categories down the left edge.
barRadiusnumberCorner radius, in viewBox px. Clamped so short bars do not turn into lozenges.
categoryPaddingnumberFraction of each category slot left empty, 0 to 0.9.
showValuesbooleanPrint each value at the end of its bar - the clearest non-colour cue there is.
tooltipbooleanPointer hover tooltip. Mouse only, and ignored for `horizontal`.