Skip to content

@vibe-labs/design-components-timeline

Framework-agnostic CSS and TypeScript types for timeline-based editors: chiptune trackers, dialogue event editors, animation sequencers, and cutscene composers.

Installation

bash
npm install @vibe-labs/design-components-timeline
css
@import "@vibe-labs/design-components-timeline";
ts
import type { TimelineBlockVariant, TimelineTrackKind } from "@vibe-labs/design-components-timeline/types";
import { TimelineBlockVariants, TimelineTrackKinds } from "@vibe-labs/design-components-timeline/types";

Contents

Sub-Components

Base ClassDescription
.timelineRoot container — grid layout, coord space
.timeline-rulerTime axis along top edge
.timeline-track-groupCollapsible group of tracks
.timeline-track-group-labelHeader row for a track group
.timeline-trackSingle horizontal lane (uses display: contents)
.timeline-track-laneContent area of a track
.timeline-track-headerLabel + controls sidebar
.timeline-blockRegion with position + duration
.timeline-block-labelText label within a block
.timeline-block-handleResize handle (left/right edge)
.timeline-markerPoint-in-time event (no duration)
.timeline-playheadVertical playback position indicator
.timeline-gridBackground snap grid
.timeline-waveformAudio waveform visualisation
.timeline-selectionRange/multi-select highlight

Tokens (timeline.css)

All tokens are in @layer vibe.tokens on :root. Prefix: --timeline-*.

Layout

TokenDefault
--timeline-header-width12rem
--timeline-min-height20rem
--timeline-ruler-height2rem

Track

TokenDefault
--timeline-track-height3rem
--timeline-track-height-collapsed1.5rem
--timeline-track-bgvar(--surface-base)
--timeline-track-bg-altvar(--surface-sunken)
--timeline-track-bordervar(--border-subtle)
--timeline-track-gap1px

Block Colours

Each block variant has three tokens: --timeline-block-{variant}-bg, --timeline-block-{variant}-fg, --timeline-block-{variant}-border.

VariantBackground hue
speechBlue 600
sfxAmber 600
musicPurple 600
gestureTeal 600
expressionRose 600
emotionPink 600
cueOrange 600
genericNeutral 600

Playhead

TokenDefault
--timeline-playhead-width2px
--timeline-playhead-colorvar(--color-accent)
--timeline-playhead-playing-colorvar(--color-danger)
--timeline-playhead-head-size0.625rem

Transitions

TokenDefault
--timeline-transition-fast80ms ease-out
--timeline-transition-normal150ms ease-out

Variants (data-attributes)

Timeline

AttributeValuesDescription
data-densitysparse normal dense microHorizontal zoom
data-snapbeat half quarter eighth frame freeSnap resolution

Track

AttributeValuesDescription
data-kindaudio dialogue event animation control masterTrack type/colour

Block

AttributeValuesDescription
data-variantspeech sfx music gesture expression emotion cue genericBlock colour
data-emphasisprimary secondary mutedVisual weight

Marker

AttributeValuesDescription
data-variantcue loop-start loop-end bookmark warningMarker type

Waveform

AttributeValuesDescription
data-stylebars mirror line filledRender mode

Boolean Flags

Timeline

data-snapping · data-looping · data-playing

Track

data-muted (40% opacity) · data-solo · data-locked (no pointer events, dims) · data-collapsed

Track Group

data-collapsed (hides child tracks)

Block

data-selected (accent ring) · data-dragging (reduced opacity, no events) · data-resizing (col-resize cursor) · data-conflicting (dashed danger outline)

Marker

data-selected (elevated z-index, ring on head)

Playhead

data-playing (switches to playing colour)

Selection

data-active (shows the region)

TypeScript Exports

Const Arrays (runtime + generation)

TimelineTrackKinds · TimelineBlockVariants · TimelineBlockEmphases · TimelineSnapResolutions · TimelineZoomDensities · TimelineMarkerVariants · TimelineWaveformStyles

Derived Types

TimelineTrackKind · TimelineBlockVariant · TimelineBlockEmphasis · TimelineSnapResolution · TimelineZoomDensity · TimelineMarkerVariant · TimelineWaveformStyle

Style Prop Interfaces

TimelineStyleProps · TimelineRulerStyleProps · TimelineTrackGroupStyleProps · TimelineTrackStyleProps · TimelineTrackHeaderStyleProps · TimelineBlockStyleProps · TimelineMarkerStyleProps · TimelinePlayheadStyleProps · TimelineGridStyleProps · TimelineWaveformStyleProps · TimelineSelectionStyleProps

Dist Structure

FileDescription
index.cssBarrel — imports tokens + generated styles
timeline.cssToken definitions
timeline.g.cssGenerated component styles
index.js / index.d.tsTypeScript types + runtime constants

Dependencies

Requires tokens from @vibe-labs/design (colors, surfaces, borders, spacing, typography, transitions, elevation).

Build

bash
npm run build

Usage Guide

Import

css
@import "@vibe-labs/design-components-timeline";
ts
import { TimelineBlockVariants, TimelineTrackKinds } from "@vibe-labs/design-components-timeline/types";
import type { TimelineStyleProps, TimelineBlockStyleProps } from "@vibe-labs/design-components-timeline/types";

Variants

Timeline root

AttributeValuesNotes
data-densitysparse normal dense microHorizontal zoom
data-snapbeat half quarter eighth frame freeSnap resolution
data-snappingboolean flagActivates snap grid
data-loopingboolean flagLoop mode active
data-playingboolean flagPlayback active

Track

AttributeValuesNotes
data-kindaudio dialogue event animation control masterTrack type/colour
data-mutedboolean flag40% opacity
data-soloboolean flagSolo state
data-lockedboolean flagNo pointer events
data-collapsedboolean flagCollapsed height

Block

AttributeValuesNotes
data-variantspeech sfx music gesture expression emotion cue genericBlock colour
data-emphasisprimary secondary mutedVisual weight
data-selectedboolean flagAccent ring
data-draggingboolean flagDrag state
data-resizingboolean flagResize state
data-conflictingboolean flagDashed danger outline

Block position and width are set via left and width inline styles (percentage or pixel values).

Examples

Minimal timeline

html
<div class="timeline" data-density="normal">
  <div class="timeline-ruler"></div>
  <div class="timeline-track" data-kind="dialogue">
    <div class="timeline-track-header">Track 1</div>
    <div class="timeline-track-lane">
      <div class="timeline-grid"></div>
      <div class="timeline-block" data-variant="speech" style="left: 10%; width: 20%;">
        <span class="timeline-block-label">Hello</span>
      </div>
    </div>
  </div>
  <div class="timeline-playhead" style="left: 15%;"></div>
</div>

Multi-track with groups

html
<div class="timeline" data-density="normal" data-snap="beat" data-snapping>
  <div class="timeline-ruler"></div>

  <div class="timeline-track-group">
    <div class="timeline-track-group-label">Dialogue</div>

    <div class="timeline-track" data-kind="dialogue">
      <div class="timeline-track-header">Bob</div>
      <div class="timeline-track-lane">
        <div class="timeline-grid"></div>
        <div class="timeline-block" data-variant="speech" style="left: 5%; width: 15%;">
          <span class="timeline-block-label">Hello there</span>
          <div class="timeline-block-handle" data-edge="start"></div>
          <div class="timeline-block-handle" data-edge="end"></div>
        </div>
      </div>
    </div>

    <div class="timeline-track" data-kind="dialogue">
      <div class="timeline-track-header">Alice</div>
      <div class="timeline-track-lane">
        <div class="timeline-grid"></div>
        <div class="timeline-block" data-variant="speech" style="left: 25%; width: 10%;">
          <span class="timeline-block-label">General!</span>
        </div>
      </div>
    </div>
  </div>

  <div class="timeline-playhead" style="left: 20%;"></div>
</div>

Block states (selected, dragging, conflicting)

html
<div class="timeline-track-lane">
  <div class="timeline-block" data-variant="music" data-selected style="left: 10%; width: 20%;">
    <span class="timeline-block-label">Selected</span>
  </div>
  <div class="timeline-block" data-variant="sfx" data-dragging style="left: 35%; width: 15%;">
    <span class="timeline-block-label">Dragging</span>
  </div>
  <div class="timeline-block" data-variant="cue" data-conflicting style="left: 55%; width: 20%;">
    <span class="timeline-block-label">Conflict</span>
  </div>
</div>

Markers and playhead

html
<div class="timeline-track-lane">
  <div class="timeline-marker" data-variant="cue" style="left: 25%;"></div>
  <div class="timeline-marker" data-variant="loop-start" style="left: 10%;"></div>
  <div class="timeline-marker" data-variant="loop-end" style="left: 60%;"></div>
</div>

<div class="timeline-playhead" data-playing style="left: 30%;"></div>

Range selection and waveform

html
<div class="timeline-track-lane">
  <div class="timeline-waveform" data-style="bars"></div>
  <div class="timeline-selection" data-active style="left: 20%; width: 30%;"></div>
</div>

With Vue

vue
<template>
  <div
    class="timeline"
    :data-density="density"
    :data-snap="snap"
    :data-snapping="snapping || undefined"
    :data-playing="playing || undefined"
  >
    <div class="timeline-ruler"></div>
    <div
      v-for="track in tracks"
      :key="track.id"
      class="timeline-track"
      :data-kind="track.kind"
      :data-muted="track.muted || undefined"
    >
      <div class="timeline-track-header">{{ track.label }}</div>
      <div class="timeline-track-lane">
        <div class="timeline-grid"></div>
        <div
          v-for="block in track.blocks"
          :key="block.id"
          class="timeline-block"
          :data-variant="block.variant"
          :data-selected="block.selected || undefined"
          :style="{ left: block.start + '%', width: block.duration + '%' }"
        >
          <span class="timeline-block-label">{{ block.label }}</span>
          <div class="timeline-block-handle" data-edge="start"></div>
          <div class="timeline-block-handle" data-edge="end"></div>
        </div>
      </div>
    </div>
    <div class="timeline-playhead" :style="{ left: playheadPosition + '%' }"></div>
  </div>
</template>

Vibe