Media

Custom audio and video media styles that work with native audio and video elements. 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.

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

TokenDefaultDescription
--brio-media-bgvar(--color-surface)Background color for the host and controls bar
--brio-media-border-colorvar(--color-border-subtle)Border color for the host container
--brio-media-border-widthvar(--border-width-default)Border width for the host frame and the controls-bar separator
--brio-media-radiusvar(--radius-md)Corner radius for the host container
--brio-media-controls-colorvar(--color-text)Icon and text color for transport controls
--brio-media-controls-bg-hovercolor-mix(in oklch, currentColor 10%, transparent)Hover background for transport buttons
--brio-media-time-colorvar(--color-text-muted)Color for the time display
--brio-media-track-height0.25remHeight of the range track for timeline and volume
--brio-media-track-bgvar(--color-fill)Background color for the unfilled portion of range tracks
--brio-media-track-fillvar(--color-primary)Color for the filled portion of range tracks
--brio-media-thumb-size0.875remDiameter of the range thumb
--brio-media-thumb-bgvar(--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

SlotDescription
.brio-media-timeElapsed and total duration display
.brio-media-timelineTimeline scrubber range input
.brio-media-volumeVolume slider range input

Tokens

TokenDefaultDescription
--brio-media-overlay-scrimoklch(0% 0 0 / 0.85)Scrim opacity at the bottom of the overlay gradient
--brio-media-overlay-fadeopacity var(--duration-normal) var(--ease-in-out)Show and hide opacity transition for overlay controls
--brio-media-overlay-play-size2.5remDiameter of the enlarged play button in overlay mode