# Timeline

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

`<audio-ui-timeline>` is the root of an arrangement: a ruler that sets the position, lanes of clips that move, trim and fade, loop ranges, and a viewport that scrolls and zooms. Units are arbitrary: seconds, beats or steps.

## 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-timeline {
    display: block;
    width: 100%;
    max-width: 36rem;
  }

  audio-ui-timeline-viewport {
    border: 1px solid var(--border, oklch(0.922 0 0));
    border-radius: calc(var(--radius, 0.625rem) - 2px);
  }

  audio-ui-timeline-ruler {
    height: calc(var(--spacing, 0.25rem) * 6);
    margin-inline-start: calc(var(--spacing, 0.25rem) * 20);
    background: var(--muted, oklch(0.97 0 0));
    cursor: pointer;
  }

  audio-ui-timeline-mark {
    height: 100%;
    padding-inline-start: calc(var(--spacing, 0.25rem) * 1);
    border-inline-start: 1px solid var(--border, oklch(0.922 0 0));
    color: var(--muted-foreground, oklch(0.556 0 0));
    font-size: 10px;
  }

  audio-ui-timeline-range {
    top: 0;
    bottom: 0;
    background: color-mix(in oklab, var(--primary, oklch(0.205 0 0)) 15%, transparent);
  }

  audio-ui-timeline-track {
    height: calc(var(--spacing, 0.25rem) * 14);
    border-top: 1px solid var(--border, oklch(0.922 0 0));
  }

  audio-ui-timeline-track-header {
    width: calc(var(--spacing, 0.25rem) * 20);
    padding: calc(var(--spacing, 0.25rem) * 2);
    border-inline-end: 1px solid var(--border, oklch(0.922 0 0));
    background: var(--background, oklch(1 0 0));
    font-size: 0.75rem;
    line-height: 1rem;
  }

  audio-ui-timeline-clip {
    top: calc(var(--spacing, 0.25rem) * 1);
    bottom: calc(var(--spacing, 0.25rem) * 1);
    overflow: hidden;
    border-radius: calc(var(--radius, 0.625rem) - 4px);
    background: color-mix(in oklab, var(--primary, oklch(0.205 0 0)) 15%, transparent);
  }

  audio-ui-timeline-clip[data-selected] {
    box-shadow: 0 0 0 2px var(--ring, oklch(0.708 0 0));
  }

  audio-ui-timeline-clip-header {
    overflow: hidden;
    padding-inline: calc(var(--spacing, 0.25rem) * 1.5);
    background: var(--primary, oklch(0.205 0 0));
    color: var(--primary-foreground, oklch(0.985 0 0));
    font-size: 10px;
    text-overflow: ellipsis;
    white-space: nowrap;
    cursor: grab;
  }

  audio-ui-timeline-clip-handle {
    position: absolute;
    top: 0;
    bottom: 0;
    width: calc(var(--spacing, 0.25rem) * 1.5);
    cursor: ew-resize;
  }

  audio-ui-timeline-clip-handle[edge="start"] {
    left: 0;
  }

  audio-ui-timeline-clip-handle[edge="end"] {
    right: 0;
  }

  audio-ui-timeline-playhead {
    top: 0;
    bottom: 0;
    width: 1px;
    background: var(--foreground, oklch(0.145 0 0));
  }

  audio-ui-timeline-navigator {
    height: calc(var(--spacing, 0.25rem) * 3);
    margin-top: calc(var(--spacing, 0.25rem) * 2);
    border-radius: calc(var(--radius, 0.625rem) - 4px);
    background: var(--muted, oklch(0.97 0 0));
  }

  audio-ui-timeline-navigator-window {
    top: 0;
    bottom: 0;
    border-radius: calc(var(--radius, 0.625rem) - 4px);
    background: color-mix(in oklab, var(--primary, oklch(0.205 0 0)) 25%, transparent);
    cursor: grab;
  }
</style>

<audio-ui-timeline duration="32" snap-step="1">
  <audio-ui-timeline-viewport>
    <audio-ui-timeline-ruler aria-label="Position"></audio-ui-timeline-ruler>
    <audio-ui-timeline-track>
      <audio-ui-timeline-track-header>Drums</audio-ui-timeline-track-header>
      <audio-ui-timeline-lane seekable>
        <audio-ui-timeline-clip aria-label="Beat" start="0" length="8">
          <audio-ui-timeline-clip-header>Beat</audio-ui-timeline-clip-header>
        </audio-ui-timeline-clip>
        <audio-ui-timeline-playhead></audio-ui-timeline-playhead>
      </audio-ui-timeline-lane>
    </audio-ui-timeline-track>
  </audio-ui-timeline-viewport>
</audio-ui-timeline>

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

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

*With Tailwind*

```html
<audio-ui-timeline class="block w-full max-w-xl" duration="32" snap-step="1">
  <audio-ui-timeline-viewport class="rounded-md border">
    <audio-ui-timeline-ruler class="ms-20 h-6 cursor-pointer bg-muted" aria-label="Position"></audio-ui-timeline-ruler>
    <audio-ui-timeline-track class="h-14 border-t">
      <audio-ui-timeline-track-header class="w-20 border-e bg-background p-2 text-xs">Drums</audio-ui-timeline-track-header>
      <audio-ui-timeline-lane seekable>
        <audio-ui-timeline-clip class="inset-y-1 overflow-hidden rounded-sm bg-primary/15 data-selected:ring-2 data-selected:ring-ring" aria-label="Beat" start="0" length="8">
          <audio-ui-timeline-clip-header class="cursor-grab truncate bg-primary px-1.5 text-[10px] text-primary-foreground">Beat</audio-ui-timeline-clip-header>
        </audio-ui-timeline-clip>
        <audio-ui-timeline-playhead class="inset-y-0 w-px bg-foreground"></audio-ui-timeline-playhead>
      </audio-ui-timeline-lane>
    </audio-ui-timeline-track>
  </audio-ui-timeline-viewport>
</audio-ui-timeline>

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

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

**Vue**

*With CSS*

```vue
<script setup lang="ts">
import "@audio-ui/elements/timeline";
</script>

<template>
  <audio-ui-timeline duration="32" snap-step="1" @change="(event) => console.log(event.detail.value)">
    <audio-ui-timeline-viewport>
      <audio-ui-timeline-ruler aria-label="Position" />
      <audio-ui-timeline-track>
        <audio-ui-timeline-track-header>Drums</audio-ui-timeline-track-header>
        <audio-ui-timeline-lane seekable>
          <audio-ui-timeline-clip aria-label="Beat" start="0" length="8">
            <audio-ui-timeline-clip-header>Beat</audio-ui-timeline-clip-header>
          </audio-ui-timeline-clip>
          <audio-ui-timeline-playhead />
        </audio-ui-timeline-lane>
      </audio-ui-timeline-track>
    </audio-ui-timeline-viewport>
  </audio-ui-timeline>
</template>

<style>
audio-ui-timeline {
  display: block;
  width: 100%;
  max-width: 36rem;
}

audio-ui-timeline-viewport {
  border: 1px solid var(--border, oklch(0.922 0 0));
  border-radius: calc(var(--radius, 0.625rem) - 2px);
}

audio-ui-timeline-ruler {
  height: calc(var(--spacing, 0.25rem) * 6);
  margin-inline-start: calc(var(--spacing, 0.25rem) * 20);
  background: var(--muted, oklch(0.97 0 0));
  cursor: pointer;
}

audio-ui-timeline-mark {
  height: 100%;
  padding-inline-start: calc(var(--spacing, 0.25rem) * 1);
  border-inline-start: 1px solid var(--border, oklch(0.922 0 0));
  color: var(--muted-foreground, oklch(0.556 0 0));
  font-size: 10px;
}

audio-ui-timeline-range {
  top: 0;
  bottom: 0;
  background: color-mix(in oklab, var(--primary, oklch(0.205 0 0)) 15%, transparent);
}

audio-ui-timeline-track {
  height: calc(var(--spacing, 0.25rem) * 14);
  border-top: 1px solid var(--border, oklch(0.922 0 0));
}

audio-ui-timeline-track-header {
  width: calc(var(--spacing, 0.25rem) * 20);
  padding: calc(var(--spacing, 0.25rem) * 2);
  border-inline-end: 1px solid var(--border, oklch(0.922 0 0));
  background: var(--background, oklch(1 0 0));
  font-size: 0.75rem;
  line-height: 1rem;
}

audio-ui-timeline-clip {
  top: calc(var(--spacing, 0.25rem) * 1);
  bottom: calc(var(--spacing, 0.25rem) * 1);
  overflow: hidden;
  border-radius: calc(var(--radius, 0.625rem) - 4px);
  background: color-mix(in oklab, var(--primary, oklch(0.205 0 0)) 15%, transparent);
}

audio-ui-timeline-clip[data-selected] {
  box-shadow: 0 0 0 2px var(--ring, oklch(0.708 0 0));
}

audio-ui-timeline-clip-header {
  overflow: hidden;
  padding-inline: calc(var(--spacing, 0.25rem) * 1.5);
  background: var(--primary, oklch(0.205 0 0));
  color: var(--primary-foreground, oklch(0.985 0 0));
  font-size: 10px;
  text-overflow: ellipsis;
  white-space: nowrap;
  cursor: grab;
}

audio-ui-timeline-clip-handle {
  position: absolute;
  top: 0;
  bottom: 0;
  width: calc(var(--spacing, 0.25rem) * 1.5);
  cursor: ew-resize;
}

audio-ui-timeline-clip-handle[edge="start"] {
  left: 0;
}

audio-ui-timeline-clip-handle[edge="end"] {
  right: 0;
}

audio-ui-timeline-playhead {
  top: 0;
  bottom: 0;
  width: 1px;
  background: var(--foreground, oklch(0.145 0 0));
}

audio-ui-timeline-navigator {
  height: calc(var(--spacing, 0.25rem) * 3);
  margin-top: calc(var(--spacing, 0.25rem) * 2);
  border-radius: calc(var(--radius, 0.625rem) - 4px);
  background: var(--muted, oklch(0.97 0 0));
}

audio-ui-timeline-navigator-window {
  top: 0;
  bottom: 0;
  border-radius: calc(var(--radius, 0.625rem) - 4px);
  background: color-mix(in oklab, var(--primary, oklch(0.205 0 0)) 25%, transparent);
  cursor: grab;
}
</style>
```

*With Tailwind*

```vue
<script setup lang="ts">
import "@audio-ui/elements/timeline";
</script>

<template>
  <audio-ui-timeline class="block w-full max-w-xl" duration="32" snap-step="1" @change="(event) => console.log(event.detail.value)">
    <audio-ui-timeline-viewport class="rounded-md border">
      <audio-ui-timeline-ruler class="ms-20 h-6 cursor-pointer bg-muted" aria-label="Position" />
      <audio-ui-timeline-track class="h-14 border-t">
        <audio-ui-timeline-track-header class="w-20 border-e bg-background p-2 text-xs">Drums</audio-ui-timeline-track-header>
        <audio-ui-timeline-lane seekable>
          <audio-ui-timeline-clip class="inset-y-1 overflow-hidden rounded-sm bg-primary/15 data-selected:ring-2 data-selected:ring-ring" aria-label="Beat" start="0" length="8">
            <audio-ui-timeline-clip-header class="cursor-grab truncate bg-primary px-1.5 text-[10px] text-primary-foreground">Beat</audio-ui-timeline-clip-header>
          </audio-ui-timeline-clip>
          <audio-ui-timeline-playhead class="inset-y-0 w-px bg-foreground" />
        </audio-ui-timeline-lane>
      </audio-ui-timeline-track>
    </audio-ui-timeline-viewport>
  </audio-ui-timeline>
</template>
```

**Svelte**

*With CSS*

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

<audio-ui-timeline duration="32" snap-step="1" onchange={(event) => console.log(event.detail.value)}>
  <audio-ui-timeline-viewport>
    <audio-ui-timeline-ruler aria-label="Position"></audio-ui-timeline-ruler>
    <audio-ui-timeline-track>
      <audio-ui-timeline-track-header>Drums</audio-ui-timeline-track-header>
      <audio-ui-timeline-lane seekable>
        <audio-ui-timeline-clip aria-label="Beat" start="0" length="8">
          <audio-ui-timeline-clip-header>Beat</audio-ui-timeline-clip-header>
        </audio-ui-timeline-clip>
        <audio-ui-timeline-playhead></audio-ui-timeline-playhead>
      </audio-ui-timeline-lane>
    </audio-ui-timeline-track>
  </audio-ui-timeline-viewport>
</audio-ui-timeline>

<style>
:global {
  audio-ui-timeline {
    display: block;
    width: 100%;
    max-width: 36rem;
  }

  audio-ui-timeline-viewport {
    border: 1px solid var(--border, oklch(0.922 0 0));
    border-radius: calc(var(--radius, 0.625rem) - 2px);
  }

  audio-ui-timeline-ruler {
    height: calc(var(--spacing, 0.25rem) * 6);
    margin-inline-start: calc(var(--spacing, 0.25rem) * 20);
    background: var(--muted, oklch(0.97 0 0));
    cursor: pointer;
  }

  audio-ui-timeline-mark {
    height: 100%;
    padding-inline-start: calc(var(--spacing, 0.25rem) * 1);
    border-inline-start: 1px solid var(--border, oklch(0.922 0 0));
    color: var(--muted-foreground, oklch(0.556 0 0));
    font-size: 10px;
  }

  audio-ui-timeline-range {
    top: 0;
    bottom: 0;
    background: color-mix(in oklab, var(--primary, oklch(0.205 0 0)) 15%, transparent);
  }

  audio-ui-timeline-track {
    height: calc(var(--spacing, 0.25rem) * 14);
    border-top: 1px solid var(--border, oklch(0.922 0 0));
  }

  audio-ui-timeline-track-header {
    width: calc(var(--spacing, 0.25rem) * 20);
    padding: calc(var(--spacing, 0.25rem) * 2);
    border-inline-end: 1px solid var(--border, oklch(0.922 0 0));
    background: var(--background, oklch(1 0 0));
    font-size: 0.75rem;
    line-height: 1rem;
  }

  audio-ui-timeline-clip {
    top: calc(var(--spacing, 0.25rem) * 1);
    bottom: calc(var(--spacing, 0.25rem) * 1);
    overflow: hidden;
    border-radius: calc(var(--radius, 0.625rem) - 4px);
    background: color-mix(in oklab, var(--primary, oklch(0.205 0 0)) 15%, transparent);
  }

  audio-ui-timeline-clip[data-selected] {
    box-shadow: 0 0 0 2px var(--ring, oklch(0.708 0 0));
  }

  audio-ui-timeline-clip-header {
    overflow: hidden;
    padding-inline: calc(var(--spacing, 0.25rem) * 1.5);
    background: var(--primary, oklch(0.205 0 0));
    color: var(--primary-foreground, oklch(0.985 0 0));
    font-size: 10px;
    text-overflow: ellipsis;
    white-space: nowrap;
    cursor: grab;
  }

  audio-ui-timeline-clip-handle {
    position: absolute;
    top: 0;
    bottom: 0;
    width: calc(var(--spacing, 0.25rem) * 1.5);
    cursor: ew-resize;
  }

  audio-ui-timeline-clip-handle[edge="start"] {
    left: 0;
  }

  audio-ui-timeline-clip-handle[edge="end"] {
    right: 0;
  }

  audio-ui-timeline-playhead {
    top: 0;
    bottom: 0;
    width: 1px;
    background: var(--foreground, oklch(0.145 0 0));
  }

  audio-ui-timeline-navigator {
    height: calc(var(--spacing, 0.25rem) * 3);
    margin-top: calc(var(--spacing, 0.25rem) * 2);
    border-radius: calc(var(--radius, 0.625rem) - 4px);
    background: var(--muted, oklch(0.97 0 0));
  }

  audio-ui-timeline-navigator-window {
    top: 0;
    bottom: 0;
    border-radius: calc(var(--radius, 0.625rem) - 4px);
    background: color-mix(in oklab, var(--primary, oklch(0.205 0 0)) 25%, transparent);
    cursor: grab;
  }
}
</style>
```

*With Tailwind*

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

<audio-ui-timeline class="block w-full max-w-xl" duration="32" snap-step="1" onchange={(event) => console.log(event.detail.value)}>
  <audio-ui-timeline-viewport class="rounded-md border">
    <audio-ui-timeline-ruler class="ms-20 h-6 cursor-pointer bg-muted" aria-label="Position"></audio-ui-timeline-ruler>
    <audio-ui-timeline-track class="h-14 border-t">
      <audio-ui-timeline-track-header class="w-20 border-e bg-background p-2 text-xs">Drums</audio-ui-timeline-track-header>
      <audio-ui-timeline-lane seekable>
        <audio-ui-timeline-clip class="inset-y-1 overflow-hidden rounded-sm bg-primary/15 data-selected:ring-2 data-selected:ring-ring" aria-label="Beat" start="0" length="8">
          <audio-ui-timeline-clip-header class="cursor-grab truncate bg-primary px-1.5 text-[10px] text-primary-foreground">Beat</audio-ui-timeline-clip-header>
        </audio-ui-timeline-clip>
        <audio-ui-timeline-playhead class="inset-y-0 w-px bg-foreground"></audio-ui-timeline-playhead>
      </audio-ui-timeline-lane>
    </audio-ui-timeline-track>
  </audio-ui-timeline-viewport>
</audio-ui-timeline>
```

**React**

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

| Export | Kind | What it is |
| --- | --- | --- |
| `TimelineClipElement` | class | A region of a source on a lane, such as a recording or a pattern. |
| `TimelineClipElementEventMap` | interface | The events a clip fires, as `addEventListener` types them. |
| `TimelineClipEventDetail` | interface | What a clip's events carry, besides the clip itself as their target. |
| `TimelineClipFadeElement` | class | Renders the fade at its `edge` (`in` or `out`) over the clip: the gain ramp as a line in `currentColor`, with the attenuated area shaded by `--clip-fade-shade` (the line color by default). |
| `TimelineClipFadeHandleElement` | class | Sets the length of the fade at its `edge` (`in` or `out`) by drag, off the grid, or through the native `<input type="range">` it contains, which carries the accessible name. |
| `TimelineClipHandleElement` | class | Trims the clip's `edge` (`start` or `end`) by drag, on the grid unless Cmd or Ctrl is held, or through the native `<input type="range">` it contains, which carries the accessible name. |
| `TimelineClipHeaderElement` | class | The clip's title bar. |
| `TimelineLaneElement` | class | A lane for placed parts, such as a track's clips or a row of markers. |
| `TimelineLaneElementEventMap` | interface | The events a `selectable` lane or ruler fires, as `addEventListener` types them. |
| `TimelineMarkElement` | class | A part placed at `value` on a lane, such as a bar number, a cue or a grid line, labelled by its content. |
| `TimelineMarkElementEventMap` | interface | The events a mark fires, as `addEventListener` types them. |
| `TimelineNavigatorElement` | class | An overview bar of the whole timeline, showing the visible range as `<audio-ui-timeline-navigator-window>`. |
| `TimelineNavigatorHandleElement` | class | An `edge` of the navigator window (`start` or `end`). |
| `TimelineNavigatorWindowElement` | class | The visible range of the timeline. |
| `TimelinePlayheadElement` | class | A marker at the playback position on its lane, following it through `--timeline-offset`. |
| `TimelineRulerElement` | class | The lane that sets the position: a press seeks to the pointer and a drag scrubs, on the grid unless Cmd or Ctrl is held, four times finer with Shift. |
| `TimelineViewportElement` | class | The scrolling container of the timeline. |
| `TimelineRangeElement` | class | A loop or selection on its lane, from `value` (`"start end"`; the whole timeline by default). |
| `TimelineRangeElementEventMap` | interface | The events a range fires, as `addEventListener` types them. |
| `TimelineRangeEventDetail` | interface | What a range's events carry: which edge moved, or `"both"`, and the range, `[start, end]` in the timeline's units. |
| `TimelineRangeHandleElement` | class | Resizes the `edge` (`start` or `end`) of its range by drag, on the grid unless Cmd or Ctrl is held, or through the native `<input type="range">` it contains, which carries the accessible name. |
| `TIMELINE_TAGS` | const | The names the timeline's elements are registered under, frozen: a page cannot rename them by accident. |
| `TimelineChangeEvent` | type | Fired once a gesture that moved the position ends. |
| `TimelineDescribe` | type | Returns how a position is announced. |
| `TimelineElement` | class | The root of a timeline. |
| `TimelineElementEventMap` | interface | The events a timeline fires, as `addEventListener` types them. |
| `TimelineEventDetail` | interface | What a timeline's `input` and `change` events carry, besides the timeline itself as their target. |
| `TimelineInputEvent` | type | Fired on every move of the position. |
| `TimelineSettings` | interface | What the timeline's parts measure and step by, as its attributes set them. |
| `TimelineZoomEvent` | type | Fired when the zoom changes, already clamped. |
| `ViewportMetrics` | interface | How the viewport is scrolled and zoomed, as last laid out. |
| `TimelineTrackElement` | class | A track: a row with a header and a lane. |
| `TimelineTrackElementEventMap` | interface | The events a track fires, as `addEventListener` types them: its height, in pixels. |
| `TimelineTrackHeaderElement` | class | The header beside a track's lane, for its name and controls. |
| `TimelineTrackResizeHandleElement` | class | Sets the track's height by drag, or through the native `<input type="range">` it contains, which carries the accessible name: the arrow keys step (Down is taller), Page Up and Page Down take larger steps, and Home and End go to the minimum and maximum. |

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `duration` | `number` | `1` | Length of the timeline, in its units. |
| `position` | `number` |  | The playback position. The `source` property follows a live position instead. |
| `default-position` | `number` | `0` |  |
| `zoom` | `number` |  | Pixels per unit. When unset, the timeline fits its width. |
| `default-zoom` | `number` |  |  |
| `min-zoom` | `number` | `0` |  |
| `max-zoom` | `number` |  |  |
| `snap-step` | `number` | `0` | Grid spacing. Gestures snap to it unless Cmd or Ctrl is held; 0 disables the grid. |
| `snap-offset` | `number` | `0` | Where the grid starts. |
| `snap-points` | `string` |  | Magnetic positions for dragged clips and ranges, separated by spaces, such as clip edges and locators. |
| `step` | `number` |  | Arrow key step. Defaults to `snap-step`, or `duration / 100`. |
| `large-step` | `number` |  | Page Up and Page Down step. Defaults to ten steps. |
| `disabled` | `boolean` |  |  |

| Event | Detail | Description |
| --- | --- | --- |
| `input`, `change` | `{ value, reason }` | The position changes, then the gesture ends |
| `zoomchange` | `{ value, reason }` | The zoom changes, already clamped. `preventDefault()` rejects it |

Every lane sets `--timeline-unit`, the width of one unit; placed parts set `--timeline-offset` and `--timeline-length`.

| Part | Description |
| --- | --- |
| `<audio-ui-timeline-viewport>` | The scrolling container. Cmd/Ctrl + wheel and pinch zoom around the pointer |
| `<audio-ui-timeline-ruler>` | The lane that sets the position, containing the native range input. With `selectable`, Shift + drag selects a range and fires `selectioninput` and `selectioncommit` |
| `<audio-ui-timeline-lane>` | A lane for placed parts. `seekable` sets the position from a press on empty space; with `selectable`, a drag selects a range |
| `<audio-ui-timeline-playhead>` | A marker at the position |
| `<audio-ui-timeline-mark>` | A part placed at `value`, such as a bar number. With `movable`, it can be dragged and fires `markinput` and `markchange` |
| `<audio-ui-timeline-range>` | A loop or selection, `value` as `"start end"`. Draggable, resizable with an `<audio-ui-timeline-range-handle>` per `edge`, and with `drawable`, drawn on empty lane. Fires `rangeinput` and `rangechange` with `{ value, edge, reason }` |
| `<audio-ui-timeline-navigator>` | An overview bar. Its `<audio-ui-timeline-navigator-window>` scrolls, its `<audio-ui-timeline-navigator-handle>`s zoom, and a double click fits the whole timeline |
| `<audio-ui-timeline-track>` | A row with a header and a lane, sized by `height` in pixels. An `<audio-ui-timeline-track-resize-handle>` changes it and fires `heightinput` and `heightchange`. `<audio-ui-timeline-track-header>` stays pinned while the lane scrolls |
| `<audio-ui-timeline-clip>` | A region of a source: `start`, `length`, `offset`, `fade-in`, `fade-out`, `fade-in-shape`, `fade-out-shape`, `source-length`. A press selects it (`clipselect`, then the `selected` attribute) and sets the position; Delete fires `clipremove`. Fires `clipinput` and `clipchange` with `{ value, reason, target, lane, delta, copy }`. Sets `--clip-unit` for its own parts |
| `<audio-ui-timeline-clip-header>` | Moves the clip by drag, across lanes |
| `<audio-ui-timeline-clip-handle>` | Trims the clip's `edge`, shifting the source offset rather than stretching |
| `<audio-ui-timeline-clip-fade-handle>` | Sets the fade at its `edge` (`in` or `out`). Double click or Enter cycles its shape |
| `<audio-ui-timeline-clip-fade>` | Renders the fade at its `edge`, shaded with `--clip-fade-shade` |

**In React**

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

