Dither Chart
A family of retro ordered-dithered micro-charts drawn on Canvas2D, where every fill is quantised through a Bayer matrix into a dot pattern.
Installation
npx smoothui-cli add dither-chartFeatures
Eight chart variants from a single component and a single data shape. Ordered dithering through a Bayer matrix — every fill becomes a dot pattern instead of a solid.
Scroll the sections below — the panel shows one chart type at a time.
Variants
Line
Dithered area under a 1px stroke, one layer per series — weekly visitors and signups.
Bar
Grouped bars, one group per point index.
Bubbles
Circles positioned by value with value-driven radius.
Donut
One ring slice per point, sweeping in on draw-in — traffic share by channel.
Funnel
Centred rows whose width follows each value — conversion funnel, top to bottom.
Gauge
270° dial, value read as a 0–100 percentage — uptime as a dial.
Heatmap
Grid of cells whose dither density follows the value — activity per slot and day.
Stacked
Stacked bars normalised against the tallest stack.
Every variant is also exported as the DITHER_CHART_VARIANTS const array, so you can map over the whole family.
import DitherChart, {
DITHER_CHART_VARIANTS,
} from "@repo/smoothui/components/dither-chart";Accessibility
ARIA Attributes
| Attribute | Element | Purpose |
|---|---|---|
aria-hidden="true" | <canvas> | The canvas is purely decorative — its pixels carry no accessible information |
sr-only text node | <span> next to the canvas | Text alternative listing the variant, every series name and every point value |
<figcaption> | Caption element | Associates the visible caption with the figure |
Screen Reader
- The chart is wrapped in a
<figure>. The canvas itself is hidden from assistive technologies and a visually hidden sibling describes the chart in full: label, variant, series names and every value. - The optional
captionis rendered as a real<figcaption>so it is announced as part of the figure. - Nothing inside the component is focusable, so it never interrupts the tab order.
Reduced Motion
This component respects prefers-reduced-motion via useReducedMotion from Motion. When reduced motion is preferred, the final frame is drawn immediately on mount — no requestAnimationFrame loop is started and no IntersectionObserver draw-in is scheduled. Setting animate={false} produces the same static result for everyone.
Props
Created by
Powered by
© 2026 SmoothUI. Built by Eduardo Calvo.