Skip to content

LineChart

Plots one or more series against an ordered category axis. Reach for it when two neighbouring points are part of one continuous change, such as a value over time or a curve over a range, rather than two separate facts.

tsx
import { LineChart } from 'neba';

<LineChart
  label="Weekly active users by month"
  categories={['Jan', 'Feb', 'Mar']}
  series={[
    { name: 'Web', data: [1820, 1960, 2140] },
    { name: 'Mobile', data: [940, 1120, 1310] }
  ]}
/>;

The data

Every chart in the library takes the same two props, so a dashboard tile can be switched from one chart to another without rewriting its data.

series is an array of NebaChartSeries. Each entry is one line:

ts
interface NebaChartSeries {
  name?: string; // its name in the legend, tooltip and table
  data: readonly NebaChartDatum[]; // the values, in category order
  color?: NebaColor | string; // overrides the palette slot
  hidden?: boolean; // starts hidden; the legend turns it back on
}

A NebaChartDatum is a number, a null, or a point:

ts
type NebaChartDatum = number | null | NebaChartPoint;

interface NebaChartPoint {
  x?: string | number | Date; // its place on the category axis
  y: number | null; // the value
  color?: string; // overrides the series colour for this point
  label?: ReactNode; // what the tooltip says instead of the number
}

null is a gap, not a zero. A sensor that was offline and a month with no sales are different facts, and the chart draws them differently: the line breaks at a null and the point is not drawn. connectNulls bridges it, and should only be used when the gap is an artefact of how the data was collected.

categories names the positions along the x axis. Points may carry their own x instead: whichever matches the shape the data already has.

Props

PropTypeDefaultDescription
series * NebaChartSeries[]The series. A colour is decided by a series' place in this array, so hiding one from the legend never repaints the survivors
categories(string | number | Date)[]The category axis' labels. Points may carry their own x instead
xAxisNebaChartAxisThe category axis
yAxisNebaChartAxisThe value axis
curve'linear' | 'smooth' | 'step''linear'How the line gets from one point to the next. smooth is a monotone cubic, so it never dips below a value both neighbours are above; step holds each value until the next reading
markers'none' | 'auto' | 'all''auto'Dots on the points. auto draws them up to fourteen points. Whatever this says, the point under the pointer always gets one
gradientbooleanfalseFades the line from a paler step of its own hue at the start to the full colour at the end, so the recent end is the loud one
connectNullsbooleanfalseDraws straight through a null instead of breaking at it. Only when the gap is an artefact of collection: a bridged gap is a number the chart made up
valueLabels'none' | 'last' | 'extremes' | 'all''none'Which values are written on the line. last names where each series ended up, extremes marks its own high and low. The default is none: a number beside every point is the most reliable way to make a chart unreadable
stackedbooleanfalseStacks the series. Rare on a line chart: an AreaChart is the shape that makes stacking legible
variantshared'solid' | 'outline' | 'text''text'Weight of the surface. A chart is a drawing rather than a sheet, so this defaults to text and a chart inside a Card draws no second edge. Use outline for one that stands alone
sizeshared'xs' | 'sm' | 'md' | 'lg' | 'xl''md'Axis type, line weight, marker size, and the height, when none is given
colorshared'primary' | 'secondary' | 'success' | 'warning' | 'danger' | 'info''primary'The sheet's colour family. A series' colour does not come from here: the palette or series.color decides that
densityshared'default' | 'compact''default'Padding only: never the height, never the type scale
elevationshared0 | 1 | 2 | 30Drop shadow depth. 0 means no shadow at all
heightnumber | stringsizeHow tall the drawing is. The axis labels are drawn inside it, so a card sized to the chart is a card the chart fits in
labelstringlocale's wordThe chart's accessible name. Read out in place of the drawing, and the caption of the hidden data table under it. Without it the locale's generic word stands in, but what the chart is *of* can only be said here
formatIntl.NumberFormatOptionsHow numbers are written everywhere they appear: the axis, the tooltip, the value labels, the table. Without it, past ten thousand they are compacted (12.4K)
localestringThe language of the chart's own words and dates
legendboolean | NebaChartLegendseries ≥ 2Shown automatically from two series up and left off below that: a legend with one swatch restates the title
tooltipboolean | NebaChartTooltiptrueWhat the pointer uncovers. It never carries a value that is not also in the hidden table
emptyReactNodeWhat to draw when there is nothing to draw

Every native <div> attribute passes through, along with every Box prop. variant defaults to text and padded to false, so a chart dropped into a Card draws no sheet of its own; variant="outline" gives it one. See prop conventions for the shared axes.

NebaChartAxis

xAxis and yAxis both take this shape.

PropTypeDefaultDescription
hiddenbooleanfalseLeaves the axis undrawn: its rule, its ticks and its labels. The band goes back to the plot
labelReactNodeA name for what the axis measures
gridboolean값 축은 true / true on the value axisThe gridlines this axis casts across the plot. On for the value axis and off for the category axis; both is graph paper
minnumberWhere the scale starts. Taken from the data otherwise. A BarChart and an AreaChart keep zero; a LineChart crops
maxnumberWhere the scale ends
tickCountnumber5Roughly how many ticks. The scale still rounds to clean numbers
tickFormat(value, index) => ReactNodeHow a tick is written, overriding the chart's own format
thicknessnumberHow much room the axis keeps for its ticks and label, in pixels. Measured from the ticks otherwise; set it to line two charts up on a dashboard

NebaChartLegend

PropTypeDefaultDescription
sideshared'top' | 'right' | 'bottom' | 'left''bottom'Which edge of the plot
alignshared'start' | 'center' | 'end''center'Where along that edge
interactivebooleantrueClicking an entry hides its series; hovering one dims the rest
showValuebooleanfalseDraws each series' value at the active category beside its name

NebaChartTooltip

PropTypeDefaultDescription
mode'index' | 'item' | 'none''index'index shows every series at the category under the pointer, with a crosshair; item shows the one mark being pointed at
crosshairbooleantrueThe line dropped through the plot at the active category, in index mode. It is what says which column the numbers belong to
render(context) => ReactNodeDraws the panel. Without it the chart draws its own

Examples

curve

curve decides how the line gets from one point to the next. linear is the default and claims nothing the data did not say. smooth is a monotone cubic: curved, but it will never dip below a value both of its neighbours are above. step holds each value until the next reading, which is what a rate limit or a plan tier actually did in between.

xAxis · yAxis

A line chart crops its value axis to the data, because a line encodes a position and cropping moves every point by the same amount. Pass yAxis with a min of 0 when zero belongs on the scale.

min, max and tickCount set the range; tickFormat writes each tick; grid: false drops the gridlines; hidden drops the axis entirely and gives its band back to the plot.

connectNulls

valueLabels · gradient · markers

valueLabels writes numbers onto the line: last names where each series ended up, extremes marks each series' own high and low, all labels every point. The default is none: a number beside every point is the most reliable way to make a chart unreadable.

markers puts dots on the points. auto draws them while there are fourteen or fewer; the point under the pointer always gets one regardless.

gradient fades each line from a paler step of its own hue at the start to the full colour at the end.

legend

The legend appears automatically from two series up and is left off below that. side and align place it; clicking an entry hides its series, and the survivors keep the colour they had. legend={false} removes it, interactive: false makes it a key rather than a control.

Colour

Series take palette slots in the order they are passed: eight hues, fixed, never cycled. A ninth series is not a ninth colour; fold the tail into an "Other" series or draw a second chart.

series.color overrides the slot with a NebaColor family or any CSS colour, and a point's own color overrides that for one mark. See colour for the ramp and what it is solved for.

tsx
<LineChart
  series={[
    { name: 'Errors', data: errors, color: 'danger' },
    { name: 'Warnings', data: warnings, color: 'warning' }
  ]}
/>

format

format takes Intl.NumberFormat options and applies everywhere a number appears: the axis, the tooltip, the value labels, the table. Without it, axis ticks past ten thousand are compacted (12.4K).

tsx
<LineChart format={{ style: 'currency', currency: 'USD', maximumFractionDigits: 0 }}  />
<LineChart format={{ style: 'percent', maximumFractionDigits: 1 }}  />

Accessibility

  • Every chart renders a table of its data, visually hidden and available to assistive technology. label becomes its caption and the chart's accessible name. A tooltip never carries a value that is not also in that table.
  • The plot is focusable. and step the crosshair between categories, Home and End jump to the ends, Escape clears it, so the tooltip is reachable without a pointer.
  • The legend is a list of aria-pressed buttons, so which series are drawn is stated rather than implied by colour.
  • Identity is never carried by colour alone: the legend is always present from two series up, and the palette's adjacent pairs are verified against simulated protanopia and deuteranopia.

Released under the MIT License