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.
Install
import { Hourglass } from "@microcharts/react/hourglass";<Hourglass value={0.75} title="Session" />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
Edge cases
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
| Prop | Type | Notes |
|---|---|---|
| value* | number | Elapsed fraction 0–1 (like Progress). |
| stream | boolean | The running-sand cue (default true). |
| label | "none" | "remaining" | "elapsed" | Print the percent that matters to the context. |
| fontSize | number | Type 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.