# Meter

> The <audio-ui-meter> custom element of @audio-ui/elements: its attributes, its parts and a live preview.

`<audio-ui-meter>` displays one or more level channels. Levels come from the `value` and `peak` attributes or properties, one per channel, or from a live `source` property, `{ getSnapshot, subscribe }`, whose frames are written directly to the parts. A click clears clip indicators latched by a live source.

## Usage

{/* usage:start: written by `bun scripts/element-usage.ts --write` */}

The styles read the theme's CSS variables, which [Styling](/docs/elements#styling) sets.

**JavaScript**

*With CSS*

```html
<style>
  audio-ui-meter {
    display: flex;
    gap: calc(var(--spacing, 0.25rem) * 1.5 * var(--density, 1));
    height: calc(var(--spacing, 0.25rem) * 44);
  }

  audio-ui-meter-channel {
    position: relative;
    display: block;
    width: calc(var(--spacing, 0.25rem) * 3);
    height: 100%;
    overflow: hidden;
    border-radius: calc(var(--radius, 0.625rem) - 4px);
    background: var(--muted, oklch(0.97 0 0));
  }

  audio-ui-meter-indicator {
    position: absolute;
    bottom: 0;
    display: block;
    background: var(--primary, oklch(0.205 0 0));
  }

  audio-ui-meter-peak {
    position: absolute;
    right: 0;
    left: 0;
    display: block;
    height: calc(var(--spacing, 0.25rem) * 0.5);
    background: var(--foreground, oklch(0.145 0 0));
  }
</style>

<audio-ui-meter min="-60" max="6" value="-18 -10">
  <audio-ui-meter-channel index="0" aria-label="Left">
    <audio-ui-meter-indicator></audio-ui-meter-indicator>
    <audio-ui-meter-peak></audio-ui-meter-peak>
  </audio-ui-meter-channel>
  <audio-ui-meter-channel index="1" aria-label="Right">
    <audio-ui-meter-indicator></audio-ui-meter-indicator>
    <audio-ui-meter-peak></audio-ui-meter-peak>
  </audio-ui-meter-channel>
</audio-ui-meter>

<script type="module">
  import "@audio-ui/elements/meter";

  const meter = document.querySelector("audio-ui-meter");
  meter.value = "-12 -6";
</script>
```

*With Tailwind*

```html
<audio-ui-meter class="flex h-44 gap-[calc(var(--spacing)*1.5*var(--density,1))]" min="-60" max="6" value="-18 -10">
  <audio-ui-meter-channel class="relative block h-full w-3 overflow-hidden rounded-sm bg-muted" index="0" aria-label="Left">
    <audio-ui-meter-indicator class="absolute bottom-0 block bg-primary"></audio-ui-meter-indicator>
    <audio-ui-meter-peak class="absolute inset-x-0 block h-0.5 bg-foreground"></audio-ui-meter-peak>
  </audio-ui-meter-channel>
  <audio-ui-meter-channel class="relative block h-full w-3 overflow-hidden rounded-sm bg-muted" index="1" aria-label="Right">
    <audio-ui-meter-indicator class="absolute bottom-0 block bg-primary"></audio-ui-meter-indicator>
    <audio-ui-meter-peak class="absolute inset-x-0 block h-0.5 bg-foreground"></audio-ui-meter-peak>
  </audio-ui-meter-channel>
</audio-ui-meter>

<script type="module">
  import "@audio-ui/elements/meter";

  const meter = document.querySelector("audio-ui-meter");
  meter.value = "-12 -6";
</script>
```

**Vue**

*With CSS*

```vue
<script setup lang="ts">
import "@audio-ui/elements/meter";
import { ref } from "vue";

const levels = ref("-18 -10");
</script>

<template>
  <audio-ui-meter min="-60" max="6" :value="levels">
    <audio-ui-meter-channel index="0" aria-label="Left">
      <audio-ui-meter-indicator />
      <audio-ui-meter-peak />
    </audio-ui-meter-channel>
    <audio-ui-meter-channel index="1" aria-label="Right">
      <audio-ui-meter-indicator />
      <audio-ui-meter-peak />
    </audio-ui-meter-channel>
  </audio-ui-meter>
</template>

<style>
audio-ui-meter {
  display: flex;
  gap: calc(var(--spacing, 0.25rem) * 1.5 * var(--density, 1));
  height: calc(var(--spacing, 0.25rem) * 44);
}

audio-ui-meter-channel {
  position: relative;
  display: block;
  width: calc(var(--spacing, 0.25rem) * 3);
  height: 100%;
  overflow: hidden;
  border-radius: calc(var(--radius, 0.625rem) - 4px);
  background: var(--muted, oklch(0.97 0 0));
}

audio-ui-meter-indicator {
  position: absolute;
  bottom: 0;
  display: block;
  background: var(--primary, oklch(0.205 0 0));
}

audio-ui-meter-peak {
  position: absolute;
  right: 0;
  left: 0;
  display: block;
  height: calc(var(--spacing, 0.25rem) * 0.5);
  background: var(--foreground, oklch(0.145 0 0));
}
</style>
```

*With Tailwind*

```vue
<script setup lang="ts">
import "@audio-ui/elements/meter";
import { ref } from "vue";

const levels = ref("-18 -10");
</script>

<template>
  <audio-ui-meter class="flex h-44 gap-[calc(var(--spacing)*1.5*var(--density,1))]" min="-60" max="6" :value="levels">
    <audio-ui-meter-channel class="relative block h-full w-3 overflow-hidden rounded-sm bg-muted" index="0" aria-label="Left">
      <audio-ui-meter-indicator class="absolute bottom-0 block bg-primary" />
      <audio-ui-meter-peak class="absolute inset-x-0 block h-0.5 bg-foreground" />
    </audio-ui-meter-channel>
    <audio-ui-meter-channel class="relative block h-full w-3 overflow-hidden rounded-sm bg-muted" index="1" aria-label="Right">
      <audio-ui-meter-indicator class="absolute bottom-0 block bg-primary" />
      <audio-ui-meter-peak class="absolute inset-x-0 block h-0.5 bg-foreground" />
    </audio-ui-meter-channel>
  </audio-ui-meter>
</template>
```

**Svelte**

*With CSS*

```svelte
<script lang="ts">
  import "@audio-ui/elements/meter";

  let levels = $state("-18 -10");
</script>

<audio-ui-meter min="-60" max="6" value={levels}>
  <audio-ui-meter-channel index="0" aria-label="Left">
    <audio-ui-meter-indicator></audio-ui-meter-indicator>
    <audio-ui-meter-peak></audio-ui-meter-peak>
  </audio-ui-meter-channel>
  <audio-ui-meter-channel index="1" aria-label="Right">
    <audio-ui-meter-indicator></audio-ui-meter-indicator>
    <audio-ui-meter-peak></audio-ui-meter-peak>
  </audio-ui-meter-channel>
</audio-ui-meter>

<style>
:global {
  audio-ui-meter {
    display: flex;
    gap: calc(var(--spacing, 0.25rem) * 1.5 * var(--density, 1));
    height: calc(var(--spacing, 0.25rem) * 44);
  }

  audio-ui-meter-channel {
    position: relative;
    display: block;
    width: calc(var(--spacing, 0.25rem) * 3);
    height: 100%;
    overflow: hidden;
    border-radius: calc(var(--radius, 0.625rem) - 4px);
    background: var(--muted, oklch(0.97 0 0));
  }

  audio-ui-meter-indicator {
    position: absolute;
    bottom: 0;
    display: block;
    background: var(--primary, oklch(0.205 0 0));
  }

  audio-ui-meter-peak {
    position: absolute;
    right: 0;
    left: 0;
    display: block;
    height: calc(var(--spacing, 0.25rem) * 0.5);
    background: var(--foreground, oklch(0.145 0 0));
  }
}
</style>
```

*With Tailwind*

```svelte
<script lang="ts">
  import "@audio-ui/elements/meter";

  let levels = $state("-18 -10");
</script>

<audio-ui-meter class="flex h-44 gap-[calc(var(--spacing)*1.5*var(--density,1))]" min="-60" max="6" value={levels}>
  <audio-ui-meter-channel class="relative block h-full w-3 overflow-hidden rounded-sm bg-muted" index="0" aria-label="Left">
    <audio-ui-meter-indicator class="absolute bottom-0 block bg-primary"></audio-ui-meter-indicator>
    <audio-ui-meter-peak class="absolute inset-x-0 block h-0.5 bg-foreground"></audio-ui-meter-peak>
  </audio-ui-meter-channel>
  <audio-ui-meter-channel class="relative block h-full w-3 overflow-hidden rounded-sm bg-muted" index="1" aria-label="Right">
    <audio-ui-meter-indicator class="absolute bottom-0 block bg-primary"></audio-ui-meter-indicator>
    <audio-ui-meter-peak class="absolute inset-x-0 block h-0.5 bg-foreground"></audio-ui-meter-peak>
  </audio-ui-meter-channel>
</audio-ui-meter>
```

**React**

In React, the same control is the [Meter](/docs/react/meter) primitive of `@audio-ui/react`, with the same parts as React components and its value as props.

{/* usage:end */}

## API reference

```bash
npm install @audio-ui/elements
```

```ts
import "@audio-ui/elements/meter";
```

| Export | Kind | What it is |
| --- | --- | --- |
| `METER_TAGS` | const | The names the meter's elements are registered under, frozen: a page cannot rename them by accident. |
| `MeterChannelElement` | class | A channel with `role="meter"`, for its `index`, labelled by `aria-label` or an `<audio-ui-meter-label>`. |
| `MeterChannelsElement` | class | Generates an `<audio-ui-meter-channel>` per level from its `<template>`, regenerated when the channel count changes. |
| `MeterElement` | class | The root of a level meter, with `role="group"`. |
| `MeterIndicatorElement` | class | The lit range of a channel, scaled to the level along the meter's length. |
| `MeterLabelElement` | class | The accessible label of its channel. |
| `MeterMarkElement` | class | A scale graduation at `value`, such as -18 dB, placed along the meter's length from its minimum. |
| `MeterPeakElement` | class | The peak-hold marker at the channel's peak level. |
| `MeterSnapshot` | interface | What the meter's parts draw from. |
| `MeterSource` | interface | A live signal, in the shape of an external store: `subscribe` calls back on each new frame, `getSnapshot` returns the latest. |
| `MeterTrackElement` | class | The full range of a channel, behind the indicator. |
| `MeterValueElement` | class | Renders the channel's level, formatted with the meter's `format` and `locale`. |

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `value` | `string` |  | One level per channel, such as `"-12 -6"`. The count sets the number of channels. |
| `peak` | `string` |  | One peak-hold level per channel. Hold time and decay are managed outside the meter. |
| `min` | `number` | `-60` |  |
| `max` | `number` | `6` |  |
| `scale` | `"linear" \| "iec"` | `"linear"` | How levels map onto the length. The `scale` property also accepts a custom increasing curve. |
| `clip-threshold` | `number` | `0` | Level at or above which a channel is clipping. |
| `warn-threshold` | `number` |  | Level at or above which a channel enters its warning zone. None by default. |
| `orientation` | `"horizontal" \| "vertical"` | `"vertical"` |  |
| `unit` | `string` |  | Appended to each channel's `aria-valuetext`, such as `dB`. The `getAriaValueText` property overrides the text. |
| `format` | `string` |  | `Intl.NumberFormat` options as JSON. One decimal by default. |
| `locale` | `string` |  | Defaults to the browser locale. |

| Part | Description |
| --- | --- |
| `<audio-ui-meter-channels>` | Generates an `<audio-ui-meter-channel>` per level from its `<template>`. `label` names each channel, `{n}` being its number (`Output {n}`). Renders with `display: contents` |
| `<audio-ui-meter-channel>` | A channel with `role="meter"`, for its `index`. Carries `data-warning`, `data-clipping`, and `data-clipped` until cleared |
| `<audio-ui-meter-track>` | The full range of a channel |
| `<audio-ui-meter-indicator>` | The lit range, scaled to the level |
| `<audio-ui-meter-peak>` | The peak-hold marker, hidden when the channel has none |
| `<audio-ui-meter-value>` | The formatted level |
| `<audio-ui-meter-label>` | Labels its channel |
| `<audio-ui-meter-mark>` | A scale graduation at its `value`, such as -18 |

**In React**

The same control in React, and its parts' props: [Meter](/docs/react/meter).

