Skip to content
Docs / Design Tokens
Build the editor

Theming

Every colour, border, shadow and overlay in the editor UI flows through one contract: the --elah-* CSS variables. Re-skinning means overriding those variables in one place.

Design Tokens

Components never inline colour literals. They use a Tailwind token class or a var(--elah-*) reference. Whoever defines --elah-* decides the editor's look, and the variables are scoped to an element with the elah-root class, so wrap the editor in one.

Import all three stylesheets once at your app root. Each package compiles only the classes its own components use, so they do not contain each other's rules. The compiled files are plain CSS with preflight off: you do not need Tailwind, and no utility class names such as .flex or .p-2 are exposed.

tsx
import '@elah/timeline/styles.css'        // compiled timeline rules
import '@elah/editor/styles.css'          // compiled editor rules
import '@elah/editor/styles/tokens.css'   // --elah-* values (dark defaults)

<EditorProvider fps={30}>
  {/* elah-root scopes the --elah-* tokens */}
  <div className="elah-root" style={{ height: '100vh' }}>
    {/* AssetPanel, Preview, Timeline ... */}
  </div>
</EditorProvider>

Skipping the tokens file is valid when your app already defines .elah-root itself. Override any variable in your own .elah-root scope:

css
.elah-root {
  --elah-accent:   #6366f1;   /* indigo brand */
  --elah-bg-panel: #1e1e2e;
  --elah-playhead: #f43f5e;
}

You can also map the variables onto your own tokens, for example --elah-bg-panel: var(--color-surface-low), so the editor follows your app's light and dark theme automatically. The components are identical in every case. Only the values differ. The full variable list is the public theming surface, in packages/editor/src/styles/tokens.css, and the repository's docs/design-tokens.md documents the groups. Every name below is prefixed with --elah-:

bg, bg-secondary, bg-panel, bg-card, bg-elevated, bg-highest
Structural surfaces: canvas, lanes, panels, cards, menus, chips
border, border-subtle, outline
Hairlines and interactive or focus edges
text, text-muted, text-on-clip
Foreground text, and the label on a coloured clip
accent, accent-hover, accent-dim, accent-glow, accent-soft, accent-text
The primary accent and its variants
clip-{video,audio,text,image,shape,freehand}-{top,mid,bottom,accent}
Per-clip-type gradient ramp and accent
tag-*
Asset and element kind chips (foreground and background per kind)
playhead, tick-color, tick-label
Playhead needle, ruler ticks and labels
selection-border, selection-glow
Selected-clip highlight
transition-*
Cut line and diamond marker states
menu-*, popover-*, dialog-*
Context menu, transition picker popover, blocking dialog
danger-*, color-error, info-*
Destructive actions, error text, informational toast
effect-*
Reusable glosses, inset highlights, scrims and drop shadows
preview-bg, stage-border, stage-glow, selection-color, selection-handle
Preview canvas and the media-transform overlay affordances

Overlay Tokens (0.6.0)

Before 0.6.0 the preview overlays and clip badges used hard-coded colours and could not be themed. They now read these variables. The values below are the standalone defaults from tokens.css.

VariableDefaultUsed for
--elah-overlay-scrimrgba(0, 0, 0, 0.7)Full-stage scrim: the GL-context recovery layer and the loading error label
--elah-overlay-text#ffffffText drawn on that scrim
--elah-overlay-borderrgba(255, 255, 255, 0.3)Ghost button drawn on the scrim (Reload preview)
--elah-overlay-bgrgba(255, 255, 255, 0.1)Ghost button fill
--elah-overlay-bg-hoverrgba(255, 255, 255, 0.2)Ghost button fill on hover
--elah-spinner-trackrgba(255, 255, 255, 0.25)Preview loading spinner ring
--elah-spinner-headrgba(255, 255, 255, 0.9)Spinner leading arc
--elah-clip-badge-bgrgba(0, 0, 0, 0.55)Backing pill for small clip badges, such as the speed badge
--elah-toast-shadow0 4px 12px rgba(0, 0, 0, 0.35)Drop shadow under the asset and source panel toasts
css
.elah-root {
  --elah-overlay-scrim:  rgba(10, 12, 20, 0.8);
  --elah-spinner-head:   #7dd3fc;
  --elah-clip-badge-bg:  rgba(0, 0, 0, 0.7);
}

The classNames Prop

Tokens recolour every editor instance. For a single component, pass classNames: a per-slot map of Tailwind class strings that wins over the built-in classes for the same CSS property. <Timeline> takes a TimelineClassNames and <SourcePanel> takes a SourcePanelClassNames. The bare className prop is shorthand for classNames.root, merged after it.

tsx
import { Timeline } from '@elah/editor'

<Timeline
  classNames={{
    root:            'rounded-xl',
    ruler:           'bg-zinc-900',                // ruler strip
    rulerTick:       'bg-zinc-600',                // tick marks
    rulerLabel:      'text-zinc-400',              // timecode labels
    lane:            'bg-zinc-950',
    clip:            'rounded-2xl shadow-lg',      // clip shape, all types
    clipVideo:       'from-sky-400 to-sky-600',    // video clip body
    clipVideoAccent: 'text-sky-300',               // its stripe and track bar
    playhead:        'text-cyan-400',              // a TEXT colour, see below
  }}
/>

The slots are root, ruler, rulerTick, rulerLabel, track, trackLabel, lane, clip, clipVideo, clipAudio, clipText, clipImage, clipVideoAccent, clipAudioAccent, clipTextAccent, clipImageAccent and playhead. Use bg-* classes or gradients to recolour surfaces and clip bodies. Use text-* to recolour accents: the playhead, clip stripe and border, and track-label bar all paint from currentColor.

Overrides win because of cn, exported from @elah/timeline (and from @elah/editor). It is clsx plus tailwind-merge, so a later class reliably beats an earlier one for the same property. Plain concatenation would leave both in the list and let CSS source order decide. Use it for your own components too:

tsx
import { cn } from '@elah/timeline'

<div className={cn('rounded border p-2', props.className)} />

timelineTheme (Deprecated)

timelineTheme is a backward-compatibility facade, marked @deprecated, and scheduled for removal in a future major release. Its values now point at the --elah-* variable contract rather than raw hex literals, for example timelineTheme.surface.background is 'var(--elah-bg-secondary)'. Components no longer consume it internally.

For global theming, override the --elah-* properties on .elah-root. For per-instance changes, use the classNames prop. Migrate off timelineTheme to either.

On this page