Skip to content

Meter

Shows a measurement within a known range, such as storage or budget used. It doesn’t show task progress.

Storage used
x
Code
demo.tsx
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.

Neutral
x
Warning
x
Negative
x
Code
semantic.tsx
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.

Seats used
x
Code
thickness.tsx
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>
  );
}
meter.module.css
.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.

Monthly budget
x
Code
custom-range.tsx
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

PropTypeDefaultDescription
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.