# Meter

> Package `@umriss-ui/core`, version 0.40.0. Demo page: <https://romanhaendler.github.io/umriss-ui/core/meter/>. Every page in one line: [llms.txt](https://romanhaendler.github.io/umriss-ui/core/llms.txt); every page in full: [llms-full.txt](https://romanhaendler.github.io/umriss-ui/core/llms-full.txt).

A bar that shows a measured share, such as capacity booked or budget used, coloured by your verdict on it (also called a gauge bar or level indicator). Reach for it in lists and tables where a percentage alone is hard to compare.

```ts
import { Meter } from "@umriss-ui/core";
```

Demo page: https://romanhaendler.github.io/umriss-ui/core/meter/

`value` runs from 0 to 1 and is clamped outside; say the true figure beside the bar where it can pass 100 %.

The `tone` is your assessment, since only you know whether 90 % is good or bad; the bar never colours itself by height. Keep the number or a word beside it so the colour is never alone.

The role `meter` needs a name from you: `label` says what is measured, and neither the percentage nor a column heading counts as one.

## Examples

### A share

A `value` from 0 to 1, a `label` naming what is measured, and `showLabel` for the percentage beside the bar.

```tsx
import { Meter } from "@umriss-ui/core";

export default function AShare() {
  return (
    <div style={{ maxWidth: 320 }}>
      <Meter value={0.8} label="Capacity booked, Arjun Mehta" showLabel />
    </div>
  );
}
```

### Tones

The `tone` is your verdict on the value, not its height; the figure beside it says the same for whoever cannot see colour.

```tsx
import { Grid, Meter, Stack, Text } from "@umriss-ui/core";

const CENTRES = [
  { name: "Sales", used: 0.62, tone: "neutral", note: "62 % of March's budget" },
  { name: "IT", used: 0.91, tone: "warning", note: "91 % – with a third of March left" },
  { name: "Marketing", used: 1.07, tone: "danger", note: "107 % – over budget" },
] as const;

export default function Tones() {
  return (
    <Grid minItemWidth="220px" gap={4}>
      {CENTRES.map((centre) => (
        <Stack key={centre.name} gap={2}>
          <Text size="sm" weight="medium">
            {centre.name}
          </Text>
          <Meter value={centre.used} tone={centre.tone} label={`Budget used, ${centre.name}`} />
          <Text size="xs" tone="muted">
            {centre.note}
          </Text>
        </Stack>
      ))}
    </Grid>
  );
}
```

### Compare a team's capacity

In a table, one rule turns each share into a tone, and the hours beside the bar carry the value in words.

```tsx
import { Meter, Text } from "@umriss-ui/core";
import type { MeterTone } from "@umriss-ui/core";

/** The rule: fully booked is fine, over 100 % warns, over 120 % is a problem. */
function toneOf(share: number): MeterTone {
  if (share > 1.2) return "danger";
  if (share > 1) return "warning";
  return "neutral";
}

/** This week, 16 to 20 March: hours booked against hours available after leave. */
const ROWS = [
  { name: "Arjun Mehta", booked: 26, available: 16 },
  { name: "Chloe Durand", booked: 38, available: 40 },
  { name: "Noah Fischer", booked: 22, available: 24 },
  { name: "Hana Sato", booked: 44, available: 40 },
  { name: "Kofi Mensah", booked: 20, available: 32 },
  { name: "Freya Olsen", booked: 12, available: 24 },
].map((row) => ({ ...row, share: row.booked / row.available }));

const CELL = { padding: "var(--u-space-2) var(--u-space-3)", borderBottom: "1px solid var(--u-edge-color)" };

export default function TeamCapacity() {
  return (
    <table style={{ borderCollapse: "collapse", width: "100%", maxWidth: 560 }}>
      <thead>
        <tr>
          <th style={{ ...CELL, textAlign: "left" }}>
            <Text as="span" size="xs" tone="muted">Person</Text>
          </th>
          <th style={{ ...CELL, textAlign: "left", width: "45%" }}>
            <Text as="span" size="xs" tone="muted">Booked this week</Text>
          </th>
          <th style={{ ...CELL, textAlign: "right" }}>
            <Text as="span" size="xs" tone="muted">Hours</Text>
          </th>
        </tr>
      </thead>
      <tbody>
        {ROWS.map(({ name, booked, available, share }) => (
          <tr key={name}>
            <td style={CELL}>
              <Text as="span" size="sm">{name}</Text>
            </td>
            <td style={CELL}>
              <Meter value={share} tone={toneOf(share)} showLabel label={`Capacity booked, ${name}`} />
            </td>
            <td style={{ ...CELL, textAlign: "right", whiteSpace: "nowrap" }}>
              <Text as="span" size="sm" mono>
                {booked} / {available}
              </Text>
            </td>
          </tr>
        ))}
      </tbody>
    </table>
  );
}
```

## When to use something else

- How far a job has come towards its end → ProgressBar
- One figure read against limits, with its history → Stat

## Accessibility

The role `meter`, from 0 to 100, with the whole percentage as its value. It is named by `label`, or by your `aria-labelledby`; without either it is named "Fill level", which says nothing about what is measured. The tone is not read out: say the verdict in a word beside the bar.

It is no live region: a new value is read when a reader reaches the bar. Under forced colours the track takes an edge and the fill the system's selection colour, whatever its tone.

## API

### `MeterProps`

| Prop | Type | Default | Description |
|---|---|---|---|
| `value` *required* | `number` | — | Fill level from 0 to 1.<br>Shown in: [Configurator](https://romanhaendler.github.io/umriss-ui/core/meter/#configurator-meter), [A share](https://romanhaendler.github.io/umriss-ui/core/meter/#a-share), [Tones](https://romanhaendler.github.io/umriss-ui/core/meter/#tones) and 3 more |
| `tone` | [`MeterTone`](https://romanhaendler.github.io/umriss-ui/core/meter/#type-MeterTone)<br>`"neutral" \| "accent" \| "success" \| "warning" \| "danger"` | `"accent"` | The colour of the bar. It comes from an assessment and not from taste: beside the bar there is always the number or a word, so that the colour is never the only statement.<br>Shown in: [Configurator](https://romanhaendler.github.io/umriss-ui/core/meter/#configurator-meter), [Tones](https://romanhaendler.github.io/umriss-ui/core/meter/#tones), [Compare a team's capacity](https://romanhaendler.github.io/umriss-ui/core/meter/#team-capacity) and 2 more |
| `showLabel` | `boolean` | `true` | The percentage to the right of the bar (Geist Mono).<br>Shown in: [Configurator](https://romanhaendler.github.io/umriss-ui/core/meter/#configurator-meter), [A share](https://romanhaendler.github.io/umriss-ui/core/meter/#a-share), [Compare a team's capacity](https://romanhaendler.github.io/umriss-ui/core/meter/#team-capacity) and 2 more |
| `label` | `string` | the wording's "Fill level" | What is being measured, e.g. "Utilisation". A role of `meter` needs a name from the author - the visible percentage inside it does not count for that, and neither does the column heading beside it. Whoever knows what the bar measures says it better than the general term.<br>Shown in: [A share](https://romanhaendler.github.io/umriss-ui/core/meter/#a-share), [Tones](https://romanhaendler.github.io/umriss-ui/core/meter/#tones), [Compare a team's capacity](https://romanhaendler.github.io/umriss-ui/core/meter/#team-capacity) and 2 more |
| `ref` | `Ref<HTMLSpanElement>` | — | Receives the component's root element. From `RefProp`.<br>Shown in: [A row that holds still (Sizes)](https://romanhaendler.github.io/umriss-ui/core/sizes/#a-row-that-holds-still), [An anchored surface (Popover)](https://romanhaendler.github.io/umriss-ui/core/popover/#anchored-surface), [Take the width of the trigger (Popover)](https://romanhaendler.github.io/umriss-ui/core/popover/#width-from-anchor) and 2 more |

Also takes every attribute of `<span>`.

### Types on this page

#### `MeterTone`

The colour of a meter's bar - an assessment, never the only statement.

```ts
type MeterTone = "neutral" | "accent" | "success" | "warning" | "danger";
```

## Known limits

- No limit marks, target tick or scale on the bar; for a value against limits use a [Stat](https://romanhaendler.github.io/umriss-ui/core/stat/).

What umriss deliberately does not build, and why: [ADR-0032](https://romanhaendler.github.io/umriss-ui/what-umriss-ui-is-not/).
