A <video> wrapper with no native controls. Build your own toolbar by slotting in the bundled kc-vid-* controls (or your own custom elements) — exactly the same slotted-control pattern used by Table, Pagination, and Markdown Editor. Use the controls attribute for a preconfigured toolbar, or mix and match.
Because there's no native <input type="range"> seek bar, there's nothing for the browser to give keyboard focus to when the user drags it — so a page-level keydown listener (e.g. space bar to play/pause) keeps working after the user interacts with the player. See Page-Level Keyboard Shortcut below.
With no controls attribute and nothing slotted in, k-video renders just the bare video — useful when you want full control over the UI, or are driving playback entirely from your own buttons.
<k-video src="../media/evo.mp4" poster="../media/evo-mp4.png"></k-video>
Slot any combination of built-in controls directly inside <k-video>, into one of three regions: unnamed (default) slot content renders in a control bar overlaid at the bottom of the player, slot="top" renders above the video, and slot="center" renders centered over the video — the last is where <kc-vid-play-big> normally goes.
<k-video src="../media/evo.mp4" poster="../media/evo-mp4.png" style="max-width: 480px;">
<kc-vid-play-big slot="center"></kc-vid-play-big>
<kc-vid-play-pause></kc-vid-play-pause>
<kc-vid-time></kc-vid-time>
<kc-vid-seek></kc-vid-seek>
<kc-vid-duration></kc-vid-duration>
<kc-vid-volume></kc-vid-volume>
<kc-vid-loop></kc-vid-loop>
<kc-fullscreen></kc-fullscreen>
</k-video>
Use controls="minimal" for play/pause, a scrub bar, current time / duration, and fullscreen. A slot="center" big play/replay button is included by default.
<k-video src="../media/evo.mp4" poster="../media/evo-mp4.png" controls="minimal" style="max-width: 480px;"></k-video>
Use controls="normal" to add volume, playback speed, and loop on top of the minimal set.
<k-video src="../media/evo.mp4" poster="../media/evo-mp4.png" controls="normal" style="max-width: 480px;"></k-video>
Use controls="full" for everything: skip forward/backward, download, volume, speed, loop, and Picture-in-Picture. Skip duration is customizable via skip-duration (default: 10 seconds).
<k-video src="../media/evo.mp4" poster="../media/evo-mp4.png" controls="full" style="max-width: 480px;"></k-video>
Use <kc-vid-menu> to group controls into a dropdown menu, saving toolbar space. The menu button displays a customizable icon and opens a dropdown containing your slotted controls. This is useful for grouping less-frequently-used options like download, loop, or Picture-in-Picture.
<k-video src="../media/evo.mp4" poster="../media/evo-mp4.png" style="max-width: 480px;">
<kc-vid-play-big slot="center"></kc-vid-play-big>
<kc-vid-play-pause></kc-vid-play-pause>
<kc-vid-seek></kc-vid-seek>
<kc-vid-volume></kc-vid-volume>
<kc-vid-menu icon="settings">
<kc-vid-speed>Speed</kc-vid-speed>
<kc-vid-loop>Loop</kc-vid-loop>
<kc-vid-pip>Picture-in-Picture</kc-vid-pip>
<kc-vid-download>Download</kc-vid-download>
</kc-vid-menu>
<kc-fullscreen></kc-fullscreen>
</k-video>
Slot <kc-vid-time> and <kc-vid-duration> next to each other (with a literal / between them) for a familiar “1:00 / 2:25” readout.
<k-video src="../media/evo.mp4" poster="../media/evo-mp4.png" style="max-width: 480px;">
<kc-vid-play-pause></kc-vid-play-pause>
<kc-vid-seek></kc-vid-seek>
<span class="time-combo">
<kc-vid-time></kc-vid-time> / <kc-vid-duration></kc-vid-duration>
</span>
<kc-fullscreen></kc-fullscreen>
</k-video>
<kc-vid-play-big> shows a large centered play button (or a replay icon once playback ends) whenever the player is paused — the same pattern used by YouTube and most native players. Clicking anywhere on the video still toggles play/pause as usual. It's slotted into slot="center" and included by default in the minimal, normal, and full preconfigured sets (see above); it also works standalone with no other controls at all:
<k-video src="../media/evo.mp4" poster="../media/evo-mp4.png" style="max-width: 480px;">
<kc-vid-play-big slot="center"></kc-vid-play-big>
</k-video>
Double-click (or double-tap) the left half of the video to rewind, or the right half to skip forward — the same convention used by YouTube and most native players. A brief icon flashes on the side that was clicked. By default, skips 10 seconds; customize both the skip duration and flash indicator duration with attributes. This is always on, no attribute required to enable it.
<k-video src="../media/evo.mp4" poster="../media/evo-mp4.png" controls="normal" skip-duration="5" skip-flash-ms="300"></k-video>
The control bar fades out after a couple of seconds of no mouse movement while the video is playing, and reappears instantly on mouse movement or whenever playback pauses. Customize the delay before auto-hide with the idle-delay-ms attribute. This is always on — play the video below and hold your mouse still over it.
<k-video src="../media/evo.mp4" poster="../media/evo-mp4.png" controls="normal" idle-delay-ms="5000"></k-video>
A spinner automatically appears over the video whenever the browser has to pause for buffering (the native waiting event) and disappears once playback can resume (playing / canplay) — handy on slower connections or with kempo-server's HTTP Range-backed seeking. Nothing to configure; it's driven entirely by the underlying <video> element's own buffering state.
By default k-video sizes itself to the video's own aspect ratio. If you force it into a width/height that doesn't match — a fixed-size player showing videos of varying dimensions, for example — the video letterboxes or pillarboxes to fit, centered both ways, with k-video's own black background filling the bars. It never stretches or crops the content.
<k-video src="../media/evo.mp4" poster="../media/evo-mp4.png" controls="normal" style="width: 480px; height: 200px;"></k-video>
<k-video src="../media/evo.mp4" poster="../media/evo-mp4.png" controls="normal" style="width: 200px; height: 400px;"></k-video>
Set persistent-id to remember the playback position in localStorage and resume from there next time this player loads — the same persistent-id convention used by Tabs, Accordion, and Split. Play the video below, seek somewhere, then reload this page — it picks up where you left off. The saved position clears automatically once playback reaches the end.
<k-video src="../media/evo.mp4" poster="../media/evo-mp4.png" controls="normal" persistent-id="docs-demo" style="max-width: 480px;"></k-video>
A common pattern: a global handler that maps keys to player methods. With a native <video controls>, dragging the native seek bar focuses it, and further key presses toggle that control instead of bubbling up to your page handler. k-video has no native controls to capture focus — its bundled kc-vid-seek is built on k-slider, which doesn't handle these keys, so they bubble up to document as expected. The e.target === document.body guard keeps this from firing while focus is on some other element (a text input, etc.).
<k-video id="player" src="../media/evo.mp4" poster="../media/evo-mp4.png" controls="normal"></k-video>
<script>
// Page-level shortcuts — work even after the user drags the seek bar,
// because k-video has no native <input type="range"> to steal focus.
document.addEventListener('keydown', (e) => {
if (e.target !== document.body) return;
const player = document.getElementById('player');
switch (e.code) {
case 'Space':
case 'KeyK':
e.preventDefault();
player.togglePlayPause();
break;
case 'ArrowLeft':
e.preventDefault();
player.skip(-5);
break;
case 'ArrowRight':
e.preventDefault();
player.skip(5);
break;
case 'ArrowUp':
e.preventDefault();
player.setVolume(player.volume + 0.1);
break;
case 'ArrowDown':
e.preventDefault();
player.setVolume(player.volume - 0.1);
break;
case 'KeyM':
player.toggleMute();
break;
case 'KeyF':
player.toggleFullscreen();
break;
}
});
</script>
new Video();
Import the main component. When using controls, import each control individually or use the controls attribute, which auto-imports all required control modules.
| Property / Attribute | Type | Default | Description |
|---|---|---|---|
src | String | '' | URL of the video source. |
poster | String | '' | Image shown before playback starts. |
preload | String | 'metadata' | Native preload hint: 'none', 'metadata', or 'auto'. |
autoplay | Boolean | false | Attempts to play automatically once a source is available (most browsers require muted as well). |
loop | Boolean | false | Replays from the start when playback ends. Toggling fires loop-changed. |
muted | Boolean | false | Mutes audio output. |
playsinline | Boolean | true | Plays inline on mobile browsers instead of forcing fullscreen. |
crossorigin | String | '' | Native crossorigin passthrough ('anonymous' / 'use-credentials'). |
downloadName / download-name | String | '' | Suggested filename used by download(). |
volume | Number | 1 | Playback volume from 0 to 1. Prefer setVolume(), which also handles unmuting. |
playbackRate / playback-rate | Number | 1 | Playback speed multiplier. |
fullscreen | Boolean | false | True while the player is the fullscreen element. Read-only in practice — use the fullscreen methods to change it. |
pictureInPicture / picture-in-picture | Boolean | false | True while the player is in Picture-in-Picture. Read-only in practice — use the Picture-in-Picture methods to change it. |
controls | String | '' | Preconfigured control set: 'minimal', 'normal', 'full', or 'none'/empty for none. |
persistentId / persistent-id | String | null | When set, remembers currentTime in localStorage and restores it on load. Cleared automatically when playback reaches the end. |
idleDelayMs / idle-delay-ms | Number | 2500 | Milliseconds before control bar fades out during playback. Set to 0 to disable auto-hide. |
skipFlashMs / skip-flash-ms | Number | 600 | Milliseconds to display the skip indicator after double-clicking. |
skipDuration / skip-duration | Number | 10 | Seconds to skip forward or backward on double-click. |
currentTime | Number | 0 | Current playback position in seconds. Get/set directly, or use seek(). |
duration | Number | 0 | Total duration in seconds. Read-only. |
paused | Boolean | true | Whether playback is paused. Read-only. |
ended | Boolean | false | Whether playback has reached the end. Read-only. |
| Method | Description |
|---|---|
play() | Starts (or resumes) playback. Chainable; swallows the rejected promise some browsers return when playback is blocked. |
pause() | Pauses playback. Chainable. |
togglePlayPause() | Plays if paused/ended, otherwise pauses. This is what <kc-vid-play-pause> calls. |
seek(time) | Jumps to time seconds, clamped to [0, duration]. |
skip(seconds) | Seeks relative to the current position (negative to rewind). |
setVolume(volume) | Sets volume, clamped to [0, 1]. Automatically unmutes when raised above 0. |
mute() | Mutes the player. |
unmute() | Unmutes the player. |
toggleMute() | Flips the muted state. |
setPlaybackRate(rate) | Sets playback speed, clamped to [0.25, 4]. |
toggleLoop() | Flips the loop property and fires loop-changed. |
enterFullscreen() | Requests fullscreen on the <k-video> element itself (not the bare <video>) so slotted controls stay visible. |
exitFullscreen() | Exits fullscreen if this player is the fullscreen element. |
toggleFullscreen() | Enters or exits fullscreen based on current state. |
enterPictureInPicture() | Requests Picture-in-Picture on the underlying <video> element. |
exitPictureInPicture() | Exits Picture-in-Picture if this player is the active element. |
togglePictureInPicture() | Enters or exits Picture-in-Picture based on current state. |
download() | Triggers a browser download of src using a temporary anchor, named via downloadName when set. |
| Event | Detail | Description |
|---|---|---|
fullscreen-changed | { fullscreen } | Fired when entering or exiting fullscreen. |
picture-in-picture-changed | { pictureInPicture } | Fired when entering or exiting Picture-in-Picture. |
loop-changed | { loop } | Fired when the loop property changes. |
restored | { time } | Fired once, when a saved position is restored from persistent-id. |
play / pause / playing / ended | — | Forwarded directly from the underlying <video> element. |
timeupdate | — | Forwarded from the underlying video; fires frequently during playback. |
durationchange / loadedmetadata | — | Forwarded once media metadata is available — useful for reading duration. |
volumechange / ratechange | — | Forwarded when volume/mute or playback rate change, whether from your code or browser policy (e.g. forced autoplay muting). |
seeking / seeked / waiting / progress / canplay / error | — | Forwarded as-is from the underlying video element. |
enterpictureinpicture / leavepictureinpicture | — | Forwarded as-is from the underlying video element. |
Each control is a separate custom element that finds its parent <k-video> and wires up automatically. Import only the controls you need.
| Tag | Import | Description |
|---|---|---|
<kc-vid-play-pause> | controls/VidPlayPause.js | Toggles playback. Shows a play or pause icon based on host.paused. |
<kc-vid-play-big> | controls/VidPlayBig.js | Large centered play/replay button, only rendered while paused. Belongs in slot="center". |
<kc-vid-seek> | controls/VidSeek.js | Scrub bar built on k-slider. Grows to fill available space in the toolbar. |
<kc-vid-time> | controls/VidTime.js | Current playback position, formatted as m:ss (or h:mm:ss). |
<kc-vid-duration> | controls/VidDuration.js | Total duration, same formatting as kc-vid-time. Slot them next to each other for a "1:00 / 2:25" readout. |
<kc-vid-mute> | controls/VidMute.js | A plain mute/unmute toggle button with no popup. |
<kc-vid-volume> | controls/VidVolume.js | Mute button that reveals a popup vertical volume slider on hover, like a native player. |
<kc-vid-speed> | controls/VidSpeed.js | A playback-rate select (0.25x–2x). |
<kc-vid-loop> | controls/VidLoop.js | Toggles loop. Reflects an active attribute while looping is enabled. |
<kc-vid-download> | controls/VidDownload.js | Downloads the current src. |
<kc-vid-skip-back> / <kc-vid-skip-forward> | controls/VidSkipBack.js / VidSkipForward.js | Jump 10 seconds backward/forward. |
<kc-vid-pip> | controls/VidPip.js | Toggles Picture-in-Picture. Reflects an active attribute while in Picture-in-Picture. |
<kc-vid-menu> | controls/VidMenu.js | Menu button that opens a dropdown containing slotted child controls. Customize the icon with the icon attribute (default: more_vert). Useful for grouping less-common controls to save toolbar space. |
<kc-fullscreen> | controls/controls/Fullscreen.js | The same generic fullscreen control used by other kempo components — works with k-video without modification. |