Skip to content
microcharts
ReferenceExpressiveHourglass

Hourglass

React hourglass chart — sand area that splits elapsed and remaining, area-true. Tiny accessible SVG microchart; RSC-safe static or /interactive.

Hourglass answers "how much time is gone and how much remains?" — the two-sided story a progress bar can't tell. Sand fills the top chamber (remaining) and the bottom (elapsed), and both areas are area-true: a naive linear-height fill in a triangular bulb would overstate early progress by up to 2×, so the geometry solves for true proportional area. A thin stream at the neck marks "running"; it disappears at both ends, because finished and not-started are shape-distinct states.

Hourglass
interactive · 3.18 kB · static · 1.83 kB

Install

Import & usereact/hourglass
import { Hourglass } from "@microcharts/react/hourglass";<Hourglass value={0.75} title="Session" />
Needs package + stylesheetSet up with AI

Try it

When to use it

  • Good for — a deadline or session-expiry read in a sentence, a TTL cell where remaining is the story, or a time-boxed tab or countdown.
  • Avoid for — exact percentages (Progress), trends (Sparkline), or non-time fractions.

Sizing

Variants

labelled remaining + finished

Edge cases

zero — not started, no stream
finished — no stream, all sand settled
out-of-range clamps to 0–1 — visually and in the accessible name
NaN — treated as 0 elapsed (documented)

Four homes

Why this default

value is the elapsed fraction — the same polarity as Progress — so the two compose in one product without re-learning which way is which. The area-true solve is the load-bearing honesty rule: in an inherently nonlinear container, the sand areas are exactly proportional to elapsed and remaining. The stream is a binary state mark, not decoration, and is never animated in the static entry — it simply renders while 0 < value < 1.

Accessibility

The accessible name carries both sides — a glyph above reads "70% elapsed, 30% remaining." The interactive entry cross-fades the sand levels on change (opacity plus a scale-from-the-floor settle, never a path interpolation) and announces only when the value crosses a documented threshold (50 / 90 / 100%), so a streaming value never spams.

This chart is a single unit, so there is nothing to rove between: a click, tap, Enter or Space selects it and fires onSelect, and no selection stays pinned. That is the scalar half of the shared interaction contract.

Props

PropTypeNotes
value*numberElapsed fraction 0–1 (like Progress).
streambooleanThe running-sand cue (default true).
label"none" | "remaining" | "elapsed"Print the percent that matters to the context.

Plus the shared grammar — data, domain, color, title, summary, format — and the layout props (width, height, className, style) that every chart accepts. Interactive entries also share animate and live, and — wherever a chart has more than one navigable unit — onActive, onSelect, selectedIndex and defaultSelectedIndex; and — wherever the chart shows a hover value — readout. See the shared grammar.

Related charts