Responsive videos

This article was first published on A List Apart (05-26-2009).

Did you ever want to resize a video on the fly, scaling it as you would an image? Using intrinsic ratios for video, you can. This technique allows browsers to determine video dimensions based on the width of their containing block.

With intrinsic dimensions, a new width triggers a new height calculation, allowing videos to resize and giving them the ability to scale the same way images do. See example one

The concept

The idea is to create a box with the proper ratio (4:3, 16:9, etc.), then make the video inside that box stretch to fit the dimensions of the box. It’s that simple.

The trick

The padding property is the magic that styles a box with an intrinsic ratio. This is because we’ll set padding in a percentage, based on the width of the containing block.

The CSS rules below illustrate how to style the parent and child to create a “magic wrapper” — a container that proportionally resizes itself depending on the width of its parent. See example two

.wrapper-with-intrinsic-ratio {
    position: relative;
    padding-bottom: 20%;
    height: 0;
}
.element-to-stretch {
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    background: teal;
}

Let’s review the declarations in each rule, starting with .wrapper-with-intrinsic-ratio.

position:relative
By declaring position:relative all child elements will position themselves in relation to this container.
padding-bottom:20%
This declaration gives the box a specific format. Using 20% for padding makes the height of the box equal to 20% of its width.
We specifically chose to use padding-bottom rather than padding-top. This is because IE5 removes the “space” created via padding-top from the flow. In other words, using padding-top:20% would create the layout we want, but the box would act like an absolutely positioned element, overlapping the next elements in the flow.
height:0
Specifying a height of 0 gives this element “layout” so that IE5 and IE6 will dimension the inner box properly. To learn more, visit “On having layout.”

(Note: because IE5 and IE6 treat width as a minimum width, you should not use width:100% as a layout trigger. This causes the box to expand to fill its container rather than respect the width we set for that container.)

Now, let’s consider each declaration within our .element-to-stretch rule.

position:absolute
This frees the element from the height boundary of its parent. This way, it can be positioned over the “padding area.”
top:0
We set top:0 to position the box near the top of its parent.
left:0
This declaration positions the box near the left side of its parent.
width:100%
Declaring width:100% makes the box stretch to fill the width of its container.
height:100%
This declaration makes the box stretch to fill the height of its container.
background:teal
We apply a color to reveal the layout of the box.

The real deal

example three uses a YouTube video (with YouTube markup), so we need to make room for the chrome. Note that the height of the chrome is static: it is 25 pixels tall, regardless of the dimensions of the video. We also change the padding value to display the video in a widescreen format (16:9).

#containingBlock {
    width: 50%;
}
.videoWrapper {
    position: relative;
    padding-bottom: 56.25%;
    padding-top: 25px;
    height: 0;
}
object,
embed {
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
}

Let’s take a closer look at our new selectors and declarations, sarting with the #containingBlock selector.

width:50%
This is just a wrapper to demonstrate resizing the video based on the viewport width. In the previous example, the containing block was the body element.

Now, let’s examine a couple of the declarations under the .videoWrapper selector.

padding-bottom:56.25%
To create a 16:9 ratio, we must divide 9 by 16 (0.5625 or 56.25%).
padding-top:25px
To avoid issues with the broken box model (IE5 or IE6 in quirks mode), we use padding-top rather than height to create room for the chrome.

Finally, we use the object, embed selectors because, while some browsers rely on object (e.g., Safari), others need embed (e.g., Firefox).

(Note: I’m using YouTube’s markup for now, but at the end of this article I’ll be using valid markup and dropping embed.)

The fix(es) for Internet Explorer

To make this work in Internet Explorer, just add an extra wrapper. (I never said it would be pretty.) See example four

#containingBlock {
	width: 50%;
}
.videoWrapper {
    position: relative;
    padding-top: 25px;
    padding-bottom: 56.25%;
    height: 0;
}
* html .videoWrapper {
    margin-bottom: 45px;
    margin-bottom: 0;
}
.videoWrapper div,
.videoWrapper embed,
.videoWrapper object {
    position:absolute;
    width: 100%;
    height: 100%;
    left: 0;
    top: 0;
}

Let’s take a closer look at our new selectors and declarations, starting with our .videoWrapper selector.

height:0
As seen in example two, IE5 and IE6 need “layout.”

Now, let’s look at our * html .videoWrapper selector. Called the “star html hack,” this selector makes sense only for IE6 and under, as only these versions parse the following declarations:

margin-bottom:45px
As seen in example two, top padding creates some issue in IE5. Here, we use an arbitrary value (that should work with various chromes) to compensate for the “space” we lose by using padding-top. This is to prevent the video from overlapping subsequent elements.
margin-bottom:0
The CSS escape notation (backslash character) within the property name acts as a filter to set a different value for IE6. IE6 “sees” this declaration, while IE5 ignores it. If you prefer using conditional comments rather than the filters above, feel free to move these declarations to IE specific style sheets or style elements in the head of the document.

Finally, the .videoWrapper div selector is the extra wrapper we need to make things work in Internet Explorer versions 5, 6, and 7.

(Note: we use .videoWrapper div, .videoWrapper embed, and .videoWrapper object {} rather than .videoWrapper * {} to prevent styling alternative content.)

The clean up

To make this solution more versatile, we remove padding-top declarations from the previous rules and associate them with classes. This way, we can easily style videos with different ratios and/or chromes. See example five

#containingBlock {
    width: 50%;
}
.videoWrapper {
    position: relative;
    height: 0;
}
* html .videoWrapper {
    margin-bottom: 45px;
    margin-bottom: 0;
}
.videoWrapper div,
.videoWrapper embed,
.videoWrapper object {
    position: absolute;
    width: 100%;
    height: 100%;
    left: 0;
    top: 0;
}
.wideScreen {
    padding-bottom: 56.25%;
}
.fourBYthree {
    padding-bottom: 75%;
}
.chrome_25 {
    padding-top: 25px;
}
.chrome_35 {
    padding-top: 35px;
}

Let’s take a look at our new classes, starting with .wideScreen.

.wideScreen
We use this class to style div.videoWrapper with a 16:9 ratio.
.fourBYthree
We use this class to style div.videoWrapper with a 4:3 ratio.
.chrome_25
This class makes room for chrome that is 25 pixels tall.
.chrome_35
This class makes room for chrome that is 35 pixels tall.

The validation issue

When it comes to video, supporting web standards is not easy. First, most vendors do not encode ampersands. Most often, they rely on the twice-cooked method (using the non-standard embed element).

To make our markup standards-compliant, we first replace all ampersands in URLs with “&” Then, we implement a single-object method. Unlike the nested-objects method, this technique feeds user agents a single object, as the sample code shows below. See example six

<div id="containingBlock">
  <div class="videoWrapper">
    <div>
      <!--[if IE]>
      <object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" width="480" height="295">
        <param name="movie" value="http://www.youtube.com/v/mDRYnaajUcY  &hl=en&fs=1&showinfo=0" />
      <![endif]-->
      <!--[if !IE]>-->
      <object type="application/x-shockwave-flash" data="http://www. youtube.com/v/mDRYnaajUcY&hl=en&fs=1&showinfo=0" width="480" height="295">
      <!--<![endif]-->
        <param name="quality" value="high" />
        <param name="wmode" value="opaque" />
        <p><a href="http://www.adobe.com/go/getflashplayer"><img alt=  "Get Adobe Flash player" src="http://www.adobe.com/images/shared/ download_buttons/get_flash_player.gif"/></a></p>
      </object>
    </div>
  </div>
  ...
</div>

This single-object technique facilitates generating code as the “forking” is done in a single place <object> rather than in two places <object> and </object>.

The bonus

Because we have an absolutely positioned element within the box, we can hide content “behind” the video. Note: this content is outside the object. It is not “alternative content” per se. See example seven

<div id="containingBlock">
  <div class="videoWrapper">
    <div>
      <!--[if IE]>
      <object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" width="480" height="295">
        <param name="movie" value="http://www.youtube.com/v/ mDRYnaajUcY&hl=en&fs=1&showinfo=0" />
      <![endif]-->
      <!--[if !IE]>-->
      <object type="application/x-shockwave-flash" data="http://www. youtube.com/v/mDRYnaajUcY&hl=en&fs=1&showinfo=0" width= "480" height="295">
      <!--<![endif]-->
        <param name="quality" value="high" />
        <param name="wmode" value="opaque" />
        <p><a href="http://www.adobe.com/go/getflashplayer"> <img  alt="Get Adobe Flash player" src="http://www.adobe.com/images/shared/ download_buttons/get_flash_player.gif"></a></p>
      </object>
    </div>
    <p>The following is the description of the video embeded in this document.</p>
    <p>This short clip is about YouTube widescreen formatting. Itshows the two main formats (16:9, 4:3) and also explains the best way to create a Flash movie according to the new widescreen format.</p>
  </div>
  ...
</div>

The SWFObject script approach

To automate this method, we can use the SWFObject script to add the .videoWrapper class we need for styling and also to plug in the wrapper we need for IE. See example height (Note: in this last example, the width of the containing block is set in em.)

To add the code we need, we replace the following line in SFWObject v1.5 (around line 117):

n.innerHTML = this.getSWFHTML();

With the ones below:

n.className += " videoWrapper";
if(typeof document.documentElement.style.zoom != "undefined"){
  var wrapper4ie = document.createElement("div");
  n.appendChild(wrapper4ie);
  wrapper4ie.innerHTML = this.getSWFHTML();
}else{
  n.innerHTML = this.getSWFHTML();
};

And that’s all there is to it. With just a little CSS and your new know-how, you too can resize and scale videos on the fly. See what you can do with the intrinsic ratio technique and share your results in the discussion.