Skip to content

Meter ​

A level against a scale, read at a glance: how full it is, and where a target or a limit sits on the same scale. Loudness against a target, storage against a quota, a budget against its limit.

tsx
import { Meter } from '@adecore/ui';
tsx
import { Meter, useNow } from '@adecore/ui';
import { formatDecimal, formatPercent } from '@adecore/ui/format';

const FLOOR = -40;
const TOP = -4;
const TARGET = -14;

// A loudness that swells and dips, as a song under the playhead would.
function loudnessAt(seconds: number, speed: number): number {
    return -20 + 7 * Math.sin(seconds * speed) + 3 * Math.sin(seconds * speed * 3.7);
}

export default function MeterDemo() {
    const now = useNow(100);
    const seconds = now / 1000;

    const rows = [
        { letter: 'M', name: 'Momentary loudness', loudness: loudnessAt(seconds, 2.3) },
        { letter: 'S', name: 'Short-term loudness', loudness: loudnessAt(seconds, 0.6) }
    ];

    return (
        <div className="flex w-full max-w-xs flex-col gap-6">
            <div role="group" aria-label={`Loudness against ${TARGET} LUFS`} className="flex flex-col gap-0.5">
                {rows.map((row) => (
                    <div key={row.letter} className="flex items-center gap-1.5 text-xs">
                        <span aria-hidden className="w-2.5 text-text-faint">
                            {row.letter}
                        </span>
                        <Meter
                            value={row.loudness}
                            min={FLOOR}
                            max={TOP}
                            marks={[TARGET]}
                            label={row.name}
                            valueText={`${formatDecimal(row.loudness)} LUFS`}
                            className="flex-1"
                        />
                        <span aria-hidden className="w-8 text-right text-text-muted tabular-nums">
                            {formatDecimal(row.loudness)}
                        </span>
                    </div>
                ))}
            </div>
            <div className="flex flex-col gap-1.5 text-xs">
                <div className="flex justify-between text-text-muted">
                    <span>Storage</span>
                    <span className="tabular-nums">{formatPercent(62)}</span>
                </div>
                <Meter value={62} marks={[80]} label="Storage" valueText={`${formatPercent(62)} used`} />
            </div>
        </div>
    );
}

value is clamped between min and max. null draws an empty track, for a level not measured yet. marks draws a line across the track at each value, such as a target or a limit.

tsx
<Meter value={loudness} min={-40} max={-4} marks={[-14]} label="Momentary loudness" valueText={`${formatDecimal(loudness)} LUFS`} />

It is a thin bar with no width of its own: className gives it one, or a flex row stretches it. It draws only the bar; you lay out the name and value. The fill has no transition, so fast updates follow without lag.

A screen reader hears a meter named by label, with valueText as the value ("-18.2 LUFS", "Measuring"), or else the number.

Props ​

PropTypeDefault
valuenumber | nullRequired. null draws an empty track.
labelstringRequired. The name a screen reader reads.
minnumber0
maxnumber100
marksreadonly number[][]Values drawn as lines across the track.
valueTextstringThe value in words for a screen reader.
classNamestringIts width.
refRef<HTMLDivElement>

MeterProps is an exported type.

Licenses and third-party notices are listed in each package.