Media Button
Media buttons are circular play / pause controls for video and audio players. They sit on top of imagery or video frames as the primary play affordance, optionally wrapped in a progress ring that traces playback time.
Ready to use
Media buttons are circular play / pause controls for video and audio players. The icon glyph fills the button, and an optional progress ring traces playback time around the edge. For any other icon-only control — close, share, mute — reach for Icon Button instead.
Anatomy
Section titled “Anatomy”- Media button
- Progress bar
- Play icon
When to use it
Section titled “When to use it”Use the Media Button when:
- It’s the primary play / pause affordance on a media surface (hero video, gallery, audio player).
- The same control needs a progress ring tracking video or audio playback time.
Avoid the Media Button when:
- The action isn’t play / pause — even close-on-a-poster or mute-on-an-asset belongs to Icon Button.
- The control sits in regular page chrome — Icon Button is the right primitive there.
- The icon needs a visible label — pair Icon Button with a tooltip, or use a standard Button.
Properties
Section titled “Properties”Size
Two sizes: Large (64×64) for hero media and Small (40×40) for inline controls. The icon glyph scales with the container; padding stays at zero so the icon reads as the surface.
Contrast
Two modes: Neutral for use on dark imagery, and Contrast for light or busy backgrounds. The contrast set keeps the icon at the 3:1 non-text contrast required by WCAG.
Lexus gradient treatment
For Lexus the resting button keeps its flat grey border. The copper gradient shows in two coincident places: the circular progress arc, and the button ring on hover/active — the state where the border already turns copper. Both share one outer edge, so a part-swept arc reads as a single ring, and hover only recolours the ring rather than thickening it. They reuse the shared border-gradient tokens (--tng-color-border-gradient-*) the rectangular gradient borders draw from. Implemented via html[data-brand='lexus']; Figma shows a flat copper placeholder because multi-brand variables can’t carry gradients. Because the ring is circular, inset, and drawn with outline (not border), the gradient is a linear-gradient masked into an annulus rather than the background-clip: border-area treatment the rectangular buttons use.
Platform considerations
Section titled “Platform considerations”Desktop
The large size suits hero videos and gallery players where the control needs to read from a distance. Keep the button centred over the media so it stays predictable.
Tablet
Same sizing as desktop. The 64 px large size still feels right on tablets; only step down to small inside compact playlists or thumbnail rows.
Mobile
Stick with the large size for primary playback. Small is reserved for inline rows where space is tight, and even then the 40 px diameter clears the touch target minimum.
Best practices
Section titled “Best practices”Reserve the Media Button for play / pause — its visual language only makes sense in that context.
Do
Always include an accessible name (aria-label="Play", aria-label="Pause") and update it when the state toggles, pick the contrast mode that matches the brightness of the media beneath, and let the icon’s 3:1 contrast ratio carry the visual cue.
Don't
Don’t repurpose the Media Button for non-playback icons, don’t leave the aria-label stale after the state flips, and don’t drop below the small size — the icon needs the container to read at a glance.
Content guidelines
Section titled “Content guidelines”The button is icon-only, so the accessible name has to do all the work. Use a single-verb label — “Play”, “Pause” — and switch it when the state toggles. Keep wording identical across the product so screen-reader users recognise the control no matter where it sits.
A circular play / pause control. The button’s outline acts as the visible ring; drop a <div class="tng-media-button-progress"> inside to overlay a 360° progress arc on top.
<button class="tng-media-button" aria-label="Play"> <i class="tng-icon icon-player" aria-hidden="true"></i></button>Elements
Section titled “Elements”Progress ring
Section titled “Progress ring”The progress ring carries role="progressbar" plus aria-valuemin / aria-valuemax / aria-valuenow for screen readers, and the --tng-media-button-progress custom property (a number between 0 and 1) drives the visual fill. Keep aria-valuenow (0–100) and the custom property (0–1) updated together so visuals and announcements stay in sync.
<button class="tng-media-button" aria-label="Play"> <div class="tng-media-button-progress" role="progressbar" aria-valuemin="0" aria-valuemax="100" aria-valuenow="0" style="--tng-media-button-progress: 0" ></div> <i class="tng-icon icon-player" aria-hidden="true"></i></button><button class="tng-media-button" aria-label="Pause"> <div class="tng-media-button-progress" role="progressbar" aria-valuemin="0" aria-valuemax="100" aria-valuenow="42" style="--tng-media-button-progress: 0.42" ></div> <i class="tng-icon icon-ui-pause" aria-hidden="true"></i></button><button class="tng-media-button" aria-label="Replay"> <div class="tng-media-button-progress" role="progressbar" aria-valuemin="0" aria-valuemax="100" aria-valuenow="100" style="--tng-media-button-progress: 1" ></div> <i class="tng-icon icon-player" aria-hidden="true"></i></button>The default size is lg (64 × 64). Use is-sm for the compact 40 × 40 variant — typically secondary controls in a player toolbar.
<button class="tng-media-button is-sm" aria-label="Play"> <i class="tng-icon icon-player" aria-hidden="true"></i></button><button class="tng-media-button is-lg" aria-label="Play"> <i class="tng-icon icon-player" aria-hidden="true"></i></button>States
Section titled “States”Disabled is expressed via the native disabled attribute. Hover, active and focus states are driven by the browser and styled by the design system — no extra classes needed.
<button class="tng-media-button" aria-label="Play"> <i class="tng-icon icon-player" aria-hidden="true"></i></button><button class="tng-media-button" disabled aria-label="Play"> <i class="tng-icon icon-player" aria-hidden="true"></i></button>Recipes
Section titled “Recipes”Video player
Section titled “Video player”Wires the media button to a <video> element. The toggle flips between play and pause icons, the aria-label and aria-pressed track the playback state, and --tng-media-button-progress is updated from a requestAnimationFrame loop scoped to playback so the ring traces playback time.
Press play to see the progress ring trace playback time.
The same wiring works for any source — <audio>, custom playback engines, HLS streams. The contract is “set the number on the progress element”; everything else is decoupled.
A media button is a native <button> — usually icon-only and placed over imagery — that controls media playback, so it follows the WAI-ARIA Button pattern and meets WCAG 2.1 AA. With no visible label and an unpredictable surface behind it, an accessible media button hinges on three things: an explicit accessible name, an icon hidden from assistive technology, and enough contrast — for both the icon and its focus ring — over whatever media sits beneath it.
For designers
Section titled “For designers”- The icon must keep at least 3:1 contrast against the button fill on every background it can land on — busy imagery, video frames, gradients. Where the media is variable or user-supplied, add a scrim behind the button so it sits on a stable, high-contrast layer. Source: WCAG 1.4.11 Non-text Contrast.
- Playback state must never be conveyed by colour alone — the play and pause icons already differ in shape; keep that distinction rather than relying on a colour swap to tell the two apart. Source: WCAG 1.4.1 Use of Color.
- The focus ring must stay visible and meet contrast over both light and dark media — it’s the only cue a keyboard user gets for a control with no label. Don’t override its colour; it’s tuned to remain visible across both contrast modes. Source: WCAG 2.4.7 Focus Visible.
- Use semantic colour tokens (e.g.
foreground/neutral/default) rather than fixed values, so the icon and ring adapt correctly across themes, contrast modes, and surfaces. - Both production sizes clear the WCAG AA target-size minimum — Small is 40 × 40 and Large is 64 × 64, well above the 24 × 24 floor. Don’t shrink the visible footprint below Small; if a denser layout needs it, switch to Icon Button instead. Keep at least one button-width of clear space between adjacent media buttons so their targets never overlap. Source: WCAG 2.5.8 Target Size (Minimum).
For developers
Section titled “For developers”A media button is a native <button>, so it is focusable and operable from the keyboard (Enter / Space) with no extra wiring — never style a <div> or <a> to stand in for it. Because it is icon-only, its accessible name has to come from an aria-label, and a play / pause or mute / unmute control exposes its on/off state with aria-pressed; the decorative glyph stays out of the accessibility tree. When a progress ring is present, its role="progressbar" semantics are documented on the Develop tab — surface meaningful playback milestones through an aria-live region rather than announcing the ring itself. The per-element roles, names, and states are in the Labelling elements section below.
Labelling elements
Section titled “Labelling elements”Give every element the role, name, and state assistive technology needs.
Button
The native <button> carries its own role and keyboard operation. Because it has no visible text, give it an accessible name with aria-label ("Play", "Pause", "Mute") and keep that name matching the action the button will perform.
Icon
The glyph is decorative — mark it aria-hidden="true" so it isn’t announced separately. The button’s aria-label is the single source of the accessible name.
Toggle state
For play / pause and mute / unmute, expose the two-state nature with aria-pressed and flip the aria-label in step with it, so sighted and screen-reader users read the same current state.
Disabled
Use the native disabled attribute — not an .is-disabled class — so the control is announced as unavailable and skipped in the tab order.
Source: WCAG 4.1.2 Name, Role, Value.