Appearance
@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-timelinecss
@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 Class | Description |
|---|---|
.timeline | Root container — grid layout, coord space |
.timeline-ruler | Time axis along top edge |
.timeline-track-group | Collapsible group of tracks |
.timeline-track-group-label | Header row for a track group |
.timeline-track | Single horizontal lane (uses display: contents) |
.timeline-track-lane | Content area of a track |
.timeline-track-header | Label + controls sidebar |
.timeline-block | Region with position + duration |
.timeline-block-label | Text label within a block |
.timeline-block-handle | Resize handle (left/right edge) |
.timeline-marker | Point-in-time event (no duration) |
.timeline-playhead | Vertical playback position indicator |
.timeline-grid | Background snap grid |
.timeline-waveform | Audio waveform visualisation |
.timeline-selection | Range/multi-select highlight |
Tokens (timeline.css)
All tokens are in @layer vibe.tokens on :root. Prefix: --timeline-*.
Layout
| Token | Default |
|---|---|
--timeline-header-width | 12rem |
--timeline-min-height | 20rem |
--timeline-ruler-height | 2rem |
Track
| Token | Default |
|---|---|
--timeline-track-height | 3rem |
--timeline-track-height-collapsed | 1.5rem |
--timeline-track-bg | var(--surface-base) |
--timeline-track-bg-alt | var(--surface-sunken) |
--timeline-track-border | var(--border-subtle) |
--timeline-track-gap | 1px |
Block Colours
Each block variant has three tokens: --timeline-block-{variant}-bg, --timeline-block-{variant}-fg, --timeline-block-{variant}-border.
| Variant | Background hue |
|---|---|
speech | Blue 600 |
sfx | Amber 600 |
music | Purple 600 |
gesture | Teal 600 |
expression | Rose 600 |
emotion | Pink 600 |
cue | Orange 600 |
generic | Neutral 600 |
Playhead
| Token | Default |
|---|---|
--timeline-playhead-width | 2px |
--timeline-playhead-color | var(--color-accent) |
--timeline-playhead-playing-color | var(--color-danger) |
--timeline-playhead-head-size | 0.625rem |
Transitions
| Token | Default |
|---|---|
--timeline-transition-fast | 80ms ease-out |
--timeline-transition-normal | 150ms ease-out |
Variants (data-attributes)
Timeline
| Attribute | Values | Description |
|---|---|---|
data-density | sparse normal dense micro | Horizontal zoom |
data-snap | beat half quarter eighth frame free | Snap resolution |
Track
| Attribute | Values | Description |
|---|---|---|
data-kind | audio dialogue event animation control master | Track type/colour |
Block
| Attribute | Values | Description |
|---|---|---|
data-variant | speech sfx music gesture expression emotion cue generic | Block colour |
data-emphasis | primary secondary muted | Visual weight |
Marker
| Attribute | Values | Description |
|---|---|---|
data-variant | cue loop-start loop-end bookmark warning | Marker type |
Waveform
| Attribute | Values | Description |
|---|---|---|
data-style | bars mirror line filled | Render 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
| File | Description |
|---|---|
index.css | Barrel — imports tokens + generated styles |
timeline.css | Token definitions |
timeline.g.css | Generated component styles |
index.js / index.d.ts | TypeScript types + runtime constants |
Dependencies
Requires tokens from @vibe-labs/design (colors, surfaces, borders, spacing, typography, transitions, elevation).
Build
bash
npm run buildUsage 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
| Attribute | Values | Notes |
|---|---|---|
data-density | sparse normal dense micro | Horizontal zoom |
data-snap | beat half quarter eighth frame free | Snap resolution |
data-snapping | boolean flag | Activates snap grid |
data-looping | boolean flag | Loop mode active |
data-playing | boolean flag | Playback active |
Track
| Attribute | Values | Notes |
|---|---|---|
data-kind | audio dialogue event animation control master | Track type/colour |
data-muted | boolean flag | 40% opacity |
data-solo | boolean flag | Solo state |
data-locked | boolean flag | No pointer events |
data-collapsed | boolean flag | Collapsed height |
Block
| Attribute | Values | Notes |
|---|---|---|
data-variant | speech sfx music gesture expression emotion cue generic | Block colour |
data-emphasis | primary secondary muted | Visual weight |
data-selected | boolean flag | Accent ring |
data-dragging | boolean flag | Drag state |
data-resizing | boolean flag | Resize state |
data-conflicting | boolean flag | Dashed 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>