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.
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.
Two sizes (Small / Large) and two contrast modes (Neutral / Contrast). For any other icon-only control — close, share, mute — reach for Icon Button instead.
When to use it (and when not to)
Section titled “When to use it (and when not to)”Use Media Buttons for:
- The primary play / pause affordance in a media surface (hero video, gallery, audio player).
- The same control with a progress ring tracking video or audio playback time.
Avoid Media Buttons 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 label — pair Icon Button with a tooltip, or use the standard Button.
Quick summary of accessibility & consistency
Section titled “Quick summary of accessibility & consistency”- Always include an accessible name (e.g.
aria-label="Play",aria-label="Pause"). Update it when the state toggles. - The icon must contrast at least 3:1 against the button fill (WCAG 1.4.11 non-text contrast).
- Use the Contrast mode when placed on light or busy media; default Neutral works on dark imagery.
Properties
Section titled “Properties”Large
Small
Contrast
Section titled “Contrast”Pick the contrast mode that matches the brightness of the media behind the button — see Schemes for how .is-contrast propagates.
Neutral
buttons-media-button-neutral-rested-fill-color#f5f5f5buttons-media-button-neutral-rested-fill-color#282830buttons-media-button-neutral-rested-foreground-color#333333buttons-media-button-neutral-rested-foreground-color#ffffffContrast
buttons-media-button-contrast-rested-fill-colorrgba(0, 0, 0, 0.2)buttons-media-button-contrast-rested-fill-color#ffffffbuttons-media-button-contrast-rested-foreground-color#ffffffbuttons-media-button-contrast-rested-foreground-color#15151bLexus brand override
Section titled “Lexus brand override”Lexus media buttons replace the solid 1px border with a copper gradient. This deviation is implemented via html[data-brand='lexus']; Figma represents it as a flat copper placeholder because multi-brand variables can’t carry gradients.
Rested / Default
linear-gradient(90deg, --tng-color-copper-400 0%, --tng-color-copper-500 100%)
Hover / Active
linear-gradient(90deg, --tng-color-copper-500 0%, --tng-color-copper-400 50%, --tng-color-copper-500 100%)
Layout and spacing
Section titled “Layout and spacing”Styling
Section titled “Styling”Colors
Section titled “Colors”| Name | Applied as | Applied to |
|---|---|---|
buttons-media-button-neutral-rested-fill-color | Background color | Media button |
buttons-media-button-neutral-rested-fill-color | Background color | Media button |
buttons-media-button-neutral-rested-border-color | Border color | Media button |
buttons-media-button-neutral-rested-border-color | Border color | Media button |
buttons-media-button-neutral-rested-foreground-color | Icon color | Media button |
buttons-media-button-neutral-rested-foreground-color | Icon color | Media button |
buttons-media-button-neutral-hover-fill-color | Background color | Media button |
buttons-media-button-neutral-hover-fill-color | Background color | Media button |
buttons-media-button-neutral-hover-border-color | Border color | Media button |
buttons-media-button-neutral-hover-border-color | Border color | Media button |
buttons-media-button-neutral-active-fill-color | Background color | Media button |
buttons-media-button-neutral-active-fill-color | Background color | Media button |
buttons-media-button-neutral-active-border-color | Border color | Media button |
buttons-media-button-neutral-active-border-color | Border color | Media button |
buttons-media-button-neutral-disabled-fill-color | Background color | Media button |
buttons-media-button-neutral-disabled-fill-color | Background color | Media button |
buttons-media-button-neutral-disabled-foreground-color | Icon color | Media button |
buttons-media-button-neutral-disabled-foreground-color | Icon color | Media button |
buttons-media-button-contrast-rested-fill-color | Background color | Media button |
buttons-media-button-contrast-rested-fill-color | Background color | Media button |
buttons-media-button-contrast-rested-border-color | Border color | Media button |
buttons-media-button-contrast-rested-border-color | Border color | Media button |
buttons-media-button-contrast-rested-foreground-color | Icon color | Media button |
buttons-media-button-contrast-rested-foreground-color | Icon color | Media button |
color-border-focus-neutral-default | Focus ring color | Media button |
color-border-focus-neutral-default | Focus ring color | Media button |
color-border-focus-contrast-default | Focus ring color | Media button |
color-border-focus-contrast-default | Focus ring color | Media button |
color-copper-400 | Border gradient (Lexus) | Media button |
color-copper-400 | Border gradient (Lexus) | Media button |
color-copper-500 | Border gradient (Lexus) | Media button |
color-copper-500 | Border gradient (Lexus) | Media button |
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.
Accessibility
Section titled “Accessibility”Media buttons are icon-only play / pause controls placed over media surfaces — busy imagery, video frames, gradients. There’s no visible label to anchor meaning, and the surrounding contrast is unpredictable. Get the accessible name, the focus ring and the contrast right.
For Design
Section titled “For Design”Minimum touch area
Section titled “Minimum touch area”Both production sizes already meet WCAG 2.5.5 / EN 301549 touch-target guidance — Small is 40 × 40 (above the 24 × 24 minimum) and Large is 64 × 64 (above the 44 × 44 recommended). Don’t shrink the visible footprint below those values; if a denser layout is needed, switch to Icon Button instead.
Contrast over media
Section titled “Contrast over media”- Default to Neutral on dark imagery and Contrast on light imagery.
- The icon must remain at least 3:1 against the button fill (WCAG 1.4.11 non-text contrast).
- If the underlying media is unpredictable, add a subtle scrim behind the button so it sits on a stable, high-contrast layer.
- Pick the contrast mode that matches the brightness of the media behind the button.
- Add a scrim or gradient behind the button when imagery is variable or user-supplied.
- Keep at least one full button width of clear space between adjacent media buttons.
- Don’t place a Neutral media button on a light photograph without a scrim.
- Don’t override the focus ring colour — it’s tuned to remain visible across both modes.
- Don’t pack media buttons together more tightly than their own diameter; touch targets must not overlap.
For Dev
Section titled “For Dev”These recommendations align with WCAG 2.2 AA and EN 301549.
- Semantic markup
- Use a native
<button>. Don’t style a<div>or<a>to look like one.
- Use a native
- Accessible name
- Every media button must have an
aria-label. For the play / pause toggle, swap the label as the state changes (Play↔Pause) and mirror it onaria-pressed. - Hide the icon glyph from assistive tech with
aria-hidden="true".
- Every media button must have an
- Keyboard navigation
- Buttons must be focusable via Tab and activatable via Enter or Space.
- Never suppress the focus ring — it’s the only way keyboard users know which control is active over media.
- For player transport controls, group the cluster in a
role="toolbar"container with a roving tabindex and Arrow-key navigation.
- Progress ring announcements
- The
--tng-media-button-progressring is decorative. Don’t announce it directly on the button. Pair it with anaria-live="polite"region that surfaces meaningful milestones (e.g. “10% played”, “Ended”) if you need spoken feedback. - Use
aria-busy="true"while the player is buffering.
- The
- Testing and validation
- Validate with Axe / Lighthouse / Accessibility Insights.
- Verify focus visibility in both Neutral and Contrast modes, on representative imagery.
- Test manually with a keyboard and a screen reader to confirm the play / pause label flips with the state.