Skip to content

Density Lanes

Show when things happen, one lane of dots per kind, swelling where it's busy, on one scale with a highlight and a marker.

import { DensityLane, DensityLanes, DensityLanesTick } from '@youj/react'

Installation & usage

npm i @youj/react

For an app already set up with npx @youj/cli init, which installs the peers (@youj/core, @youj/motion, motion and Base UI) and wires the provider. New app? Start at Installation.

Use density lanes for "when" across a few kinds of thing: alerts by hour per source, orders by half-hour per channel, commits by weekday per team. Each lane's shape tells when it's busy; the shares under the names tell how much of the whole it is. For one series of counts use a Unit Chart; for a year of days, a Calendar Heatmap. The Hour lanes card is built on it.

import { DensityLanes } from '@youj/react'

<DensityLanes
  label="Alerts by hour"
  columns={hours}
  lanes={[
    { id: 'identity', label: 'Identity', values: identityByHour, tone: 'chart-3' },
    { id: 'network', label: 'Network', values: networkByHour, tone: 'chart-2' },
  ]}
  highlight={[[0, 6], [19, 23]]}
  marker={{ column: 12, label: 'Peak 12:00 · 504 alerts' }}
/>

columns names each column (in its tooltip and spoken), and every lane gives one value per column. A column swells depth dots (4 by default) above and below the lane's spine at max (by default the largest value, one scale for every lane), and a column with nothing draws no dots, only the faint grid. ticks places labels at column edges (at 0 is the start of the first column), by default one at each quarter. Lanes take the chart colors in order unless they set a tone; shares={false} drops the shares under their names; formatValue writes values with their unit. No lanes, or nothing in them, shows the empty state.

Reference

Propslanes, columns, depth, max, highlight, marker, ticks…

Props

DensityLanes props
PropType
lanesRequiredreadonly DensityLane[]–
columnsRequiredreadonly string[]

Names each column, in its tooltip and spoken: "12:30".

–
depthnumber

How many dots a column can swell above and below the spine.

default 4

4
maxDotnumber

The largest a dot grows, in px from one dot to the next. Past it a wide plot stops growing taller and its columns spread apart.

default 16

16
maxnumber

The value at full swell, one scale for every lane. Default: the largest value.

–
highlightreadonly (readonly [number, number])[]

Column ranges, first and last index, kept in each lane's tone; the rest go quiet.

–
marker{ column: number; label: string }

A dashed rule through every lane at one column, with a flag above it.

–
ticksreadonly DensityLanesTick[]

Labels under the plot at column edges. Default: a label at each quarter, from columns.

–
sharesboolean

Each lane's share of the total under its name.

default true

true
formatValue(value: number) => string

Formats values in the tooltip and the spoken text. Default: the locale's number format.

–
localestring

Default: the YoujProvider locale.

–
labelstring

Accessible name of the chart. Default: the labels' "Density lanes".

–
configReadonly<Record<string, { color?: string }>>

A ChartConfig shared with your other charts: a lane whose id is a key takes its color, so one series matches across chart types.

–
Other props pass through: extends ComponentProps<'div'>.
StylingParts and data attributes

Styling

Parts: density-lanes, density-lanes-grid, density-lanes-lane, density-lanes-label, density-lanes-plot, density-lanes-column, density-lanes-cells, density-lanes-cell, density-lanes-band, density-lanes-flag-row, density-lanes-flag, density-lanes-marker, density-lanes-axis, density-lanes-tick, density-lanes-tooltip, density-lanes-summary and density-lanes-empty. A column in a highlight has data-highlighted. A lane's color is --density-lanes-tone on its plot, and an empty dot's is --density-lanes-off.

MotionReveal, highlight · reduced motion

Motion

Motion tokens
MomentTokens
Revealthe dots light column by column from the start when a third of the chart scrolls into view
duration.deliberateease.standard

560ms · cubic-bezier(0.2, 0, 0, 1)

560mscubic-bezier(0.2, 0, 0, 1)
Highlightcolumns leaving or entering a highlight recolor between the tone and the quiet gray
duration.fastease.standard

140ms · cubic-bezier(0.2, 0, 0, 1)

140mscubic-bezier(0.2, 0, 0, 1)

Reduced motion

Drawn in place; changes land at once.

Reduced motion

The dots are drawn in place, and a new highlight lands at once.

AccessibilityKeyboard, focus, screen readers

Accessibility

  • A named grid (your label, or the labels' "Density lanes") described by a summary: how many lanes, their total, and the busiest column with its value.
  • A row per lane, its header the lane's name and share; every cell is named with its column and value, and says when it is in the highlight ("12:00: 504 alerts, highlighted").
  • One tab stop. Left and Right walk the columns (mirrored in RTL), Up and Down the lanes, Home and End go to a lane's ends. Focus shows the cell's tooltip and marks its column through every lane, as hovering does.
  • The dots, flag and axis are decorative; the cells say the same in words. Color is never the only cue: lanes are named, and a highlight is spoken.
  • Its built-in text comes from the labels, so <YoujProvider labels> translates it.