Skip to content

Waffle Chart

Show a whole as countable cells colored by part, square or hexagonal, with a legend that lights each part.

import { WaffleChart, WaffleChartItem } 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 a waffle chart when a whole splits into a few parts and the count matters as much as the share: waste by source, seats by plan, storage by type. Each cell is one unit, colored by its part, so shares read as areas you could count. For many parts, or parts that differ by orders of magnitude, use a Treemap; for one share in a row, a Share Bar.

import { WaffleChart } from '@youj/react'

<WaffleChart
  label="Waste by source"
  items={[
    { id: 'trim', name: 'Produce trim', value: 22 },
    { id: 'plates', name: 'Plate returns', value: 17 },
    { id: 'bread', name: 'Bread', value: 11 },
  ]}
  formatValue={(v) => `${v} kg`}
/>

unit sets how much one cell counts for, and each part rounds to whole cells. layout="rows" (the default) fills from the top-start corner row by row; layout="cluster" gathers the cells into a round patch at the center, each part a wedge, with empty cells around it. shape="hex" draws hexagons. The grid is columns wide (20 by default) and as tall as it needs, or rows tall; it grows when the cells don't fit. Parts take the chart colors in order unless they set a tone. Say what a cell is next to the chart.

Reference

Propsitems, unit, columns, layout… (9)

Props

WaffleChart props
PropType
itemsRequiredreadonly WaffleChartItem[]–
unitnumber

How much one cell counts for.

default 1

1
columnsnumber

Cells across.

default 20

20
rowsnumber

Cells down. Default: enough to hold every filled cell, with room around a cluster.

–
shape'square' | 'hex'

default 'square'

'square'
layout'rows' | 'cluster'

rows fills from the top-start corner, row by row; cluster gathers the cells at the center, each part a wedge.

default 'rows'

'rows'
formatValue(value: number) => string

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

–
labelstring

Accessible name of the chart. Default: the labels' "Waffle chart".

–
legendboolean

Show the legend under the cells.

default true

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

A ChartConfig shared with your other charts: an item 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: waffle-chart, waffle-chart-cells, waffle-chart-cell, waffle-chart-legend, waffle-chart-legend-item, waffle-chart-swatch, waffle-chart-legend-name, waffle-chart-legend-share, waffle-chart-summary and waffle-chart-empty. Each cell has data-state (on or off) and, when filled, data-tone; a legend item has data-tone, and data-active while its part is lit. --waffle-chart-off on the root is the empty cells' color.

MotionReveal, new data, lighting a part · reduced motion

Motion

Motion tokens
MomentTokens
Revealthe cells fill in order, along the rows or outward from the center for a cluster, 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)
New datacells that change part recolor in the same order
duration.fastease.standard

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

140mscubic-bezier(0.2, 0, 0, 1)
Lighting a partthe other parts fade back at once, without waiting
duration.fastease.standard

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

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

Reduced motion

Drawn in place; new data lands at once.

Reduced motion

The cells are drawn in place, and new data lands at once.

AccessibilityKeyboard, focus

Accessibility

  • A named group (your label, or the labels' "Waffle chart"), described by a summary: how many parts, their total and the largest.
  • The legend is a list; each part is named with its value and share ("Bread: 11 kg, 15% of total").
  • The legend is one tab stop. The arrow keys walk the parts, Home and End go to the first and last, and Escape lets go. Focusing a part lights its cells, as hovering it or one of its cells does.
  • The cells are decorative; everything they show is in the legend and the summary. Color is never the only cue: each part's name and share are written beside its swatch.
  • Its built-in text comes from the labels, so <YoujProvider labels> translates it.