Lab

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

The styles read the theme's CSS variables, which Styling sets.

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

API reference

pnpm add @audio-ui/elements
import "@audio-ui/elements/timeline";
ExportKindWhat it is
TimelineClipElementclassA region of a source on a lane, such as a recording or a pattern.
TimelineClipElementEventMapinterfaceThe events a clip fires, as addEventListener types them.
TimelineClipEventDetailinterfaceWhat a clip's events carry, besides the clip itself as their target.
TimelineClipFadeElementclassRenders 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).
TimelineClipFadeHandleElementclassSets 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.
TimelineClipHandleElementclassTrims 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.
TimelineClipHeaderElementclassThe clip's title bar.
TimelineLaneElementclassA lane for placed parts, such as a track's clips or a row of markers.
TimelineLaneElementEventMapinterfaceThe events a selectable lane or ruler fires, as addEventListener types them.
TimelineMarkElementclassA part placed at value on a lane, such as a bar number, a cue or a grid line, labelled by its content.
TimelineMarkElementEventMapinterfaceThe events a mark fires, as addEventListener types them.
TimelineNavigatorElementclassAn overview bar of the whole timeline, showing the visible range as <audio-ui-timeline-navigator-window>.
TimelineNavigatorHandleElementclassAn edge of the navigator window (start or end).
TimelineNavigatorWindowElementclassThe visible range of the timeline.
TimelinePlayheadElementclassA marker at the playback position on its lane, following it through --timeline-offset.
TimelineRulerElementclassThe 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.
TimelineViewportElementclassThe scrolling container of the timeline.
TimelineRangeElementclassA loop or selection on its lane, from value ("start end"; the whole timeline by default).
TimelineRangeElementEventMapinterfaceThe events a range fires, as addEventListener types them.
TimelineRangeEventDetailinterfaceWhat a range's events carry: which edge moved, or "both", and the range, [start, end] in the timeline's units.
TimelineRangeHandleElementclassResizes 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_TAGSconstThe names the timeline's elements are registered under, frozen: a page cannot rename them by accident.
TimelineChangeEventtypeFired once a gesture that moved the position ends.
TimelineDescribetypeReturns how a position is announced.
TimelineElementclassThe root of a timeline.
TimelineElementEventMapinterfaceThe events a timeline fires, as addEventListener types them.
TimelineEventDetailinterfaceWhat a timeline's input and change events carry, besides the timeline itself as their target.
TimelineInputEventtypeFired on every move of the position.
TimelineSettingsinterfaceWhat the timeline's parts measure and step by, as its attributes set them.
TimelineZoomEventtypeFired when the zoom changes, already clamped.
ViewportMetricsinterfaceHow the viewport is scrolled and zoomed, as last laid out.
TimelineTrackElementclassA track: a row with a header and a lane.
TimelineTrackElementEventMapinterfaceThe events a track fires, as addEventListener types them: its height, in pixels.
TimelineTrackHeaderElementclassThe header beside a track's lane, for its name and controls.
TimelineTrackResizeHandleElementclassSets 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.

Props · 13

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

PartDescription
<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

The same control in React, and its parts' props: Timeline.

Built by Ouest Labs, the source code is available on GitHub.
RSS