Skip to content

Histogram

ChartsSince 0.4.0
import { Histogram } from '@absoluteui/components/histogram';
import '@absoluteui/components/histogram/style.css';

Using Angular? See Histogram for Angular.

An accessible SVG histogram of the distribution of raw numeric values in adjacent bins on a linear x axis. It provides:

  • automatic bins (Sturges’ rule on nice boundaries), a bin count, or explicit thresholds; bins are lower-inclusive and the last bin is also upper-inclusive;
  • the binning as a reusable module function, computeBins, which also reports missing and out-of-range values;
  • keyboard-reachable bins (one tab stop), including empty bins, with a tooltip on focus and hover (“10 to 20: 14 values”);
  • a count axis that prints integer ticks only;
  • a generated accessible summary (value count, bin count, binned range, most frequent bin, values outside the bins) and a data table fallback (bin, count).
  • The shape of a distribution of one numeric variable (response times, amounts, scores).
  • Checking skew, spread, and outliers before choosing a summary statistic.
  • Comparing categories: use Bar Chart.
  • Comparing distributions of several groups: use Box Plot.
  • Relationships between two variables: use Scatter Chart.

Response-time distribution with automatic bins

Section titled “Response-time distribution with automatic bins”
import { Histogram } from '@absoluteui/components/histogram';
const values = Array.from({ length: 500 }, (_, i) => 80 + ((i * 37) % 200));
const host = document.createElement('div');
document.body.append(host);
const chart = Histogram.create(host, {
label: 'Response times',
values,
binFormat: { style: 'unit', unit: 'millisecond' },
xAxis: { label: 'Response time' },
});
host.dataset.bins = String(chart.bins.length);

Explicit thresholds and computeBins for a custom table

Section titled “Explicit thresholds and computeBins for a custom table”
import { Histogram, computeBins } from '@absoluteui/components/histogram';
const scores = [12, 45, 67, 68, 71, 88, 90, 95, null, 102];
const thresholds = [0, 50, 70, 90, 100];
const { bins, outside, missing } = computeBins(scores, { bins: thresholds });
const host = document.querySelector<HTMLElement>('#scores');
if (host) {
Histogram.create(host, { label: 'Exam scores', values: scores, bins: thresholds, color: 3, dataTable: 'visible' });
host.dataset.counts = bins.map((b) => String(b.count)).join(',');
host.dataset.outside = String(outside + missing);
}
import { Histogram } from '@absoluteui/components/histogram';
import '@absoluteui/components/histogram/style.css';
const host = document.querySelector<HTMLElement>('#amounts');
if (host) {
const chart = Histogram.create(host, {
label: 'Order amounts',
values: [12, 18, 25, 31, 40, 44, 52, 67, 89, 120],
bins: 5,
min: 0,
max: 150,
binFormat: { style: 'currency', currency: 'USD', maximumFractionDigits: 0 },
xAxis: { label: 'Amount' },
});
chart.update({ values: [15, 22, 30, 48, 75] });
}
import { Histogram, computeBins } from '@absoluteui/components/histogram';
const values = [120, 135, 160, 180, 210, 240, 250, 310, null];
const host = document.createElement('div');
document.body.append(host);
const chart = Histogram.create(host, { label: 'Response times', values, binFormat: { style: 'unit', unit: 'millisecond' } });
// The same binning for a custom table or export.
const { bins, outside, missing } = computeBins(values, { bins: [100, 200, 300] });
host.dataset.info = `${String(chart.bins.length)} bins; ${String(bins.length)} custom; ${String(outside)} outside; ${String(missing)} missing`;

Export: @absoluteui/components/chart-export (chartToSvg, chartToPng, downloadChart) exports any chart instance to SVG or PNG.

<div class="aui-chart aui-histogram [aui-chart--patterns]">
<div class="aui-chart__header">
<div class="aui-chart__title" id="T">title</div>
<p class="aui-chart__description" id="D">description</p>
<div class="aui-chart__toolbar" hidden/>
</div>
<ul class="aui-chart__legend" aria-label="Legend" hidden/> (always empty)
<div class="aui-chart__body"> (height: --aui-chart-height)
<svg class="aui-chart__svg" role="group" aria-roledescription="histogram" aria-labelledby="T" aria-describedby="D S">
<g class="aui-chart__grid" aria-hidden="true"/> <g class="aui-chart__axis …" aria-hidden="true"/>
<g class="aui-histogram__bars aui-chart__series--c1">
<rect class="aui-chart__mark aui-chart__bar" role="img" aria-label="0 to 10: 14 values" tabindex="0"/>
<rect class="aui-chart__mark aui-chart__bar" role="img" aria-label="10 to 20: 0 values"/>
</g>
</svg>
<div class="aui-chart__empty" hidden/> <div class="aui-chart__tooltip" aria-hidden="true" hidden/>
</div>
<p class="aui-chart__summary" id="S" hidden>generated summary</p>
<div class="aui-chart__data">...Show data table...</div>
</div>
Histogram.create(host: HTMLElement, options: HistogramOptions): Histogram

Global configuration key: histogram (see Configuration).

Option Type Default Description
label string Chart title: visible text and the accessible name of the plot (aria-labelledby). Required.
hideLabel boolean false Visually hide the title; it stays the accessible name.
description string Visible description below the title, also referenced by aria-describedby (text only).
summary string Replaces the generated accessible summary (aria-describedby).
valueFormat Intl.NumberFormatOptions Number format of bin boundaries (x axis ticks, bar labels, tooltips, table, summary) when binFormat is not set. Counts always use the plain locale number format.
formatValue (value: number) => string Custom text for bin boundaries (x axis ticks included) when binFormat is not set. Counts are not affected.
hiddenSeries readonly string[] Accepted and validated (string[]) for the shared chart contract; a histogram has one data set, so it has no effect.
values readonly (number | null)[] Raw values to bin; null, NaN, and infinities are ignored (counted as missing). Required.
bins 'auto' | number | readonly number[] "auto" auto: Sturges’ rule on nice boundaries; a number (integer 1-500): about that many bins on nice boundaries; an array: 2-501 ascending thresholds (bin edges).
min number Lower edge of the binned domain (finite); with auto or count bins it becomes the exact first edge. Default: a nice boundary at or below the data minimum. Ignored for binning when bins is an array of thresholds (still validated). Values outside the bins are counted and named in the summary.
max number Upper edge of the binned domain (finite, greater than min); with auto or count bins it becomes the exact last edge. Default: a nice boundary at or above the data maximum. Ignored for binning when bins is an array of thresholds.
color ChartColor 1 Palette slot 1-8 of the bars.
binFormat Intl.NumberFormatOptions Number format of bin boundaries (axis, labels, tooltips, table, summary). Default: formatValue, else valueFormat.
xAxis { label?: string } X axis title (also the first table column header; default “Bin”).
yAxis { label?: string; min?: number; max?: number; tickCount?: number; includeZero?: boolean } Count axis: title (default “Count”, always drawn), fixed domain (bars taller than max are clipped), and tick count (2-20). Only integer ticks are printed. includeZero is accepted but has no effect (the count axis starts at zero unless min is set).
legend 'auto' | 'show' | 'hide' "auto" Accepted for the shared configuration; a histogram never shows a legend (the legend list stays empty and hidden). Globally configurable.
dataTable 'toggle' | 'visible' | 'hidden' | 'none' "toggle" Data table fallback with the exact data: behind a Show data table button (built on first open), always visible, visually hidden (screen readers only), or none. Globally configurable.
tooltip boolean true Tooltip on pointer hover and keyboard focus. Globally configurable.
patterns boolean false Toggles the aui-chart–patterns host class; the single bar series draws no texture. Globally configurable.
height number 300 Plot height in CSS px (integer 120-2000); the width follows the container. Globally configurable.
locale string "en-US" Number and message locale. Globally configurable. Inherited from common configuration.
direction 'ltr' | 'rtl' "ltr" rtl mirrors the x axis (lowest bin at the right), puts the count axis on the right, and swaps ArrowLeft/ArrowRight. Globally configurable. Inherited from common configuration.
Property Type Description
host HTMLElement The host element.
element SVGSVGElement The plot (svg role=“group”), named by the title and described by the summary.
summary string The generated (or custom) accessible summary.
bins readonly HistogramBin[] Bins of the current data: { from, to, count }; lower-inclusive, the last bin also upper-inclusive.
hiddenSeries string[] Always empty.
Method Description
update(options: Partial<HistogramOptions>): void Merge, validate, and re-render. Invalid options throw and keep the previous state. Keyboard focus stays on the same mark when it still exists.
setSeriesVisible(id: string, visible: boolean): void Inherited from the shared chart controller; no visible effect.
destroy(): void Remove rendering, host classes, listeners, the resize observer, and the configuration subscription.

All rules live in the aui.components cascade layer, so application styles override them without specificity battles.

Class Purpose
aui-chart Host (shared chart chrome).
aui-histogram Host modifier for this chart type.
aui-chart--patterns patterns option on: textures (and dash patterns) shown.
aui-chart__header / __title / __title--hidden / __description Title and description.
aui-chart__legend / __legend-entry / __legend-item / __legend-item--hidden / __legend-label Legend list (always empty and hidden for a histogram).
aui-chart__swatch aria-hidden legend and tooltip key (marker, dash, texture).
aui-chart__body / __svg / __layer Plot container (height from –aui-chart-height), SVG, and drawing layer.
aui-chart__grid / __gridline / __zero-line / __axis / __axis-line / __tick-label / __axis-title Axes (aria-hidden).
aui-chart__series--c1 ... --c8 Palette slot classes; set –aui-chart-color from color-chart-<n>.
aui-chart__mark / __mark--active / __mark--hover Focusable data mark and its states.
aui-chart__tooltip / __tooltip-title / __tooltip-rows / __tooltip-row / __tooltip-label / __tooltip-value Tooltip (aria-hidden; the focused mark carries the same text).
aui-chart__empty Empty state text (No data, All series are hidden).
aui-chart__summary Hidden summary referenced by aria-describedby.
aui-chart__data / __data--hidden / __table-toggle / __table-scroll / __table / __cell--numeric Data table fallback.
aui-chart__bar / __line / __area / __point / __connector / __value-label Shared mark styles (chart-marks): bars, lines, areas, point markers, dashed connectors, printed values.
aui-histogram__bars Bars group (carries the aui-chart__series–c<n> slot class).

Tokens this stylesheet reads (verified in CI). Override them in a theme or with the Theme Designer.

--aui-border-width | --aui-color-border | --aui-color-border-strong | --aui-color-chart-1 | --aui-color-chart-2 | --aui-color-chart-3 | --aui-color-chart-4 | --aui-color-chart-5 | --aui-color-chart-6 | --aui-color-chart-7 | --aui-color-chart-8 | --aui-color-chart-axis | --aui-color-chart-grid | --aui-color-focus-ring | --aui-color-hover | --aui-color-link | --aui-color-surface | --aui-color-surface-sunken | --aui-color-text | --aui-color-text-muted | --aui-duration-fast | --aui-easing-standard | --aui-focus-ring-offset | --aui-focus-ring-width | --aui-font-family-sans | --aui-font-size-md | --aui-font-size-sm | --aui-font-size-xs | --aui-font-weight-medium | --aui-font-weight-regular | --aui-font-weight-semibold | --aui-line-height-normal | --aui-line-height-tight | --aui-radius-md | --aui-radius-sm | --aui-shadow-md | --aui-space-1 | --aui-space-2 | --aui-space-3 | --aui-tooltip-max-width | --aui-touch-target-min

interface HistogramBin { readonly from: number; readonly to: number; readonly count: number }
interface HistogramBinning { bins?: 'auto' | number | readonly number[]; min?: number; max?: number }
type HistogramConfig = ChartBaseConfig; // { legend; dataTable; tooltip; patterns; height }
interface HistogramOptions extends ChartCommonOptions, Partial<HistogramConfig>, HistogramBinning {
values: readonly (number | null)[];
color?: ChartColor;
binFormat?: Intl.NumberFormatOptions;
xAxis?: { label?: string };
yAxis?: HistogramValueAxis; // { label?; min?; max?; tickCount?; includeZero? }
}
function computeBins(values: readonly (number | null)[], binning?: HistogramBinning): { bins: HistogramBin[]; outside: number; missing: number };

Pattern: SVG chart as a named, described group (role=“group” with aria-roledescription) of role=“img” data marks with roving focus; data table fallback (native table). Follows the WAI Complex Images tutorial (short name plus long description) and the roving tabindex technique of WAI-ARIA APG.

Key Context Action
ArrowLeft / ArrowRight (also ArrowUp / ArrowDown) Bin focused Previous / next bin; Left/Right are mirrored in RTL. Empty bins are reachable too.
Home / End Bin focused First / last bin.
PageUp / PageDown Bin focused Ten bins back / forward.
Escape Tooltip visible Hides the tooltip without moving focus (WCAG 1.4.13); it returns on the next move.
Tab / Shift+Tab Any One tab stop for the plot (the active bin), then the Show data table button and table region.
Element Role
svg.aui-chart__svg group with aria-roledescription (localized chart type), named by the title
data marks img with an aria-label: bin range and value count
axes, tooltip aria-hidden (decorative duplicates of the accessible data)
data table table (native) with caption, column headers, and row headers; wide tables scroll in a focusable region
Attribute Element Value
aria-labelledby svg id of the visible (or visually hidden) title
aria-describedby svg description id (when provided) and the generated summary id
aria-roledescription svg Localized “histogram”
aria-label each mark Localized “{from} to {to}: {count} values” (pluralized)
tabindex active mark 0 on exactly one mark (roving); other marks have none
aria-expanded / aria-controls Show data table button Table open state and the table region id

Roving focus: one bin is in the tab sequence and remembers the last visited bin across re-renders (resize, update). Toggling the table keeps focus on the table button.

Criterion Level Note
1.1.1 Non-text Content A
1.3.1 Info and Relationships A
1.4.1 Use of Color A
1.4.3 Contrast (Minimum) AA
1.4.10 Reflow AA
1.4.11 Non-text Contrast AA
1.4.13 Content on Hover or Focus AA
2.1.1 Keyboard A
2.4.3 Focus Order A
2.4.7 Focus Visible AA
4.1.2 Name, Role, Value A

Automated test results: Accessibility and compliance.

  • The generated summary names the value count, bin count, the binned range, the most frequent bin, and values outside the bins.
  • One data set, so no color coding is needed; bars are separated by a surface-colored outline.
  • Forced colors: bars use CanvasText with a Canvas outline (hovered and active bar outlined in Highlight); gridlines GrayText, axes and text CanvasText; focus uses Highlight.
  • No animation: nothing moves on render; hover and focus emphasis transitions are removed under prefers-reduced-motion and animation: none.
  • Manual screen-reader verification (NVDA, JAWS, VoiceOver, TalkBack) is pending.
  • aria-roledescription support varies; screen readers without it announce “group”.

default, empty (“No data”), bin focused (tooltip), hovered, table open.

From Trigger To
plot not focused Tab active bin focused, tooltip shown
bin focused arrow keys, Home, End, PageUp, PageDown another bin focused
tooltip visible Escape tooltip hidden, focus unchanged
table closed Show data table table built and shown
any update({ values }) bins recomputed and re-rendered; focus kept on the same bin index
any container resize re-render at the new width; focus kept on the same bin
  • The summary names the type, value count, bin count, the binned range, the most frequent bin, and values outside the bins, for example “histogram, 8 values in 4 bins, from 0 sec to 8 sec. Most values: 2 sec to 4 sec (5 values).” Values outside the bins add “3 values outside the bins.”
  • One data set, so no color coding is needed; adjacent bars are separated by a surface-colored outline.
  • Forced colors: bars use CanvasText with a Canvas outline; the hovered and active bar and focus use Highlight. Nothing animates.
  • Manual screen-reader verification (NVDA, JAWS, VoiceOver, TalkBack) is pending.

The width follows the container (ResizeObserver); the height is height. Bin edge labels are thinned (every k-th edge) so they never crowd. Reflows at 320 CSS px.

direction: 'rtl' mirrors the x axis (lowest bin at the right), puts the count axis on the right, and swaps ArrowLeft/ArrowRight.

The bar color comes from color-chart-<n> via color (slot 1 by default). Gridlines use color-chart-grid, axes color-chart-axis, the hovered or active bar outline color-text.

configureAbsoluteUI({ components: { histogram: { dataTable: 'visible', height: 260 } } });

Changes apply to existing instances immediately.

Instance options override global and theme defaults. All options are dynamic.

Not applicable: numeric axes only; no dates are formatted.

None. No legend or toolbar is shown.

All text uses textContent; SVG is built with createElementNS. Colors are classes; sizes and the tooltip position are written through the CSSOM. No innerHTML, style attributes, or inline handlers.

Renders the given data only; no persistence, network access, or telemetry. Values are not logged; validation errors name the option, never its value.

Budget: Histogram 10,000 values create < 40 ms in headless Chromium (e2e performance test), including binning and the first render (tests/e2e/perf-r3.spec.ts). Binning is one pass for the extent plus a binary search per value; the DOM has one node per bin; the data table is built only when opened in toggle mode.

Tested with 10,000 values. Up to 500 bins (count) or 501 thresholds; the value count is not capped, and DOM size depends on the bin count only.

  • No values, or only missing values: “No data”.
  • A value equal to an inner edge falls in the upper bin; a value equal to the last edge falls in the last bin (for example thresholds [0, 10, 20]: 10 counts in 10-20, 20 counts in 10-20).
  • Values outside min/max or the thresholds are counted in outside, named in the summary, and included in the summary’s value count; they are in no bin.
  • Missing values (null, NaN, infinities) are excluded from all counts except missing.
  • All values equal: the domain is padded around the value (10% each side, or 0-1 for zero).
  • Empty bins are zero-height bars that stay focusable.
  • Counts above a fixed yAxis.max are clipped at the top of the plot.
  • formatValue applies to bin boundaries unless binFormat is set; counts are never custom-formatted.
Code Category Cause
HISTOGRAM_OPTIONS configuration Missing label; values not an array of numbers or null; bins not 'auto', an integer 1-500, or 2-501 strictly ascending finite thresholds; non-finite min/max; max not greater than min; color outside 1-8; invalid binFormat, xAxis, or yAxis.
CFG_INVALID_VALUE / CFG_UNKNOWN_OPTION configuration Invalid or unknown configuration.

A failed construction leaves the host untouched; a failed update() keeps the previous state. computeBins does not validate.

  • One data set per chart (no overlaid distributions).
  • Bins are lower-inclusive; the last bin is also upper-inclusive.
  • A bin count is approximate (nice boundaries), unless min and max fix the outer edges.
  • Only integer count ticks are printed.
  • Manual screen-reader verification is pending.
  • Density curves, cumulative histograms, and logarithmic axes.
  • Weighted values.
  • Zoom, pan, and crosshair.
  • HTML in labels or tooltips.