A <brio-media> element wraps a child <audio> or
<video>. Keep the native controls attribute on the child:
without JavaScript the browser renders its own controls, which is the fallback. When the
element upgrades, it strips controls and inserts its own transport bar below
the media (e.g., play, timeline, time display, mute, volume, and fullscreen on video). The
bar's buttons are media-specific atoms rather than .button instances; transport
controls report continuous playback state, and form-button chrome would read as the wrong
rhetoric.
The overlay attribute (video only) floats the controls over the bottom of the
picture with a gradient scrim and fades them out while playing and idle. Audio ignores it.
no-volume and no-fullscreen on the host suppress those controls.
Examples
Video
<brio-media>
<video
poster="../../media/video-test.png"
controls
src="../../media/video-test.mp4"
></video>
</brio-media>
Overlay controls
<brio-media overlay>
<video
poster="../../media/video-test.png"
controls
src="../../media/video-test.mp4"
></video>
</brio-media>
Audio
<brio-media>
<audio controls src="../../media/audio-test.mp3"></audio>
</brio-media>
Source comments
Audio / Video -- the <brio-media> element wraps a child <audio>
or <video>. Without JS, the child media keeps its native
`controls` attribute and the browser renders UA chrome -- a
functional, no-JS-required fallback.
On JS upgrade, brio-media strips the native `controls` and
inserts its own controls bar. The buttons and ranges in the bar
are media-specific atoms (.brio-media-play, .brio-media-timeline,
etc.) -- not .button / .input. Transport controls communicate
continuous playback state, not discrete-input user commands;
borrowing form chrome would suggest the wrong kind of control.
Each control styles itself directly off the host token set.
Tier-1 sizing for bare <audio>/<video> outside <brio-media>
lives in reset.css -- inline-size:100% on audio (UA intrinsic
widths vary too widely to leave at default), max-width:100%
on video (already in the existing media-elements rule).
Markup:
<brio-media>
<video src="..." poster="..." controls></video>
</brio-media>
<brio-media>
<audio src="..." controls></audio>
</brio-media>
Overlay variant -- `overlay` attribute on the host (video only).
Controls float absolutely on the bottom of the media with a gradient
scrim, recolor light-on-dark for legibility regardless of theme,
and the play button enlarges into a circular shape. JS adds an
`idle` attribute to the host while playing + idle to fade the
bar. Audio silently ignores `overlay` -- there's no surface to
fade against and the inline transport already serves the same
intent.
Author opt-out attributes on the host: `no-volume` (suppresses
the mute button + volume slider), `no-fullscreen` (suppresses
the fullscreen button on video). brio-media is a custom element
and owns its attribute namespace, so all four modifiers --
`overlay`, `idle`, `no-volume`, `no-fullscreen` -- are bare
attributes rather than data-* prefixed.
Range progress fill technique:
WebKit has no native filled-portion pseudo. We render the fill
as a linear-gradient on the track, with a CSS custom property
`--value` (0-100) set by JS on every value change. Firefox uses
the native `::-moz-range-progress` pseudo, which the browser
advances automatically -- the JS-set `--value` is harmless there.
Reference
Source: src/css/extras/brio-media.css
Applies to
brio-media
Tokens
| Token | Default | Description |
|---|---|---|
--brio-media-bg | var(--color-surface) | Background color for the host and controls bar |
--brio-media-border-color | var(--color-border-subtle) | Border color for the host container |
--brio-media-border-width | var(--border-width-default) | Border width for the host frame and the controls-bar separator |
--brio-media-radius | var(--radius-md) | Corner radius for the host container |
--brio-media-controls-color | var(--color-text) | Icon and text color for transport controls |
--brio-media-controls-bg-hover | color-mix(in oklch, currentColor 10%, transparent) | Hover background for transport buttons |
--brio-media-time-color | var(--color-text-muted) | Color for the time display |
--brio-media-track-height | 0.25rem | Height of the range track for timeline and volume |
--brio-media-track-bg | var(--color-fill) | Background color for the unfilled portion of range tracks |
--brio-media-track-fill | var(--color-primary) | Color for the filled portion of range tracks |
--brio-media-thumb-size | 0.875rem | Diameter of the range thumb |
--brio-media-thumb-bg | var(--color-primary) | Fill color for the range thumb |
Related: Brio Media Controls
Transport controls bar for media playback
Source: src/css/extras/brio-media.css
Applies to
.brio-media-controls
Slots
| Slot | Description |
|---|---|
.brio-media-time | Elapsed and total duration display |
.brio-media-timeline | Timeline scrubber range input |
.brio-media-volume | Volume slider range input |
Tokens
| Token | Default | Description |
|---|---|---|
--brio-media-overlay-scrim | oklch(0% 0 0 / 0.85) | Scrim opacity at the bottom of the overlay gradient |
--brio-media-overlay-fade | opacity var(--duration-normal) var(--ease-in-out) | Show and hide opacity transition for overlay controls |
--brio-media-overlay-play-size | 2.5rem | Diameter of the enlarged play button in overlay mode |