Editor Toolkit
Context Bridging: Flexible Control Layouts
The ef-controls element bridges playback context from a target element to controls placed anywhere in the DOM, enabling flexible UI layouts that aren't constrained by DOM hierarchy.
The Problem with Direct Nesting
Control elements like ef-toggle-play, ef-scrubber, and ef-time-display work by reading playback context from an ancestor element — specifically an ef-timegroup, ef-video, or ef-preview. This means the simplest setup is to nest controls directly inside the composition:
<ef-timegroup mode="contain" class="w-full h-96" id="player"><ef-video src="/video.mp4" class="size-full"></ef-video><!-- Works: controls are descendants of the timegroup --><div class="absolute bottom-4 left-4 flex gap-2"><ef-toggle-play><button slot="play">Play</button><button slot="pause">Pause</button></ef-toggle-play></div></ef-timegroup>
Direct nesting works, but it constrains your layout. UI designs often require controls in separate areas — a sticky footer, a sidebar, a modal, or a completely different part of the component tree. Direct nesting prevents this.
The Solution: ef-controls
ef-controls bridges context from a target element to controls placed anywhere in the DOM. Controls inside ef-controls receive the same playback context as if they were nested directly inside the target, enabling flexible layouts without sacrificing functionality.
<ef-timegroup id="player" mode="contain" class="w-full h-96"><ef-video src="/video.mp4" class="size-full"></ef-video></ef-timegroup><!-- Controls are outside the timegroup — anywhere on the page --><div class="fixed bottom-0 left-0 right-0 bg-black p-4"><ef-controls target="player" class="flex justify-center gap-4"><ef-toggle-play><button slot="play">▶</button><button slot="pause">⏸</button></ef-toggle-play><ef-scrubber class="flex-1"></ef-scrubber><ef-time-display></ef-time-display></ef-controls></div>
The target attribute takes the id of the element to control. Everything inside ef-controls behaves identically to controls nested directly inside that element.
What Context Is Bridged
ef-controls provides these contexts to its children:
- playing — Current playback state (boolean)
- loop — Loop state (boolean)
- currentTimeMs — Current playhead position in milliseconds
- durationMs — Total duration in milliseconds
- targetTemporal — Reference to the controlled element
- focusedElement — Currently focused element
Child elements like ef-toggle-play, ef-toggle-loop, ef-scrubber, and ef-time-display automatically consume these contexts. You don't need to wire anything up manually.
What ef-controls Can Target
ef-controls works with any temporal element:
ef-timegroup— Control a full compositionef-video— Control a single video elementef-audio— Control a single audio elementef-preview— Control via the preview wrapper
Multiple Control Sets
Multiple ef-controls elements can target the same composition. All sets stay synchronized — a seek in one will update the time display in all others:
<ef-timegroup id="main" mode="contain" class="w-full h-96"><!-- composition --></ef-timegroup><!-- Desktop control bar --><ef-controls target="main" class="desktop-controls flex items-center gap-4 p-4"><ef-toggle-play>...</ef-toggle-play><ef-scrubber class="flex-1"></ef-scrubber><ef-time-display></ef-time-display></ef-controls><!-- Mobile control bar --><ef-controls target="main" class="mobile-controls flex items-center gap-2 p-2"><ef-toggle-play>...</ef-toggle-play><ef-scrubber class="flex-1"></ef-scrubber></ef-controls>
Live Demo
Controls placed outside the timegroup work identically to nested controls:
<ef-timegroup mode="contain" class="w-[720px] h-[405px] bg-black" id="context-bridge-demo"><ef-video src="https://assets.editframe.com/bars-n-tone.mp4" sourceout="2s" class="size-full object-contain"></ef-video></ef-timegroup><ef-controls target="context-bridge-demo" class="mt-4 flex items-center gap-4 p-4 bg-gray-900 rounded"><ef-toggle-play><button slot="play" class="px-4 py-2 bg-blue-600 text-white rounded">▶</button><button slot="pause" class="px-4 py-2 bg-orange-600 text-white rounded">⏸</button></ef-toggle-play><ef-time-display class="text-white"></ef-time-display><ef-scrubber class="flex-1"></ef-scrubber></ef-controls>
When You Don't Need ef-controls
If your controls are already descendants of the timegroup, you don't need ef-controls at all — the context flows down through the DOM naturally:
<ef-timegroup mode="contain" class="w-full h-96 relative"><ef-video src="/video.mp4" class="size-full"></ef-video><!-- No ef-controls needed — these are descendants of the timegroup --><div class="absolute bottom-0 left-0 right-0 p-4 flex gap-3"><ef-toggle-play><button slot="play">Play</button><button slot="pause">Pause</button></ef-toggle-play><ef-scrubber class="flex-1"></ef-scrubber></div></ef-timegroup>
Use ef-controls only when the controls and their target are not in an ancestor–descendant relationship.
Practical Benefits
Flexible layouts: Build interfaces where the video preview is in the main content area and controls are in a sticky footer, sidebar, or modal dialog. Position controls based on your design needs, not DOM structure.
Portal-based rendering: Works with React portals, Vue teleports, and similar rendering patterns. Controls rendered outside the normal component tree remain connected to the preview through ef-controls.
Multi-location control: Build applications where the same video is controlled from multiple places — for example, both an inline control bar and a floating mini-player. All sets stay synchronized.
Separation of concerns: Keep preview and control markup separate in your component structure while maintaining tight synchronization. This improves code organization and makes layouts easier to reason about.