Video
tier 2 — classes
A frame around the platform's own player, and a video as a tile in a grid. The
<video controls> stays native — play, seek, volume, captions, full
screen and the keyboard are the browser's and none of it is redrawn. The library adds what the
element does not carry: the title, the length, the actions and the transcript. Sound on its own
is Audio.
<source> and <track> go inside the element.
A player
.media-player is a <figure>: the <video>
on a black letterbox at 16:9 before a byte has loaded, so the page does not jump when the
metadata arrives, then the .media-player-body as its caption — the
.media-player-title and a .file-meta line in
.media-player-text, and .media-player-actions. Give the element its
width, height and a poster, and
preload="metadata" or "none" in a page of several.
Captions are a <track kind="captions">, which is what puts the captions
button in the native controls; a toggle of your own beside the title is a button with
aria-pressed that sets the track's mode.
.media-transcript is the same words under the player, readable and searchable
without playing: a <details>, so it opens with no script, holding an
<ol> of lines. Each .media-transcript-time is a button the app
wires to currentTime, and the line being spoken carries
aria-current="true", set from the element's timeupdate.
Transcript
- Before the truck backs in, check the dock plate is down and the bay light is green.
- Scan the delivery note first, not the pallet — the note is what the order is matched against.
- Count the cartons on the outside layer and compare with the quantity on the note.
- Anything crushed or wet gets a photo before the wrap comes off.
- Sign on the scanner, and the note is filed against the order on its own.
The poster is a photograph by Ruchindra Gunasekara on Unsplash. The demo has no source: the
app's <source> and <track kind="captions"> go inside the
<video>.
<figure class="media-player" style="max-width:640px">
<video controls preload="none" width="1280" height="720"
poster="https://images.unsplash.com/photo-1553413077-190dd305871c?w=1280&h=720&fit=crop&q=70&auto=format"
aria-label="Receiving a pallet at dock 4">
</video>
<figcaption class="media-player-body">
<span class="media-player-text">
<strong class="media-player-title">Receiving a pallet at dock 4</strong>
<span class="file-meta"><span>4:32</span><time datetime="2026-09-12">12 Sep 2026</time><span>Alex Fischer</span></span>
</span>
<span class="media-player-actions">
<button class="btn btn-ghost btn-icon btn-sm" type="button" aria-pressed="true" aria-label="Captions"><i class="ri-closed-captioning-line"></i></button>
<a class="btn btn-ghost btn-icon btn-sm" href="#" download aria-label="Download the video, 86 MB"><i class="ri-download-2-line"></i></a>
</span>
</figcaption>
<details class="media-transcript">
<summary>Transcript</summary>
<ol>
<li><button class="media-transcript-time" type="button" aria-label="Play from 0:00">0:00</button><span>Before the truck backs in, check the dock plate is down and the bay light is green.</span></li>
<li aria-current="true"><button class="media-transcript-time" type="button" aria-label="Play from 0:18">0:18</button><span>Scan the delivery note first, not the pallet — the note is what the order is matched against.</span></li>
<li><button class="media-transcript-time" type="button" aria-label="Play from 0:41">0:41</button><span>Count the cartons on the outside layer and compare with the quantity on the note.</span></li>
<li><button class="media-transcript-time" type="button" aria-label="Play from 1:12">1:12</button><span>Anything crushed or wet gets a photo before the wrap comes off.</span></li>
<li><button class="media-transcript-time" type="button" aria-label="Play from 1:40">1:40</button><span>Sign on the scanner, and the note is filed against the order on its own.</span></li>
</ol>
</details>
</figure>
<p class="form-hint" style="max-width:640px">
The poster is a photograph by Ruchindra Gunasekara on Unsplash. The demo has no source: the
app's <code><source></code> and <code><track kind="captions"></code> go inside the
<code><video></code>.
</p>
Video tiles
A library of videos is a .file-grid with
--wide, which gives each tile the room and a 16:9 thumbnail. On the thumbnail,
.file-tile-play says it plays and .file-tile-badge in the corner is
its length, both on a scrim that reads over any picture; the words “Length” are
there for a screen reader. The rest is the file tile: the link that opens it, the meta line
and the menu.
Length 4:32 Receiving a pallet at dock 4
Length 12:05 Loading high racking safely
Length 1:48 The end-of-shift handover
Photographs by Ruchindra Gunasekara, CHUTTERSNAP and Marcin Jozwiak on Unsplash.
<div style="max-width:820px">
<ul class="file-grid file-grid--wide" aria-label="Training videos">
<li class="file-tile">
<a class="file-tile-open" href="#">
<span class="file-tile-thumb">
<img src="https://images.unsplash.com/photo-1553413077-190dd305871c?w=480&h=270&fit=crop&q=70&auto=format"
width="480" height="270" loading="lazy" alt="" />
<i class="ri-play-fill file-tile-play" aria-hidden="true"></i>
<span class="file-tile-badge"><span class="visually-hidden">Length </span>4:32</span>
</span>
<span class="file-name">Receiving a pallet at dock 4</span>
</a>
<span class="file-meta"><time datetime="2026-09-12">12 Sep</time><span>Alex Fischer</span></span>
<button class="btn btn-ghost btn-icon btn-sm file-tile-menu" type="button" aria-label="Actions for Receiving a pallet at dock 4"><i class="ri-more-2-line"></i></button>
</li>
<li class="file-tile">
<a class="file-tile-open" href="#">
<span class="file-tile-thumb">
<img src="https://images.unsplash.com/photo-1587293852726-70cdb56c2866?w=480&h=270&fit=crop&q=70&auto=format"
width="480" height="270" loading="lazy" alt="" />
<i class="ri-play-fill file-tile-play" aria-hidden="true"></i>
<span class="file-tile-badge"><span class="visually-hidden">Length </span>12:05</span>
</span>
<span class="file-name">Loading high racking safely</span>
</a>
<span class="file-meta"><time datetime="2026-09-03">3 Sep</time><span>Priya Nair</span></span>
<button class="btn btn-ghost btn-icon btn-sm file-tile-menu" type="button" aria-label="Actions for Loading high racking safely"><i class="ri-more-2-line"></i></button>
</li>
<li class="file-tile">
<a class="file-tile-open" href="#">
<span class="file-tile-thumb">
<img src="https://images.unsplash.com/photo-1565891741441-64926e441838?w=480&h=270&fit=crop&q=70&auto=format"
width="480" height="270" loading="lazy" alt="" />
<i class="ri-play-fill file-tile-play" aria-hidden="true"></i>
<span class="file-tile-badge"><span class="visually-hidden">Length </span>1:48</span>
</span>
<span class="file-name">The end-of-shift handover</span>
</a>
<span class="file-meta"><time datetime="2026-08-28">28 Aug</time><span>Tomás Ferreira</span></span>
<button class="btn btn-ghost btn-icon btn-sm file-tile-menu" type="button" aria-label="Actions for The end-of-shift handover"><i class="ri-more-2-line"></i></button>
</li>
</ul>
<p class="form-hint sedna-mb-0">Photographs by Ruchindra Gunasekara, CHUTTERSNAP and Marcin Jozwiak on Unsplash.</p>
</div>