Bar Chart

Plot one series of values as vertical bars, over categories such as 'Mon' or along a numeric axis, with a touch tooltip, pan and zoom, and dark mode built in.

Installation

npx @blotch/dolan add bar-chart
import { UiBarChart } from '../components/ui/bar-chart';

This also adds Chart, which draws the preset, and installs the npm packages @tanstack/charts and d3-shape.

Usage

import { Component } from '@angular/core';
import { UiBarChart } from '../components/ui/bar-chart';

@Component({
  selector: 'app-visits',
  imports: [UiBarChart],
  template: `<ui-bar-chart [data]="visits" label="Visits per day" />`,
})
export class Visits {
  protected readonly visits = [
    { x: 'Mon', y: 42 },
    { x: 'Tue', y: 58 },
    { x: 'Wed', y: 71 },
    { x: 'Thu', y: 49 },
  ];
}
  • data is an array of { x, y } items. y is a number, and x is a category label or a number.
  • Category labels: if any x is a string, each bar gets an equal slot in data order, labeled with its x.
  • Numbers: if every x is a number, the bars sit at their values on a numeric axis. Uneven gaps stay visible, and the x axis can pan and zoom.
  • Touch the chart to see a bar's value in a tooltip. Tap to pin it. Set label so a screen reader can say what the chart shows.

Customize

Colors

color fills every bar (default var(--primary)). A theme color such as var(--chart-2) switches with dark mode. A fixed color stays the same. Pass colors to give each bar its own color. The list repeats if it's shorter than the data.

<ui-bar-chart [data]="visits" [colors]="['var(--chart-1)', 'var(--chart-2)']" />

Shape

Bars grow from baseline (default 0), and the y axis always includes it. Values below the baseline grow down, so gains and losses read as a diverging chart.

<ui-bar-chart
  [data]="profit"
  [baseline]="0"
  [radius]="4"
  [barWidthRatio]="0.5"
/>
  • radius rounds each bar's corners, in px.
  • barWidthRatio sets how much of its slot each bar fills, from 0 to 1 (default 0.7). With numeric x, a slot is the smallest gap between two x values.
  • To color by sign, pass colors built from the data, such as profit.map((d) => (d.y >= 0 ? 'rgba(34, 197, 94, 1)' : 'rgba(239, 68, 68, 1)')).

Axes and range

xAxisLabel and yAxisLabel add axis titles. Horizontal gridlines are on by default: showGrid turns them off, and showXGrid adds vertical ones.

<ui-bar-chart
  [data]="visits"
  xAxisLabel="Day"
  yAxisLabel="Visits"
  [yMin]="0"
  [yMax]="100"
/>
  • yMin and yMax fix the y axis instead of fitting the data, for example to compare two charts on one scale.
  • yTickFormat turns y tick values into labels, and tickCount sets roughly how many ticks the axis gets.
  • With numeric x, xMin, xMax and xTickFormat do the same for the x axis.
  • With category labels, the x axis always shows every category. xMin, xMax, xTickFormat and the x tickCount don't apply to it.

Padding

padding insets the plot, each side as a fraction (0 to 1) of its size. With category labels, only top and bottom apply.

<ui-bar-chart [data]="visits" [padding]="{ top: 0.1 }" />

Size

The chart fills its container's width and is 200px tall. Set height for another height and width for a fixed width, both in px. Use height rather than a height class, because the preset always sets a height.

<ui-bar-chart [data]="visits" [height]="160" />

Interaction

Taps

barTap emits the tapped { x, y } item. A tap on empty space emits nothing. Set tooltip to false to turn the tooltip off.

<ui-bar-chart [data]="visits" (barTap)="selected.set($event)" />

barTap fires inside the touch, so wrap router navigation in setTimeout (see the setTimeout rule).

Pan and zoom

Set zoomable to let users drag to pan, with +, − and 1:1 buttons. Zooming in on a numeric x axis spreads the bars apart. A category axis doesn't zoom, so with category labels only the y axis does.

<ui-bar-chart [data]="sales" [zoomable]="true" zoomAxes="x" />

To zoom from your own controls, set showZoomControls to false and call zoomIn(), zoomOut() or resetZoom() on the component. See Pan and zoom for how it behaves.

API

UiBarChart

InputTypeDefaultDescription
datareadonly BarDatum[]RequiredBars, each { x, y }.
colorstring'var(--primary)'Fill for every bar (any CSS color).
colorsreadonly string[][]One fill per bar, repeating. Overrides color when not empty.
baselinenumber0The value bars grow from. Values below it grow down.
barWidthRationumber0.7Bar width as a fraction of its slot, 0–1.
radiusnumber0Corner radius in px.
widthnumberfills containerFixed width in px.
heightnumber200Height in px.
tickCountnumber5Target number of ticks per axis.
showGridbooleantrueHorizontal gridlines at the y ticks.
showXGridbooleanfalseVertical gridlines at the x ticks.
xAxisLabelstring''X-axis title.
yAxisLabelstring''Y-axis title.
paddingChartPadding{}Inner inset, each side a 0–1 fraction of the plot.
xMinnumberfits the dataFixed x-axis lower bound (numeric x only).
xMaxnumberfits the dataFixed x-axis upper bound (numeric x only).
yMinnumberfits the dataFixed y-axis lower bound.
yMaxnumberfits the dataFixed y-axis upper bound.
xTickFormat(value: number) => stringautomaticFormats x-axis tick labels (numeric x only).
yTickFormat(value: number) => stringautomaticFormats y-axis tick labels.
tooltipbooleantrueShow a tooltip for the touched bar. A tap pins it.
labelstring'Chart'What a screen reader announces for the chart.
colorScheme'light' | 'dark' | 'system'from provideChartThemeWhich color table the marks use.
zoomablebooleanfalseDrag to pan, with zoom buttons.
zoomAxes'x' | 'y' | 'xy''xy'Which axes pan and zoom.
showZoomControlsbooleantrueShow the +/−/1:1 buttons when zoomable.
minZoomnumber1Smallest zoom. 1 shows all the data.
maxZoomnumber8Largest zoom.
zoomStepnumber1.4Zoom factor per button press or method call.
classstring''Extra Tailwind classes for the chart's root view.
OutputTypeDescription
barTapBarDatumThe bar a tap landed on.
MethodDescription
zoomIn()Zooms in by zoomStep.
zoomOut()Zooms out by zoomStep.
resetZoom()Shows all the data again.

BarDatum is { x: number | string; y: number }, exported from ../components/ui/bar-chart. ChartPadding is { top?, right?, bottom?, left? }, each a fraction (0–1) of the plot's size, with omitted sides 0. It's exported from ../components/ui/chart.

barChartDefinition

function barChartDefinition(options: BarChartOptions): ChartDefinition;

Returns the TanStack Charts definition <ui-bar-chart> renders: barY on a band scale for category labels, or rect on a linear scale for numbers. The options are the inputs that shape the chart, with the same defaults: data, color, colors, baseline, barWidthRatio, radius, tickCount, showGrid, showXGrid, xAxisLabel, yAxisLabel, padding, xMin, xMax, yMin, yMax, xTickFormat, yTickFormat and tooltip. Size, label, color scheme and zoom are inputs of <ui-chart> instead.

Start from it to add marks of your own, such as a target line, and render the result with <ui-chart>:

import { defineChart, ruleY } from '@tanstack/charts/universal';
import { barChartDefinition } from '../components/ui/bar-chart';

const base = barChartDefinition({ data: visits, radius: 4 });

protected readonly withTarget = defineChart({
  ...base,
  marks: [
    ...base.marks,
    ruleY([60], {
      stroke: 'var(--destructive)',
      strokeOpacity: 1,
      strokeDasharray: '4 4',
    }),
  ],
});

For grouped or stacked bars, build a definition with TanStack's barY and a z channel. A z channel stacks the series, and layout: group() places them side by side.