Responsive controls

To provide the best user experience across the myriad of device screen sizes and website layouts available in today’s world, it’s a good idea to modify your video player UI to adapt to different sizes.

@media queries are great, but they don’t always address the styling requirements that video players face. For example, you might be using a large screen, but if your media player is appearing in a confined layout like a fixed-width sidebar, you’ll still want the media player to present its compact view and limit the number of controls that appear.

There are two ways you can address responsive styling using Media Chrome: using breakpoints, or using container queries.

Container queries are a recent development just starting to make their way in evergreen browsers. If you need styling support for older browser versions, consider using breakpoints until container query support becomes more commonplace.

Media Chrome helps you to make changes to your video player layout at different screen sizes by providing breakpoints for common device screen widths. Let’s take a look at how you can use these breakpoints to style your video player at different widths.

Use your browser resizing controls throughout this guide to apply the responsive design changes to each example video layout.

media-controller {
  --media-primary-color: red;
}
media-controller[breakpointsm] {
  --media-primary-color: orange;
}
media-controller[breakpointmd] {
  --media-primary-color: yellow;
}
media-controller[breakpointlg] {
  --media-primary-color: green;
}
media-controller[breakpointxl] {
  --media-primary-color: blue;
}
media-controller #log::before {
  content: 'none';
  display: flex;
  color: white;
  width: 24px;
  height: 24px;
  border-radius: 100%;
  align-items: center;
  justify-content: center;
  padding: 1em;
  background-color: red;
  font-family: sans-serif;
  position: absolute;
  right: 5px;
  bottom: 5px;
}
media-controller[breakpointsm] #log::before {
  content: 'sm';
  background-color: orange;
  color: black;
}
media-controller[breakpointmd] #log::before {
  content: 'md';
  background-color: yellow;
  color: black;
}
media-controller[breakpointlg] #log::before {
  content: 'lg';
  background-color: green;
  color: white;
}
media-controller[breakpointxl] #log::before {
  content: 'xl';
  background-color: blue;
  color: white;
}

We’ve chosen a set of sensible default breakpoint values that consider many common screen sizes available in today’s market. As your video play width grows or shrinks, the different attribute values listed below will be applied to the media-controller element.

SizeValueAttribute
sm384pxbreakpointsm
md576pxbreakpointmd
lg768pxbreakpointlg
xl960pxbreakpointxl

Overriding with your own custom breakpoint values

Section titled Overriding with your own custom breakpoint values

It’s possible to override the default breakpoint values with your own values if the defaults do not meet your needs. To do this, set the breakpoints attribute on your media-controller element and pass a set of breakpoint definitions in the following format:

{breakpointName}:{breakpointValue}

<media-controller breakpoints="sm:288 md:480 lg:768 xl:1056">
  <video
    slot="media"
    src="https://stream.mux.com/A3VXy02VoUinw01pwyomEO3bHnG4P32xzV7u1j1FSzjNg/low.mp4"
    playsinline
    muted
  ></video>
  <media-play-button></media-play-button>
</media-controller>

Note that even if you only want to override one of the default values that Media Chrome defines, you should still pass the entire set of breakpoint definitions. This is because Media Chrome will override all of its internal breakpoints with your custom breakpoints if it detects that the breakpoints attribute is being used.

<!-- Overriding only the 'xl' breakpoint, but still passing all breakpoints -->
<media-controller breakpoints="sm:384 md:576 lg:768 xl:1056">
  <video
    slot="media"
    src="https://stream.mux.com/A3VXy02VoUinw01pwyomEO3bHnG4P32xzV7u1j1FSzjNg/low.mp4"
    playsinline
    muted
  ></video>
  <media-play-button></media-play-button>
</media-controller>

Listening for breakpoint changes

Section titled Listening for breakpoint changes

Whenever a breakpoint change is detected, the <media-controller> element will receive a breakpointchange event with an array of the new breakpoint values in the event’s detail payload.

document.querySelector('media-controller')
  .addEventListener('breakpointchange', (e) => {
    // e.detail might look like this: [sm, md, lg]
    // this line would log `lg`
    console.log('active breakpoint is ' + e.detail[e.detail.length - 1]);
  }
);

You might already be familiar with Media Queries in CSS. They allow you to have different CSS selectors depending on the size of the page itself. However, since we’re building a video player, that doesn’t help us much.

Depending on which browsers you need to support, Container Queries can be a better choice for styling responsive video player. These allow us to create CSS selectors that apply depending on the size of an element itself.

That’s a great use case for a responsive video player, where the dimensions of the player itself are what matters rather than the dimensions of the page that the player is on.

Since CQ is quite new, it’s not available everywhere yet. Depending on the browsers that you need to support, that may not be an issue. However, to support the most users, it’s recommended to write your styles in a progressive enhancement approach, which we will go over, and consider including the Container Queries Polyfill as well.

Example: Building a simple responsive layout with CQ

Section titled Example: Building a simple responsive layout with CQ

Let’s make a simple responsive layout. In addition to the <media-control-bar> above, we want to add some center controls.

<div class="center" slot="centered-chrome">
  <media-seek-backward-button seekoffset="15"></media-seek-backward-button>
  <media-play-button></media-play-button>
  <media-seek-forward-button seekoffset="15"></media-seek-forward-button>
</div>

When the player dimensions are small, we can hide the control bar and only show these center controls. When the player is medium sized, we can keep the control bar but hide the buttons that are available in the center controls. In a large player size, we can show just the control bar.

The combined HTML for the player should look like this:

<media-controller>
  <video
    playsinline
    slot="media"
    src="https://stream.mux.com/O6LdRc0112FEJXH00bGsN9Q31yu5EIVHTgjTKRkKtEq1k/high.mp4"
    poster="https://image.mux.com/O6LdRc0112FEJXH00bGsN9Q31yu5EIVHTgjTKRkKtEq1k/thumbnail.jpg?time=56"
  ></video>
  <div class="center" slot="centered-chrome">
    <media-seek-backward-button seekoffset="15"></media-seek-backward-button>
    <media-play-button></media-play-button>
    <media-seek-forward-button seekoffset="15"></media-seek-forward-button>
  </div>
  <media-control-bar class="bottom">
    <media-play-button></media-play-button>
    <media-seek-backward-button seekoffset="15"></media-seek-backward-button>
    <media-seek-forward-button seekoffset="15"></media-seek-forward-button>
    <media-mute-button></media-mute-button>
    <media-volume-range></media-volume-range>
    <media-time-display></media-time-display>
    <media-time-range></media-time-range>
    <media-duration-display></media-duration-display>
    <media-playback-rate-button></media-playback-rate-button>
    <media-fullscreen-button></media-fullscreen-button>
  </media-control-bar>
</media-controller>

Now, we have a working player, but the center controls are always showing up. We only want them to show up in some specific cases, so, let’s hide them by default.

.center {
  display: none;
}

This would also help for progressive enhancement, because then on browsers without CQ, we’ll have a player that only has the bottom control bar, which looks better.

Now, let’s make it responsive with container queries.

First, we need to declare the media-controller as a container

media-controller {
  container: media-chrome / inline-size;
}

This marks media-controller as a container named media-chrome should we have multiple containers that we need to refer to by name. We also tell CQ that we only care about the inline-size of this element, in this case it’s the width of the player.

For the smaller player, we want to show the center controls, since they are hidden by default, and then hide the control bar.

@container (inline-size < 420px) {
  .center {
    display: block;
  }
  media-control-bar {
    display: none;
  }
}

This tells CSS that apply display: block to the .center class if the inline-size, aka the width player, is less than 420px. This range syntax is brand new and may not be supported everywhere yet. We can use max-width instead, like so:

@container (max-width: 420px) {
  .center {
    display: block;
  }
  media-control-bar {
    display: none;
  }
}

This isn’t a big difference yet, but the new syntax will be especially nice in the medium player size.

Styling the medium player size

Section titled Styling the medium player size

For the medium player, we want to show the center controls and control bar. However, because space is a bit cramped, we’ll hide the controls available in the center controls from the control bar.

@container (420px <= inline-size <= 590px) {
  .center {
    display: block;
  }
  media-control-bar {
    display: flex;
  }
  media-control-bar media-play-button,
  media-control-bar media-seek-backward-button,
  media-control-bar media-seek-forward-button {
    display: none;
  }
}

Once again, since the new range syntax isn’t available yet, you can use min-width, max-width, and and, but it’s not quite as nice as the range syntax.

@container (min-width: 420px) and (max-width: 590px) {
  .center {
    display: block;
  }
  media-control-bar {
    display: flex;
  }
  media-control-bar media-play-button,
  media-control-bar media-seek-backward-button,
  media-control-bar media-seek-forward-button {
    display: none;
  }
}

Now, since we already have a default that hides the center controls in the cases where the player isn’t small or medium, we don’t strictly need another container query, but let’s add one for the sake of completeness.

@container (inline-size > 590px) {
  .center {
    display: none;
  }
  media-control-bar {
    display: flex;
  }
}

Together, it’ll give you a player like this. You can resize the page and see how the player responds to the page width. Or select a width with the following radio buttons.

.width-controls {
  padding-block: 1rem;
}
.width-controls label {
  padding-inline: 0.5rem 1rem;
}
.center {
  display: none;
}

#res-final {
  display: block;
  aspect-ratio: 16 / 9;
  container: media-chrome / inline-size;
}
#res-final video {
  width: 100%;
}

/* small player size */
@container (inline-size < 420px) {
  #res-final .center {
    display: block;
  }
  #res-final media-control-bar {
    display: none;
  }
}

/* medium player size */
@container (420px <= inline-size <= 590px) {
  #res-final .center {
    display: block;
  }
  #res-final media-control-bar {
    display: flex;
  }
  #res-final media-control-bar media-play-button,
  #res-final media-control-bar media-seek-backward-button,
  #res-final media-control-bar media-seek-forward-button {
    display: none;
  }
}

/* large (default) player size */
@container (inline-size > 590px) {
  #res-final .center {
    display: none;
  }
  #res-final media-control-bar {
    display: flex;
  }
}