Skip to content
microcharts
ReferenceExpressiveHourglass

Hourglass

Hourglass splits a countdown into elapsed and remaining sand, with both chamber areas true to the fraction.

encodes
sand area split top (remaining) / bottom (elapsed)
precision
medium
nodes
4

A session expires in nine minutes, and you need both halves of that: how much is gone and how much is left. Hourglass shows both. Sand fills the top chamber for remaining and the bottom for elapsed, and both areas are area-true: a linear-height fill in a triangular bulb would overstate early progress by up to 2×, so the geometry solves for true proportional area instead. value is the elapsed fraction, the same polarity as Progress, so the two compose in one product without re-learning which way is which. A thin stream at the neck renders while 0 < value < 1 and marks the running state; it disappears at both ends, so finished and not-started are shape-distinct.

Hourglass
interactive · 3.78 kB · static · 2.19 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

Use it 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. For exact percentages use Progress, for trends use Sparkline, and for fractions that aren't time use a chart that doesn't imply a clock.

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)

Out-of-range values clamp to 0–1 in the drawing and in the accessible name, and NaN renders as 0 elapsed. Both ends drop the neck stream, so a not-started or finished glyph never reads as a running timer. In the static entry the stream is a state mark and is never animated.

Four homes

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, with opacity plus a scale-from-the-floor settle rather than a path interpolation. It announces only when the value crosses 50, 90, or 100%, so a streaming value never floods a screen reader.

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. Hover or focus also reveals the reading itself in a floating chip, for the sizes and label modes where the mark does not print it; readout={false} drops the chip and keeps everything else.

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.
fontSizenumberType size of the percent numeral, in viewBox units (default 8).

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