Controls
Playback control elements. Compose them freely against any timegroup.
Controls are unstyled building blocks — each one wires a single playback concern to a target timegroup. Use ef-controls as a context provider so child controls share a target without individual target attributes, or wire each element directly with target.
<ef-timegroup id="demo" mode="fixed" duration="5s" loop class="w-[1920px] h-[1080px] bg-slate-900 flex items-center justify-center">
<style>
@keyframes pulse {
0%, 100% { transform: scale(1); }
50% { transform: scale(1.08); }
}
.demo-text {
animation: pulse 2.4s ease-in-out infinite;
display: inline-block;
}
</style>
<ef-text class="demo-text text-white text-7xl font-bold">Hello</ef-text>
</ef-timegroup>
<ef-controls target="demo">
<div class="ctrl-grid">
<span class="ctrl-label">ef-toggle-play</span>
<div class="ctrl-cell">
<ef-toggle-play>
<button slot="play" class="ctrl-btn">▶ Play</button>
<button slot="pause" class="ctrl-btn">⏸ Pause</button>
</ef-toggle-play>
</div>
<span class="ctrl-label">ef-play</span>
<div class="ctrl-cell ctrl-pair">
<ef-play><button class="ctrl-btn">▶ Play</button></ef-play>
</div>
<span class="ctrl-label">ef-pause</span>
<div class="ctrl-cell ctrl-pair">
<ef-pause><button class="ctrl-btn">⏸ Pause</button></ef-pause>
</div>
<span class="ctrl-label">ef-scrubber</span>
<div class="ctrl-cell">
<ef-scrubber class="ctrl-scrubber"></ef-scrubber>
</div>
<span class="ctrl-label">ef-time-display</span>
<div class="ctrl-cell">
<ef-time-display class="ctrl-time"></ef-time-display>
</div>
<span class="ctrl-label">ef-toggle-loop</span>
<div class="ctrl-cell">
<ef-toggle-loop data-loop-on onclick="this.toggleAttribute('data-loop-on')">
<button class="ctrl-btn ctrl-loop-btn" title="Toggle loop">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
<polyline points="17 1 21 5 17 9"></polyline>
<path d="M3 11V9a4 4 0 0 1 4-4h14"></path>
<polyline points="7 23 3 19 7 15"></polyline>
<path d="M21 13v2a4 4 0 0 1-4 4H3"></path>
</svg>
Loop
</button>
</ef-toggle-loop>
</div>
</div>
</ef-controls>
<style>
.ctrl-grid {
display: grid;
grid-template-columns: max-content 1fr;
grid-auto-rows: 44px;
align-items: center;
border-top: 1px solid var(--border-subtle);
}
.ctrl-label {
font-size: 11px;
font-family: monospace;
color: var(--chrome-fg-muted);
white-space: nowrap;
padding: 0 12px 0 16px;
height: 100%;
display: flex;
align-items: center;
border-bottom: 1px solid var(--border-subtle);
}
.ctrl-cell {
display: flex;
align-items: center;
padding: 0 16px 0 0;
height: 100%;
border-bottom: 1px solid var(--border-subtle);
}
.ctrl-pair {
gap: 8px;
}
.ctrl-btn {
cursor: pointer;
padding: 4px 12px;
font-size: 13px;
display: inline-flex;
align-items: center;
gap: 6px;
color: var(--chrome-fg);
background: transparent;
border: 1px solid var(--border-subtle);
border-radius: 4px;
}
.ctrl-btn:hover {
border-color: var(--chrome-fg-muted);
}
.ctrl-loop-btn {
color: var(--chrome-fg-muted);
border-style: dashed;
transition: background 0.15s, color 0.15s, border-color 0.15s, border-style 0.15s;
}
ef-toggle-loop[data-loop-on] .ctrl-loop-btn {
background: var(--chrome-fg);
color: var(--chrome-bg);
border-color: var(--chrome-fg);
border-style: solid;
}
.ctrl-scrubber {
flex: 1;
--ef-scrubber-background: var(--chrome-fg-muted);
--ef-scrubber-progress-color: var(--chrome-fg);
}
.ctrl-time {
font-variant-numeric: tabular-nums;
color: var(--chrome-fg);
}
</style>Elements
| Element | Description |
|---|---|
ef-controls | Context provider — wraps controls and connects them to a target timegroup |
ef-toggle-play | Single button that swaps between play and pause slots |
ef-play | Play-only button — hides itself when already playing |
ef-pause | Pause-only button — hides itself when already paused |
ef-scrubber | Seek slider — drag to scrub through the composition |
ef-time-display | Live currentTime / duration readout |
ef-toggle-loop | Flips the loop property on click — bring your own UI |