# Fader

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

`<audio-ui-fader>` is the root of a linear control, with audio defaults: vertical, from -60 to +6. It has `role="group"`, labelled by its label part.

## 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-fader {
    position: relative;
    height: calc(var(--spacing, 0.25rem) * 40);
  }

  audio-ui-fader-control {
    position: relative;
    display: flex;
    flex-direction: column;
    align-items: center;
    width: auto;
    height: 100%;
    touch-action: none;
    user-select: none;
  }

  audio-ui-fader-track {
    position: relative;
    flex-grow: 1;
    width: calc(var(--spacing, 0.25rem) * 2);
    overflow: hidden;
    border-radius: 9999px;
    background: color-mix(in oklab, var(--input, oklch(0.922 0 0)) 90%, transparent);
    cursor: pointer;
    user-select: none;
  }

  audio-ui-fader-indicator {
    position: absolute;
    width: 100%;
    background: var(--primary, oklch(0.205 0 0));
    user-select: none;
  }

  audio-ui-fader-thumb {
    position: absolute;
    z-index: 10;
    display: block;
    flex-shrink: 0;
    width: calc(var(--spacing, 0.25rem) * 4);
    height: calc(var(--spacing, 0.25rem) * 6);
    border: 1px solid var(--ring, oklch(0.708 0 0));
    border-radius: calc(var(--radius, 0.625rem) - 2px);
    background: var(--card, oklch(1 0 0));
    outline: none;
    cursor: grab;
    user-select: none;
    transition: box-shadow 150ms;
  }

  audio-ui-fader-thumb:hover {
    box-shadow: 0 0 0 2px color-mix(in oklab, var(--ring, oklch(0.708 0 0)) 40%, transparent);
  }

  audio-ui-fader-thumb:has(:focus-visible),
  audio-ui-fader-thumb:active {
    box-shadow: 0 0 0 3px color-mix(in oklab, var(--ring, oklch(0.708 0 0)) 50%, transparent);
  }

  audio-ui-fader-thumb:active {
    cursor: grabbing;
  }
</style>

<audio-ui-fader value="-6">
  <audio-ui-fader-control>
    <audio-ui-fader-track>
      <audio-ui-fader-indicator></audio-ui-fader-indicator>
    </audio-ui-fader-track>
    <audio-ui-fader-thumb aria-label="Volume"></audio-ui-fader-thumb>
  </audio-ui-fader-control>
</audio-ui-fader>

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

  const fader = document.querySelector("audio-ui-fader");
  fader.addEventListener("input", (event) =>
    console.log(event.detail.value)
  );
</script>
```

*With Tailwind*

```html
<audio-ui-fader class="relative h-40" value="-6">
  <audio-ui-fader-control class="relative flex h-full w-auto touch-none select-none flex-col items-center">
    <audio-ui-fader-track class="relative w-2 grow cursor-pointer select-none overflow-hidden rounded-full bg-input/90">
      <audio-ui-fader-indicator class="absolute w-full select-none bg-primary"></audio-ui-fader-indicator>
    </audio-ui-fader-track>
    <audio-ui-fader-thumb class="absolute z-10 block h-6 w-4 shrink-0 cursor-grab select-none rounded-md border border-ring bg-card outline-none transition-shadow hover:ring-2 hover:ring-ring/40 has-focus-visible:ring-3 has-focus-visible:ring-ring/50 active:cursor-grabbing active:ring-3 active:ring-ring/50" aria-label="Volume"></audio-ui-fader-thumb>
  </audio-ui-fader-control>
</audio-ui-fader>

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

  const fader = document.querySelector("audio-ui-fader");
  fader.addEventListener("input", (event) =>
    console.log(event.detail.value)
  );
</script>
```

**Vue**

*With CSS*

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

const level = ref(-6);
</script>

<template>
  <audio-ui-fader v-model="level">
    <audio-ui-fader-control>
      <audio-ui-fader-track>
        <audio-ui-fader-indicator />
      </audio-ui-fader-track>
      <audio-ui-fader-thumb aria-label="Volume" />
    </audio-ui-fader-control>
  </audio-ui-fader>
</template>

<style>
audio-ui-fader {
  position: relative;
  height: calc(var(--spacing, 0.25rem) * 40);
}

audio-ui-fader-control {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  width: auto;
  height: 100%;
  touch-action: none;
  user-select: none;
}

audio-ui-fader-track {
  position: relative;
  flex-grow: 1;
  width: calc(var(--spacing, 0.25rem) * 2);
  overflow: hidden;
  border-radius: 9999px;
  background: color-mix(in oklab, var(--input, oklch(0.922 0 0)) 90%, transparent);
  cursor: pointer;
  user-select: none;
}

audio-ui-fader-indicator {
  position: absolute;
  width: 100%;
  background: var(--primary, oklch(0.205 0 0));
  user-select: none;
}

audio-ui-fader-thumb {
  position: absolute;
  z-index: 10;
  display: block;
  flex-shrink: 0;
  width: calc(var(--spacing, 0.25rem) * 4);
  height: calc(var(--spacing, 0.25rem) * 6);
  border: 1px solid var(--ring, oklch(0.708 0 0));
  border-radius: calc(var(--radius, 0.625rem) - 2px);
  background: var(--card, oklch(1 0 0));
  outline: none;
  cursor: grab;
  user-select: none;
  transition: box-shadow 150ms;
}

audio-ui-fader-thumb:hover {
  box-shadow: 0 0 0 2px color-mix(in oklab, var(--ring, oklch(0.708 0 0)) 40%, transparent);
}

audio-ui-fader-thumb:has(:focus-visible),
audio-ui-fader-thumb:active {
  box-shadow: 0 0 0 3px color-mix(in oklab, var(--ring, oklch(0.708 0 0)) 50%, transparent);
}

audio-ui-fader-thumb:active {
  cursor: grabbing;
}
</style>
```

*With Tailwind*

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

const level = ref(-6);
</script>

<template>
  <audio-ui-fader class="relative h-40" v-model="level">
    <audio-ui-fader-control class="relative flex h-full w-auto touch-none select-none flex-col items-center">
      <audio-ui-fader-track class="relative w-2 grow cursor-pointer select-none overflow-hidden rounded-full bg-input/90">
        <audio-ui-fader-indicator class="absolute w-full select-none bg-primary" />
      </audio-ui-fader-track>
      <audio-ui-fader-thumb class="absolute z-10 block h-6 w-4 shrink-0 cursor-grab select-none rounded-md border border-ring bg-card outline-none transition-shadow hover:ring-2 hover:ring-ring/40 has-focus-visible:ring-3 has-focus-visible:ring-ring/50 active:cursor-grabbing active:ring-3 active:ring-ring/50" aria-label="Volume" />
    </audio-ui-fader-control>
  </audio-ui-fader>
</template>
```

**Svelte**

*With CSS*

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

  let level = $state(-6);
</script>

<audio-ui-fader value={level} oninput={(event) => (level = event.detail.value)}>
  <audio-ui-fader-control>
    <audio-ui-fader-track>
      <audio-ui-fader-indicator></audio-ui-fader-indicator>
    </audio-ui-fader-track>
    <audio-ui-fader-thumb aria-label="Volume"></audio-ui-fader-thumb>
  </audio-ui-fader-control>
</audio-ui-fader>

<style>
:global {
  audio-ui-fader {
    position: relative;
    height: calc(var(--spacing, 0.25rem) * 40);
  }

  audio-ui-fader-control {
    position: relative;
    display: flex;
    flex-direction: column;
    align-items: center;
    width: auto;
    height: 100%;
    touch-action: none;
    user-select: none;
  }

  audio-ui-fader-track {
    position: relative;
    flex-grow: 1;
    width: calc(var(--spacing, 0.25rem) * 2);
    overflow: hidden;
    border-radius: 9999px;
    background: color-mix(in oklab, var(--input, oklch(0.922 0 0)) 90%, transparent);
    cursor: pointer;
    user-select: none;
  }

  audio-ui-fader-indicator {
    position: absolute;
    width: 100%;
    background: var(--primary, oklch(0.205 0 0));
    user-select: none;
  }

  audio-ui-fader-thumb {
    position: absolute;
    z-index: 10;
    display: block;
    flex-shrink: 0;
    width: calc(var(--spacing, 0.25rem) * 4);
    height: calc(var(--spacing, 0.25rem) * 6);
    border: 1px solid var(--ring, oklch(0.708 0 0));
    border-radius: calc(var(--radius, 0.625rem) - 2px);
    background: var(--card, oklch(1 0 0));
    outline: none;
    cursor: grab;
    user-select: none;
    transition: box-shadow 150ms;
  }

  audio-ui-fader-thumb:hover {
    box-shadow: 0 0 0 2px color-mix(in oklab, var(--ring, oklch(0.708 0 0)) 40%, transparent);
  }

  audio-ui-fader-thumb:has(:focus-visible),
  audio-ui-fader-thumb:active {
    box-shadow: 0 0 0 3px color-mix(in oklab, var(--ring, oklch(0.708 0 0)) 50%, transparent);
  }

  audio-ui-fader-thumb:active {
    cursor: grabbing;
  }
}
</style>
```

*With Tailwind*

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

  let level = $state(-6);
</script>

<audio-ui-fader class="relative h-40" value={level} oninput={(event) => (level = event.detail.value)}>
  <audio-ui-fader-control class="relative flex h-full w-auto touch-none select-none flex-col items-center">
    <audio-ui-fader-track class="relative w-2 grow cursor-pointer select-none overflow-hidden rounded-full bg-input/90">
      <audio-ui-fader-indicator class="absolute w-full select-none bg-primary"></audio-ui-fader-indicator>
    </audio-ui-fader-track>
    <audio-ui-fader-thumb class="absolute z-10 block h-6 w-4 shrink-0 cursor-grab select-none rounded-md border border-ring bg-card outline-none transition-shadow hover:ring-2 hover:ring-ring/40 has-focus-visible:ring-3 has-focus-visible:ring-ring/50 active:cursor-grabbing active:ring-3 active:ring-ring/50" aria-label="Volume"></audio-ui-fader-thumb>
  </audio-ui-fader-control>
</audio-ui-fader>
```

**React**

In React, the same control is the [Fader](/docs/react/fader) 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/fader";
```

| Export | Kind | What it is |
| --- | --- | --- |
| `FADER_TAGS` | const | The names the fader's elements are registered under, frozen: a page cannot rename them by accident. |
| `FaderChangeEvent` | type | Fired once a gesture ends with a new value. |
| `FaderControlElement` | class | The interactive area. |
| `FaderElement` | class | The root of a linear control, with audio defaults: vertical, from -60 to +6. |
| `FaderElementEventMap` | interface | The events a fader fires, as `addEventListener` types them. |
| `FaderEventDetail` | interface | What a fader's `input` and `change` events carry, besides the fader itself as their target. |
| `FaderIndicatorElement` | class | The filled range of the track, from `min` to the value, or between `anchor` and the value. |
| `FaderInputEvent` | type | Fired on every change. |
| `FaderLabelElement` | class | The accessible label. |
| `FaderThumbElement` | class | The draggable thumb, positioned at the value. |
| `FaderTrackElement` | class | The full range of travel, containing the indicator and the thumb. |
| `FaderValueElement` | class | Renders the value, formatted with the fader's `format` and `locale`. |

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `min` | `number` | `-60` |  |
| `max` | `number` | `6` |  |
| `step` | `number` | `1` |  |
| `large-step` | `number` | `10` | Step for Page Up, Page Down and Shift + Arrow keys. |
| `value` | `number` |  | The current value. Mirrored by the `value` property. |
| `default-value` | `number` |  | The initial value, restored by a double click, Alt + click or Delete. Defaults to the midpoint. |
| `anchor` | `number` |  | The value the indicator fills from, such as `0` on a -1 to 1 pan. Defaults to `min`. |
| `orientation` | `"horizontal" \| "vertical"` | `"vertical"` |  |
| `format` | `string` |  | `Intl.NumberFormat` options as JSON, for the value part and `aria-valuetext`. |
| `locale` | `string` |  | Defaults to the browser locale. |
| `name` | `string` |  | The form field name. |
| `disabled` | `boolean` |  |  |

| Part | Description |
| --- | --- |
| `<audio-ui-fader-control>` | The interactive area. A press on the track jumps to the pointer; a press on the thumb drags from where it was grabbed; the wheel steps |
| `<audio-ui-fader-track>` | The full range of travel |
| `<audio-ui-fader-indicator>` | The filled range, from `min` or `anchor` to the value |
| `<audio-ui-fader-thumb>` | The draggable thumb, positioned at the value, containing the native range input |
| `<audio-ui-fader-value>` | The formatted value |
| `<audio-ui-fader-label>` | Labels the group, and the input when the thumb has no accessible name |

**In React**

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

