Refill Meter
Show capacity that drains with use and refills over time, such as a rate limit’s token bucket.
import { RefillMeter } from '@youj/react'
Installation & usage
npm i @youj/reactFor 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 refill meter where a client, a key or an endpoint spends from a token bucket: how much it can still send right now, and how fast that room comes back. Each cell is a token (or a share of a large bucket), lit while it's in the bucket. Requests drain cells from the edge; between the updates you give it, the bucket refills on its own at refillRate, and the edge cell charges from its foot as the next token accrues. For a quota that only resets with the period, use an Limit Meter; for usage of a resource, a Segmented Meter.
import { RefillMeter } from '@youj/react'
// What the gateway reported: 38 tokens left at `measuredAt`, refilling 10 a second.
<RefillMeter value={38} updatedAt={measuredAt} capacity={60} refillRate={10} burst={20} />Give it what you measured: value tokens at updatedAt (a Date.now() time; by default, when value last changed). It counts the refill from there, so there's no need to poll to keep it moving, and a new measurement simply resets the count. The refill stops at capacity, while the meter is off screen and in a hidden tab, and catches up when it's back.
It draws a cell a token up to 60 cells; a larger bucket shares its capacity across 60 (set segments for another count), and the charging cell fills by its share.
Description
A gateway's bucket of 60 tokens refilling 10 a second, with buttons that spend 10 requests or a burst of 40.
Two buckets: one under its burst marker, and one empty with no refill.
burst puts a marker in the bucket: the burst allowance, the most a client may send at once. While the level is under it, a full burst would be partly refused, so the lit cells turn to the warning tone and the value is spoken with "under the burst of 20". An empty bucket tints its track danger; with no refill it reads "Empty".
Code
refill-meter-demo.tsx, the Demo story. The component itself comes from the package.
Reference
Propsvalue, capacity, refillRate… (8)
Props
| Prop | Type | |
|---|---|---|
valueRequired | numberTokens in the bucket when it was measured (at | – |
capacityRequired | numberThe bucket's size: the most tokens it holds, so the largest burst it lets through at once. | – |
refillRate | numberTokens earned back each second. default 0 | 0 |
updatedAt | numberWhen | – |
burst | numberA marker at this many tokens: the burst a client should keep room for. Under it the lit cells turn to the warning tone. | – |
segments | numberHow many cells. Default: one per token, up to 60. | – |
label | ReactNodeThe visible name on the start side. Default: the labels' "Tokens". | – |
locale | stringDefault: the YoujProvider locale. | – |
Other props pass through: extends MeterPrimitive.Root.Props. | ||
StylingParts and data attributes
Styling
Parts: refill-meter, refill-meter-header, refill-meter-label, refill-meter-value, refill-meter-capacity, refill-meter-gauge, refill-meter-flags, refill-meter-burst-flag, refill-meter-track, refill-meter-cell, refill-meter-fill, refill-meter-burst, refill-meter-reserve, refill-meter-footer, refill-meter-refill, refill-meter-eta and refill-meter-term (the words in the footer). The root has data-state (ok, low or empty) and data-full; each cell has data-state (on, charging or off). The labels take the theme's label voice, and mono capitals in Bezel.
MotionRefill, spend, each cell… · reduced motion
Motion
| Moment | Tokens | ||
|---|---|---|---|
| Refillthe edge cell fills from its foot as the next token accrues, then locks in; the clock follows the time scale and stops while full, off screen or hidden | no token the refill rate | the refill rate | |
| Spendthe cells drain from the edge inward at a constant rate, the whole row spanning the deliberate token, each falling to its foot | duration.deliberate560ms | 560ms | |
| Each cellfalls or rises, and takes its tone | duration.fastease.standard140ms · cubic-bezier(0.2, 0, 0, 1) | 140mscubic-bezier(0.2, 0, 0, 1) | |
| New value mid-draincarries on from the edge on screen, so a burst of updates never jumps | duration.fastease.standard140ms · cubic-bezier(0.2, 0, 0, 1) | 140mscubic-bezier(0.2, 0, 0, 1) |
Reduced motion
The bucket still refills (it's data), a whole token at a time, with no charging cell and no drain: cells change in place.
Reduced motion
The level still refills, a whole token at a time; there's no charging cell and no drain, and cells change in place.
AccessibilityLabels and assistive technology
Accessibility
- It's a
meter(Base UI Meter) named by its visiblelabel(by default the labels' "Tokens"). - Its value is spoken as "42 of 60 tokens, refilling 10 a second", with "under the burst of 20" while under the marker and "Empty" when there's nothing left. It updates a whole token at a time, never every frame.
- The cells, the marker and the charging fill are hidden from assistive tech. The bucket fills from the inline start, so right to left in RTL; its figures keep their order.
- Its built-in text comes from the labels, so
<YoujProvider labels>translates it.