Meter
Shows a measurement within a known range, such as storage or budget used. It doesn’t show task progress.
Code
import {Grid, Meter, Stack} from '@earendil-works/elanor-react';
export default function MeterDemo() {
return (
<Grid.Root>
<Grid.Item span={{base: 'full', xSmall: 6, medium: 4}}>
<Meter.Root value={24}>
<Stack gap="xSmall" align="stretch">
<Stack direction="row" gap="auto">
<Meter.Label>Storage used</Meter.Label>
<Meter.Value />
</Stack>
<Meter.Track>
<Meter.Indicator />
</Meter.Track>
</Stack>
</Meter.Root>
</Grid.Item>
</Grid.Root>
);
} Usage
import {Meter} from '@earendil-works/elanor-react';
<Meter.Root value={24}>
<Meter.Label>Storage used</Meter.Label>
<Meter.Value />
<Meter.Track>
<Meter.Indicator />
</Meter.Track>
</Meter.Root>;
Meter adds no layout; arrange its parts with Stack. Without a visible label, name the meter with aria-label on Meter.Root.
Semantic
warning and negative flag a value near or past a limit. Choose the thresholds in your application.
Code
import {Grid, Meter, Stack} from '@earendil-works/elanor-react';
import type {MeterSemantic} from '@earendil-works/elanor-react';
const meters: ReadonlyArray<{label: string; value: number; semantic: MeterSemantic}> = [
{label: 'Neutral', value: 40, semantic: 'neutral'},
{label: 'Warning', value: 85, semantic: 'warning'},
{label: 'Negative', value: 100, semantic: 'negative'},
];
export default function MeterSemanticExample() {
return (
<Grid.Root>
<Grid.Item span={{base: 'full', xSmall: 6, medium: 4}}>
<Stack gap="large" align="stretch">
{meters.map(({label, value, semantic}) => (
<Meter.Root key={label} value={value} semantic={semantic}>
<Stack gap="xSmall" align="stretch">
<Stack direction="row" gap="auto">
<Meter.Label>{label}</Meter.Label>
<Meter.Value />
</Stack>
<Meter.Track>
<Meter.Indicator />
</Meter.Track>
</Stack>
</Meter.Root>
))}
</Stack>
</Grid.Item>
</Grid.Root>
);
} Thickness
The track is semantic.dimension.size.xxSmall (8px) tall. For a thin meter, give Meter.Track a class that sets block-size to semantic.dimension.size.xxxSmall (4px); the indicator follows.
Code
import {Grid, Meter, Stack} from '@earendil-works/elanor-react';
import styles from './meter.module.css';
export default function MeterThickness() {
return (
<Grid.Root>
<Grid.Item span={{base: 'full', xSmall: 6, medium: 4}}>
<Meter.Root value={60}>
<Stack gap="xSmall" align="stretch">
<Stack direction="row" gap="auto">
<Meter.Label>Seats used</Meter.Label>
<Meter.Value />
</Stack>
<Meter.Track className={styles.thin}>
<Meter.Indicator />
</Meter.Track>
</Stack>
</Meter.Root>
</Grid.Item>
</Grid.Root>
);
} .thin {
block-size: var(--semantic-dimension-size-xxx-small);
} Custom range
value is a percentage by default; set min and max for other ranges. format formats the value, and getAriaValueText gives screen readers the same text.
Code
import {Grid, Meter, Stack} from '@earendil-works/elanor-react';
const used = 40;
const limit = 50;
const format = {style: 'currency', currency: 'USD', maximumFractionDigits: 0} as const;
const limitText = new Intl.NumberFormat('en-US', format).format(limit);
export default function MeterCustomRange() {
return (
<Grid.Root>
<Grid.Item span={{base: 'full', xSmall: 6, medium: 4}}>
<Meter.Root
value={used}
max={limit}
format={format}
locale="en-US"
getAriaValueText={(formatted) => `${formatted} of ${limitText}`}
>
<Stack gap="xSmall" align="stretch">
<Stack direction="row" gap="auto">
<Meter.Label>Monthly budget</Meter.Label>
<Meter.Value>{(formatted) => `${formatted} of ${limitText}`}</Meter.Value>
</Stack>
<Meter.Track>
<Meter.Indicator />
</Meter.Track>
</Stack>
</Meter.Root>
</Grid.Item>
</Grid.Root>
);
} API
Meter.Root
| Prop | Type | Default | Description |
|---|---|---|---|
semantic | 'neutral' | 'warning' | 'negative' | 'neutral' | Indicator color. warning and negative flag a value near or past a limit. |
Also accepts Base UI Meter.Root props.
Meter.Label
Accepts Base UI Meter.Label props.
Meter.Value
Accepts Base UI Meter.Value props.
Meter.Track
Accepts Base UI Meter.Track props.
Meter.Indicator
Accepts Base UI Meter.Indicator props.