Tag: Video

ShopTalk Goes Video

Dave and I slapped up a little videos section of the ShopTalk website. Twelve so far! They are short-ish, between 10-20 minutes, each focused on one fairly specific thing. We’re kinda just dipping our toes here — we don’t even have a real proper name for them yet! The place to subscribe to them is YouTube, and you might actually be subscribed already as we’ve been publishing them right to the CSS-Tricks YouTube Channel. There is a direct RSS feed as well if you’re cool like that.

Dave wrote:

One nice by-product of exploring video is that we get a chance to re-explain mouth-coding examples or overly visual concepts that were probably confusing on the audio podcast. As much as I love podcasting, the lack of visual examples for a visual field like Web Design and Development is a limitation. I like that the YouTube series is like supplementary material to ideas covered in the podcast, rather than a replacement.

Quick hits:

  • If you have topic ideas you’d like to see, hit me in the comments or anywhere else.
  • If you have ideas on how to make the show work, also let me know (E.g. Is the quality OK? Is the length OK? Are the thumbnails OK? What else can we do to appease the YouTube Algorithm Gods?)
  • Should we actually stream it?
  • Smash that Like and Subscribe button.

The post ShopTalk Goes Video appeared first on CSS-Tricks. You can support CSS-Tricks by being an MVP Supporter.


, ,

Boost app engagement with chat, voice, and video APIs

Sendbird is a service for helping you add social features to your app. Wanna add in-app chat? Sendbird does that. Wanna add in-app voice or video calls? Sendbird does that.

Here’s how I always think about stuff like this. Whatever the thing you are building is, you should specialize in the core of it, and not get bogged down in building essentially another product as you’re doing it. Say you want to build an app for dentists to do bookings and customer management. Please do, by the way, my dentist could really use it. You’ve got a lot of work ahead of you, the core of which is building a thing that is perfect for actual dentists and getting the UX right. In-app chat might be an important part of that, but you aren’t specifically building chat software, you’re building dentist software. Lean on Sendbird for the chat (and everything else).

To elaborate on the dentist software example for a bit, I can tell you more about my dentist. They are so eager to text me, email me, call me, even use social media, to remind me about everything constantly. But for me to communicate with them, I have to call. It’s the only way to talk to them about anything—and it’s obnoxious. If there was a dentist in town where I knew I could fire up a quick digital chat with them to book things, I’d literally switch dentists. Even better if I could click a button in a browser to call them or do a video consult. That’s just good business.

You know what else? Your new app for dentists (seriously, you should do this) is going to have to be compliant on a million standards for any dentist to buy it. This means any software you buy will need to be too. Good thing Sendbird is all set with HIPPA/HITECH, SOC 2, GDPR, and more, not to mention being hugely security-focused.

Sendbird aren’t spring chickens either, they are already trusted by Reddit, Virgin UAE, Yahoo, Meetup, and tons more.

Chat is tricky stuff, too. It’s not just a simple as shuffling a message off to another user and displaying it. Modern chat offers things like reactions, replies, and notifications. Chat needs to be friendly to poor networks and offline situations. Harder still, moderating chat activity and social control like blocking and reporting. Not only does Sendbird help with all that, their UIKit will help you build the interface as well.

Build it with Sendbird, and it’ll scale forever.

Sendbird’s client base gave us confidence that they would be able to handle our traffic and projected growth. ”

Ben Celibicic, CTO

Modern apps have modern users that expect these sort of features.

The post Boost app engagement with chat, voice, and video APIs appeared first on CSS-Tricks.

You can support CSS-Tricks by being an MVP Supporter.


, , , , ,

HTML Video Sources Should Be Responsive

Scott Jehl doesn’t mince words here:

Removing media support from HTML video was a mistake. It means that for every video we embed in HTML, we’re stuck with the choice of serving source files that are potentially too large or small for many users’ devices (resulting in poor performance, wasteful data consumption, and even sub-optimal quality on larger screens), or resorting to more complicated server-side or scripted or third-party solutions to deliver a correct size.

I remember when responsive images were just starting to come out. One way to explain it was to say it’s like <video> in that you can have multiple <source> elements inside which (in supporting browsers) allowed you to specify attributes like type (e.g. video format) and media (e.g. screen size). But then

Despite being implemented in multiple browsers, the feature was removed from browsers and the HTML specification, without any proposed replacement for the functionality it once provided. One exception is the feature was never removed from Webkit, so it still works in Safari browsers, which is great.

I don’t remember that. That feels like a big WTF moment (some background). I think of the web as being tremendous at backwards compatibility. It’s a rare day when we just yank stuff, and even more rare is a yanking with no alternative whatsoever.

So now with responsive images being a success (it’s a success, right? I can’t imagine how incredibly much bandwidth it has saved the world)… can’t we… put it back?

When I have an immediate need for this, I always think of Cloudinary, because I can alter the size and format of video by changing the URL. Like here’s a video URL where the video codec is automatically determined and the size is forced down to 400px:


It’s nice to have tools like this, but that doesn’t mean the platform shouldn’t be helping.

Direct Link to ArticlePermalink

The post HTML Video Sources Should Be Responsive appeared first on CSS-Tricks.

You can support CSS-Tricks by being an MVP Supporter.


, , , ,

Weekly Platform News: The :not() pseudo-class, Video Media Queries, clip-path: path() Support

Hey, we’re back with weekly updates about the browser landscape from Šime Vidas.

In this week’s update, the CSS :not pseudo class can accept complex selectors, how to disable smooth scrolling when using “Find on page…” in Chrome, Safari’s support for there media attribute on <video> elements, and the long-awaited debut of the path() function for the CSS clip-path property.

Let’s jump into the news…

The enhanced :not() pseudo-class enables new kinds of powerful selectors

After a years-long wait, the enhanced :not() pseudo-class has finally shipped in Chrome and Firefox, and is now supported in all major browser engines. This new version of :not() accepts complex selectors and even entire selector lists.

For example, you can now select all <p> elements that are not contained within an <article> element.

/* select all <p>s that are descendants of <article> */ article p { }  /* NEW! */ /* select all <p>s that are not descendants of <article> */ p:not(article *) { }

In another example, you may want to select the first list item that does not have the hidden attribute (or any other attribute, for that matter). The best selector for this task would be :nth-child(1 of :not([hidden])), but the of notation is still only supported in Safari. Luckily, this unsupported selector can now be re-written using only the enhanced :not() pseudo-class.

/* select all non-hidden elements that are not preceded by a non-hidden sibling (i.e., select the first non-hidden child */ :not([hidden]):not(:not([hidden]) ~ :not([hidden])) { }

The HTTP Refresh header can be an accessibility issue

The HTTP Refresh header (and equivalent HTML <meta> tag) is a very old and widely supported non-standard feature that instructs the browser to automatically and periodically reload the page after a given amount of time.

<!-- refresh page after 60 seconds --> <meta http-equiv="refresh" content="60">

According to Google’s data, the <meta http-equiv="refresh"> tag is used by a whopping 2.8% of page loads in Chrome (down from 4% a year ago). All these websites risk failing several success criteria of the Web Content Accessibility Guidelines (WCAG):

If the time interval is too short, and there is no way to turn auto-refresh off, people who are blind will not have enough time to make their screen readers read the page before the page refreshes unexpectedly and causes the screen reader to begin reading at the top.

However, WCAG does allow using the <meta http-equiv="refresh"> tag specifically with the value 0 to perform a client-side redirect in the case that the author does not control the server and hence cannot perform a proper HTTP redirect.

(via Stefan Judis)

How to disable smooth scrolling for the “Find on page…” feature in Chrome

CSS scroll-behavior: smooth is supported in Chrome and Firefox. When this declaration is set on the <html> element, the browser scrolls the page “in a smooth fashion.” This applies to navigations, the standard scrolling APIs (e.g., window.scrollTo({ top: 0 })), and scroll snapping operations (CSS Scroll Snap).

Unfortunately, Chrome erroneously keeps smooth scrolling enabled even when the user performs a text search on the page (“Find on page…” feature). Some people find this annoying. Until that is fixed, you can use Christian Schaefer’s clever CSS workaround that effectively disables smooth scrolling for the “Find on page…” feature only.

@keyframes smoothscroll1 {   from,   to {     scroll-behavior: smooth;   } }  @keyframes smoothscroll2 {   from,   to {     scroll-behavior: smooth;   } }  html {   animation: smoothscroll1 1s; }  html:focus-within {   animation-name: smoothscroll2;   scroll-behavior: smooth; }

In the following demo, notice how clicking the links scrolls the page smoothly while searching for the words “top” and “bottom” scrolls the page instantly.

Safari still supports the media attribute on video sources

With the HTML <video> element, it is possible to declare multiple video sources of different MIME types and encodings. This allows websites to use more modern and efficient video formats in supporting browsers, while providing a fallback for other browsers.

<video>   <source src="/flower.webm" type="video/webm">   <source src="/flower.mp4" type="video/mp4"> </video>

In the past, browsers also supported the media attribute on video sources. For example, a web page could load a higher-resolution video if the user’s viewport exceeded a certain size.

<video>   <source media="(min-width: 1200px)" src="/large.mp4" type="video/mp4">   <source src="/small.mp4" type="video/mp4"> </video>

The above syntax is in fact still supported in Safari today, but it was removed from other browsers around 2014 because it was not considered a good feature:

It is not appropriate for choosing between low resolution and high resolution because the environment can change (e.g., the user might fullscreen the video after it has begun loading and want high resolution). Also, bandwidth is not available in media queries, but even if it was, the user agent is in a better position to determine what is appropriate than the author.

Scott Jehl (Filament Group) argues that the removal of this feature was a mistake and that websites should be able to deliver responsive video sources using <video> alone.

For every video we embed in HTML, we’re stuck with the choice of serving source files that are potentially too large or small for many users’ devices … or resorting to more complicated server-side or scripted or third-party solutions to deliver a correct size.

Scott has written a proposal for the reintroduction of media in video <source> elements and is welcoming feedback.

The CSS clip-path: path() function ships in Chrome

It wasn’t mentioned in the latest “New in Chrome 88” article, but Chrome just shipped the path() function for the CSS clip-path property, which means that this feature is now supported in all three major browser engines (Safari, Firefox, and Chrome).

The path() function is defined in the CSS Shapes module, and it accepts an SVG path string. Chris calls this the ultimate syntax for the clip-path property because it can clip an element with “literally any shape.” For example, here’s a photo clipped with a heart shape:

The post Weekly Platform News: The :not() pseudo-class, Video Media Queries, clip-path: path() Support appeared first on CSS-Tricks.

You can support CSS-Tricks by being an MVP Supporter.


, , , , , , , , ,

Cloudinary Tricks for Video

Creating video is time consuming. A well-made 5-minute video can take hours to plan, record, and edit — and that’s before we start talking about making that video consistent with all the other videos on your site.

When we took on the Jamstack Explorers project (a video-driven educational resource for web developers), we wanted to find the right balance of quality and shipping: what could we automate in our video production process to reduce the time and number of steps required to create video content without sacrificing quality?

With the help of Cloudinary, we were able to deliver a consistent branding approach in all our video content without adding a bunch of extra editing tasks for folks creating videos. And, as a bonus, if we update our branding in the future, we can update all the video branding across the whole site at once — no video editing required!

What does “video branding” mean?

To make every video on the Explorers site feel like it all fits together, we include a few common pieces in each video:

  1. A title scene
  2. A short intro bumper (video clip) that shows the Jamstack Explorers branding
  3. A short outro bumper that either counts down to the next video or shows a “mission accomplished” if this is the last video in the mission

Skip to the end: here’s how a branded video looks

To show the impact of adding the branding, here’s one of the videos from Jamstack Explorers without any branding:

This video (and this Vue mission from Ben Hong) is legitimately outstanding! However, it starts and ends a little abruptly, and we don’t have a sense of where this video lives.

We worked with Adam Hald to create branded video assets that help give each video a sense of place. Check out the same video with all the Explorers branding applied:

We get the same great content, but now we’ve added a little extra va-va-voom that makes this feel like it’s part of a larger story.

In this article, we’ll walk through how we automatically customize every video using Cloudinary.

How does Cloudinary make this possible?

Cloudinary is a cloud-based asset delivery network that gives us a powerful, URL-based API to manipulate and transform media. It supports all sorts of asset types, but where it really shines is with images and video.

To use Cloudinary, you create a free account, then upload your asset. This asset then becomes available at a Cloudinary URL:

https://res.cloudinary.com/netlify/image/upload/v1605632851/explorers/avatar.jpg                            ^^^^^^^             ^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^                               |                      |                |                               V                      V                V                       cloud (account) name    version (optional)  file name

This URL points to the original image and can be used in <img /> tags and other markup.

The original image size is 97.6kB.

Dynamically adjust file format and quality to reduce file sizes

If we’re using this image on a website and want to improve our site performance, we may decide to reduce the size of this image by using next-generation formats like WebP, AVIF, and so on. These new formats are much smaller, but aren’t supported by all browsers, which would usually mean using a tool to generate multiple versions of this image in different formats, then using a <picture> element or other specialized markup to provide modern options with the JPG fallback for older browsers.

With Cloudinary, all we have to do is add a transformation to the URL:

https://res.cloudinary.com/netlify/image/upload/q_auto,f_auto/v1605632851/explorers/avatar.jpg                                                 ^^^^^^^^^^^^                                                       |                                                       V                                     automatic quality & format transformations 

What we see in the browser is visually identical:

The transformed image is 15.4kB.

By setting the file format and quality settings to automatic (f_auto,q_auto), Cloudinary is able to detect which formats are supported by the client and serves the most efficient format at a reasonable quality level. In Chrome, for example, this image transforms from a 97.6kB JPG to a 15.4kB WebP, and all we had to do was add a couple of things to the URL!

We can transform our images in lots of different ways!

We can go further with other transformations, including resizing (w_150 for “resize to 150px wide”) and color effects (e_grayscale for “apply the grayscale effect”):

The same image after adding grayscale effects and resizing.

This is only a tiny taste of what’s possible — make sure to check out the Cloudinary docs for more examples!

There’s a Node SDK to make this a little more human-readable

For more advanced transformations like what we’re going to get into, writing the URLs by hand can get a little hard to read. We ended up using the Cloudinary Node SDK to give us the ability to add comments and explain what each transformation was doing, and that’s been extremely helpful as we maintain and evolve the platform.

To install it, get your Cloudinary API key and secret from your console, then install the SDK using npm:

# create a new directory mkdir cloudinary-video  # move into the new directory cd cloudinary-video/  # initialize a new Node project npm init -y  # install the Cloudinary Node SDK npm install cloudinary

Next, create a new file called index.js and initialize the SDK with your cloud_name and API credentials:

const cloudinary = require('cloudinary').v2;  // TODO replace these values with your own Cloudinary credentials cloudinary.config({   cloud_name: 'your_cloud_name',   api_key: 'your_api_key',   api_secret: 'your_api_secret', });

Don’t commit your API credentials to GitHub or share them anywhere. Use environment variables to keep them safe! If you’re unfamiliar with environment variables, Colby Fayock has written a great introduction to using environment variables.

Next, we can create the same transformation as before using slightly more human-readable configuration settings:

cloudinary.uploader   // the first argument should be the public ID (including folders!) of the   // image we want to transform   .explicit('explorers/avatar', {     // these two properties match the beginning of the URL:     // https://res.cloudinary.com/netlify/image/upload/...     //                                    ^^^^^^^^^^^^     resource_type: 'image',     type: 'upload',      // "eager" means we want to run these transformations ahead of time to avoid     // a slow first load time     eager: [       {         fetch_format: 'auto',         quality: 'auto',         width: 150,         effect: 'grayscale',       },     ],      // allow this transformed image to be cached to avoid re-running the same     // transformations over and over again     overwrite: false,   })   .then((result) => {     console.log(result);   });

Let’s run this code by typing node index.js in our terminal. The output will look something like this:

{   asset_id: 'fca4abba96ffdf70ef89498aa340ae4e',   public_id: 'explorers/avatar',   version: 1605632851,   version_id: 'b8a923931af20404e89d03852ff1bff1',   signature: 'e7201c9ab36cb5b6a0545cee4f5f8ee27fb7f99f',   width: 300,   height: 300,   format: 'jpg',   resource_type: 'image',   created_at: '2020-11-17T17:07:31Z',   bytes: 97633,   type: 'upload',   url: 'http://res.cloudinary.com/netlify/image/upload/v1605632851/explorers/avatar.jpg',   secure_url: 'https://res.cloudinary.com/netlify/image/upload/v1605632851/explorers/avatar.jpg',   access_mode: 'public',   eager: [     {       transformation: 'e_grayscale,f_auto,q_auto,w_150',       width: 150,       height: 150,       bytes: 6192,       format: 'jpg',       url: 'http://res.cloudinary.com/netlify/image/upload/e_grayscale,f_auto,q_auto,w_150/v1605632851/explorers/avatar.jpg',       secure_url: 'https://res.cloudinary.com/netlify/image/upload/e_grayscale,f_auto,q_auto,w_150/v1605632851/explorers/avatar.jpg'     }   ] }

Under the eager property, our transformations are shown along with the full URL to view the transformed image.

While the Node SDK is probably overkill for a straightforward transformation like this one, it becomes really handy when we start looking at the complex transformations required to add video branding.

Transforming videos with Cloudinary

To transform our videos in Jamstack Explorers, we follow the same approach: each video is uploaded to Cloudinary, and then we modify the URLs to resize, adjust quality, and insert the title card and bumpers.

There are a few major categories of transformation that we’ll be tackling to add the branding:

  1. Overlays
  2. Transitions
  3. Text overlays
  4. Splicing

Let’s look at each of these categories and see if we can’t reimplement the Jamstack Explorers branding on Ben’s video! Let’s get set up by setting up index.js to transform our base video:

cloudinary.uploader   .explicit('explorers/bumper', {     // these two properties match the beginning of the URL:     // https://res.cloudinary.com/netlify/image/upload/...     //                                    ^^^^^^^^^^^^     resource_type: 'video',    type: 'upload',      // "eager" means we want to run these transformations ahead of time to avoid     // a slow first load time     eager: [       {         fetch_format: 'auto',         quality: 'auto',         height: 360,         width: 640,         crop: 'fill', // avoid letterboxing if videos are different sizes       },     ],      // allow this transformed image to be cached to avoid re-running the same     // transformations over and over again     overwrite: false,   })   .then((result) => {     console.log(result);   });

You may have noticed that we’re using a video called “bumper” instead of Ben’s original video. This is due to the way Cloudinary orders videos as we add them together. We’ll add Ben’s video in the next section!

Combine two videos with a custom transition using Cloudinary

To add our bumpers, we need to add a second transformation “layer” to the eager array that adds a second video as an overlay.

To do this, we use the overlay transformation and set it to video:publicID, where publicID is the Cloudinary public ID of the asset with any slashes (/) transformed to colons (:).

We also need to tell Cloudinary how to transition between the two videos, which we do using a special kind of video called a luma matte that lets us mask one video with the black area of the video, and a second video with the white area. This results in a stylized cross-fade.

Here’s what the luma matte looks like on its own:

The video and the transition both have their own transformations, which means that we need to treat them as different “layers” in the Cloudinary transform. This means splitting them into separate objects, then adding additional objects to “apply” each layer, which allows us to call that section done and continue adding more transformations to the main video.

To tell Cloudinary this this is a luma matte and not another video, we set the effect type to transition.

Make the following changes in index.js to put all of this in place:

const videoBaseTransformations = {   fetch_format: 'auto',   quality: 'auto',   height: 360,   width: 600,   crop: 'fill', }  cloudinary.uploader   .explicit('explorers/bumper', {     // these two properties match the beginning of the URL:     // <https://res.cloudinary.com/netlify/image/upload/>...     //     resource_type: 'video',     type: 'upload',      // "eager" means we want to run these transformations ahead of time to avoid     // a slow first load time     eager: [       videoBaseTransformations,       {         overlay: 'video:explorers:LCA-07-lifecycle-hooks',         ...videoBaseTransformations,       },       {         overlay: 'video:explorers:transition',         effect: 'transition',       },       { flags: 'layer_apply' }, // <= apply the transformation       { flags: 'layer_apply' }, // <= apply the actual video     ],      // allow this transformed image to be cached to avoid re-running the same     // transformations over and over again     overwrite: false,   })   .then((result) => {     console.log(result);   });

We need the same format, quality, and sizing transformations on all videos, so we pulled those out into a variable called videoBaseTransformations, then added a second object to contain the overlay.

If we run this with node index.js, the video we get back looks like this:

Not bad! This already looks like it’s part of the Jamstack Explorers site, and that transition adds a nice flow from the common bumper into the custom video.

Adding the outro bumper works exactly the same: we need to add another overlay for the ending bumper and a transition. We won’t show this code in the tutorial, but you can see it in the source code if you’re interested.

Add a title card to a video using text overlays

To add a title card, there are two distinct steps:

  1. Extract a short video clip to serve as the title card background
  2. Add a text overlay with the video’s title

The next two sections walk through each step individually so we can see the distinction between the two.

Extract a short video clip to use as the title card background

When Adam Hald created the Explorers video assets, he included a beautiful intro video that opens on a starry sky that’s perfect for a title card. Using Cloudinary, we can grab a few seconds of that starry sky and splice it into every video as a title card!

In index.js, add the following transformation blocks:

cloudinary.uploader   .explicit('explorers/bumper', {     // these two properties match the beginning of the URL:     // https://res.cloudinary.com/netlify/image/upload/...     //     resource_type: 'video',     type: 'upload',      // "eager" means we want to run these transformations ahead of time to avoid     // a slow first load time     eager: [       videoBaseTransformations,       {         overlay: 'video:explorers:LCA-07-lifecycle-hooks',         ...videoBaseTransformations,       },       {         overlay: 'video:explorers:transition',         effect: 'transition',       },       { flags: 'layer_apply' }, // <= apply the transformation       { flags: 'layer_apply' }, // <= apply the actual video        // add the outro bumper and a transition       {         overlay: 'video:explorers:countdown',         ...videoBaseTransformations,       },       {         overlay: 'video:explorers:transition',         effect: 'transition',       },       { flags: 'layer_apply' },       { flags: 'layer_apply' },        // splice a title card at the beginning of the video       {         overlay: 'video:explorers:intro',         flags: 'splice', // splice this into the video         ...videoBaseTransformations,       },       {         audio_codec: 'none', // remove the audio         end_offset: 3, // shorten to 3 seconds         effect: 'accelerate:-25', // slow down 25% (to ~4 seconds)       },       {         flags: 'layer_apply',         start_offset: 0, // put this at the beginning of the video       },     ],      // allow this transformed image to be cached to avoid re-running the same     // transformations over and over again     overwrite: false,   })   .then((result) => {     console.log(result);   });

Using the splice flag, we tell Cloudinary to add this video directly without a transition.

In the next set of transformations, we add three transformations we haven’t seen before:

  1. We set audio_codec to none to remove sound from this segment of video.
  2. We set end_offset to 3, which means we’ll get only the first 3 seconds of the video.
  3. We add the accelerate effect with a value of -25, which slows the video down by 25%.

Running node index.js will now give us a video that starts with just under 4 seconds of silent, starry skies:

Add text overlays to videos using Cloudinary

Our last step is to add a text overlay to show the video title!

Text overlays use the same overlay property as other overlays, but we pass an object with settings for the font. Cloudinary supports a wide variety of fonts — I haven’t been able to find a definitive list, but it seems to be a large number of Google Fonts — and if you’ve purchased a license to use a custom font, you can upload a custom font to Cloudinary for use in text overlays as well.

cloudinary.uploader   .explicit('explorers/bumper', {     // these two properties match the beginning of the URL:     // <https://res.cloudinary.com/netlify/image/upload/>...     //     resource_type: 'video',     type: 'upload',      // "eager" means we want to run these transformations ahead of time to avoid     // a slow first load time     eager: [       videoBaseTransformations,       {         overlay: 'video:explorers:LCA-07-lifecycle-hooks',         ...videoBaseTransformations,       },       {         overlay: 'video:explorers:transition',         effect: 'transition',       },       { flags: 'layer_apply' }, // <= apply the transformation       { flags: 'layer_apply' }, // <= apply the actual video        // add the outro bumper and a transition       {         overlay: 'video:explorers:countdown',         ...videoBaseTransformations,       },       {         overlay: 'video:explorers:transition',           effect: 'transition',         },         { flags: 'layer_apply' },         { flags: 'layer_apply' },          // splice a title card at the beginning of the video         {           overlay: 'video:explorers:intro',           flags: 'splice', // splice this into the video           ...videoBaseTransformations,         },         {           audio_codec: 'none', // remove the audio           end_offset: 3, // shorten to 3 seconds           effect: 'accelerate:-25', // slow down 25% (to ~4 seconds)         },         {         overlay: {           font_family: 'roboto', // lots of Google Fonts are supported           font_size: 40,           text_align: 'center',           text: 'Lifecycle Hooks', // this can be any text you want         },         width: 500,         crop: 'fit',         color: 'white',       },       { flags: 'layer_apply' },       {         flags: 'layer_apply',         start_offset: 0, // put this at the beginning of the video       },     ],      // allow this transformed image to be cached to avoid re-running the same     // transformations over and over again     overwrite: false,   })   .then((result) => {     console.log(result);   });

In addition to setting the font size and alignment, we also apply a width of 500px (which will be centered by default) to keep our title text from smashing into the side of the title card, and set the crop value to fit, which will wrap longer titles. Setting the color to white makes our text visible against the dark, starry background.

Run node index.js to generate the URL and we’ll see our fully branded video, including a title card and bumpers!

Build your video branding once; use it everywhere

Creating bumpers, transitions, and title cards is a lot of work. Creating high-quality video content is also a lot of work. If we had to manually edit every Jamstack Explorers video to insert these title cards and bumpers, it’s extremely unlikely that we would have actually done it.

We knew that the only realistic way for us to keep the videos consistently branded was to reduce the friction of adding the branding, and Cloudinary let us automate it entirely. This means that we can stay consistent without any manual steps!

As an added bonus, it also means that if we update our title cards or bumpers in the future, we can update all the branding for all the videos by changing the code in one place. This is a huge relief for us, because we know that Explorers is going to continue to grow and evolve over time.

What to do next

Now that you know how to use Cloudinary to add custom branding, here are some additional resources to help you keep learning.

What else can you automate using Cloudinary? How much time could you save by automating the repetitive parts of your video editing workflow? I am exactly the kind of nerd who loves to talk about this stuff, so send me your ideas on Twitter!

The post Cloudinary Tricks for Video appeared first on CSS-Tricks.

You can support CSS-Tricks by being an MVP Supporter.


, ,

Overlaying Video With Transparency While Wrangling Cross-Browser Support

As websites are becoming more and more dynamic when it comes to design, there is sometimes a need to incorporate complex, animated elements. There are many ways to do that from CSS transitions to 3D rendering on canvas, and animated SVG. But it is often easier to use a <video> since they can be rather efficient and, with them, just about anything visual is possible.

But what if you a need transparent background on that video, so that it could overlay and appear to interact with other content on the page? It’s possible, but can be tricky, especially cross-browser. Let’s explore that.

Here’s an example

To see how transparent video overlays work, I’ve prepared an example that I hope feels both fun and relatable. The idea is to integrate video with interactive content underneath so that the content behind the video changes as the slider is advancing. It is a slider advertising dog food with a real video of a dog on the overlay and I’m sure that realistic, cute dog would improve the conversion rate!

You can follow along with this article to recreate this demo by yourself. Part of what you’ll need to create a video like this is an “image sequence” (a bunch of alpha-transparent images we’ll stitch together into a video). I’m providing the images I used for the example.

Exploring possibilities

To achieve this effect, I started with a search for any kind of solution that would allow me to insert animated content with transparent background on the page.


The first thing that came up was a GIF. Even though the GIF format was introduced over 30 years ago, it is still widely used. Unfortunately, it has its limitations. While it works perfectly for simple and small animated graphics, it is not that great for colorful, long video footage, it has limited color space, and will grow a lot in size for complex videos.


The next option I’ve looked at was APNG, which for some reason, is not as popular as GIF but is much better. (I didn’t even know it existed, did you?) APNG works similarly to animated GIF files while supporting 24-bit images and 8-bit transparency. It is also backward-compatible with regular PNG. If APNG is not supported, the first frame is displayed. It is supported by popular browsers, but no support in Internet Explorer 11 (yup, some people still have to support that). While it seemed like the way to go for a moment, I was not satisfied with its file size and performance.

Fun fact: Apple adopted the APNG format as their preferred format for animated stickers in iOS 10 iMessage apps.

Loop through PNGs

Next, a crude, but working, idea came to mind: use a bunch of PNGs and loop through them with JavaScript to imitate the video. But it requires using a lot of data transfer (each video frame would be a separate image) and resources to animate (you could drain a user’s battery quickly or make their computer fan go crazy).

I quickly abandoned that idea, but it turned out to be useful later. I’ll get back to that at the end of the article.

WebM with alpha transparency

As all the formats for <img> failed here, I started looking into videos. I found an article about alpha transparency in Chrome video published in 2013, which announces Google Chrome support for WebM with an alpha channel. It even shows an example and shares tips on how to use it. I went through the article and it immediately felt like the way to go. I was even more convinced after converting my image sequence to WebM because, while GIF was weighing in at 5.8 MB, WebM with transparency,using the same frame rate and full colors was only 540 KB! That’s more than 10 times smaller while offering better performance and quality. Awesome!

The joy did not last long though. As soon as I opened the website on my iOS Phone I realized that I should’ve started with checking the browser compatibility. Unfortunately, Safari (iOS and macOS) doesn’t support transparency in WebM. While my video was working perfectly on Chrome, Firefox and Edge, Safari greeted me with an ugly black background.

The search continues…

A good Solution

Thankfully, I found a video from WWDC 2019 announcing HEVC video with Alpha support for Safari starting in iOS 13 and macOS Catalina. It seemed to provide the same functionality as WebM but with support on Apple devices. So I decided to use the hybrid solution: HEVC for Safari and WebM for other browsers.

It seems like the perfect solution. But now we have two tasks:

  1. Create an HEVC with alpha-transparency
  2. Detect for Safari (and the version) to serve the correct format

Creating a transparent video

We will start with the easy part: creating a WebM file. If you want to create, convert, or even edit video files, FFmpeg is your friend. It is an extremely powerful open source multimedia framework, and if you have anything to do with multimedia files, I’d recommend starting there because it’s capable of so many things. I can say that FFmpeg has helped me reduce video file sizes over 10 times without any visible image quality degradation. But let’s get back to transparency.

In my experience, if you need to include animated elements into website layout, you get them as a set of video frames in PNG. Even while working on my example project, a tool that removes background from videos generated a set of images for me. So I will continue here assuming we’re building the video from a set of images (remember, you can download our set), but even if you’ve got a video file instead, the process will look similar (adjust FFmpeg options to your needs).

We are now ready to create a video. Using the command line, we’ll navigate to the folder that contains the PNG files and run the following command:

ffmpeg -framerate 25 -i unscreen-%3d.png -c:v libvpx-vp9 -pix_fmt yuva420p output.webm

You can adjust the arguments to fit your needs:

  • framerate: how much images will be used for 1s of the output video
  • -i unscreen-%3d.png: input file name and format. My files have numbers from 001 to 150 so i used %3d as a mask to select all files with three digits in the name.
  • -c:v: specifies the codec to use. I want the video to be encoded with VP9, which is supported by most web browsers.
  • -pix_fmt: specifies pixel format to be used. In our cas, it should support an alpha channel. You can see all supported formats if you run ffmpeg --pix_fmts.
  • output.webm: provides the desired output file name as a last argument

There are a lot more options available, but I won’t dive into details as it would take probably more than one article to go through them. Those provided in the example command work fine for our use case.

After the process is finished, we should see a new output.webm file, and if you open it in a supported browser, you’ll see a video. Easy, right?

Creating HEVC Alpha

Since we have a WebP file ready, it’s time to move to the second format we will be using: HEVC with alpha transparency. Unfortunately, at the time of writing, FFmpeg doesn’t support HEVC, so we have to use another tool. As far as I know, the only way to create HEVC with alpha is to use the Finder or Compressor on Mac. If you have a PC, you’ll probably have to ask someone with Mac to do it for you. The Compressor app is only provided with Final Cut Pro, so I won’t be using it, although it may be worth considering if you need custom settings.

Since macOS Catalina, the Finder has a built in Encode Media tool to convert videos. It is simple (and free), so it feeds our needs.

The Finder expects a video file as an input, so first, we have to convert the image sequence to ProRes 4444. This is an important step, as the Encode Media tool won’t accept just any video — it will fail unless you provide the format it accepts. It gave me a headache or two until I found out the correct encoding for the input file.

We can create input video using FFmpeg. As we did when creating WebM, we just have to run FFmpeg with the proper arguments:

ffmpeg -framerate 25 -i unscreen-%3d.png -c:v prores_ks -pix_fmt yuva444p10le -alpha_bits 16 -profile:v 4444 -f mov -vframes 150 output.mov

From this point on, a Mac is needed. But the process is simple and does not involve typing anything in terminal, so even if you ask a non-techy friend with a Mac to do it for you, I’m sure they can manage.

Having ProRes4444 video ready, you can navigate to the containing folder in Finder, right-click on it, and in the context menu, select Encode Selected Video Files.

A window will appear. Select the desired HEVC quality (there are two to choose from), and check the option to Preserve Transparency. Then click the “Continue” button.

After a moment you should see a new file, which is encoded in HEVC with alpha. It’s done! Opening this file in Safari confirm that the transparency works.

Last but not least, it’s time to use videos on the website!

Serving transparent videos for all browsers

We can use <video> element to serve videos. If the browser supports the given format in the src attribute, it will just work. But as I’ve mentioned, we need to serve HEVC for Safari and WebM for other browsers.

There is one more thing to take care of: some browsers support WebM or HEVC itself but without transparency. Those browsers will play the video but with a black background instead of an alpha channel. For example, Safari 12 will play HEVC without transparency. That’s unacceptable for us.

Normally, I’d use the <source /> element to provide the video along with multiple formats as fallbacks, and the browser would pick the one it supports. However, since we want to show HEVC only on specific Safari versions, we will have to set the video src attribute instead using JavaScript.

HEVC with transparency is supported in iOS 13 and Mac Safari 13, so let’s start by detecting those. The recommended way to adjust website features based on support is by detecting the existence of an API rather than looking at the User Agent, but I didn’t find any simple way to detect if the browser supports transparency in video format. Instead, I came up with my own solution. It’s not pretty, but it does the job.

function supportsHEVCAlpha() {   const navigator = window.navigator;   const ua = navigator.userAgent.toLowerCase()   const hasMediaCapabilities = !!(navigator.mediaCapabilities && navigator.mediaCapabilities.decodingInfo)   const isSafari = ((ua.indexOf('safari') != -1) && (!(ua.indexOf('chrome')!= -1) && (ua.indexOf('version/')!= -1)))   return isSafari && hasMediaCapabilities }

This targets Safari 13 and above by looking at navigator.mediaCapabilities, which is not supported in older versions, as well as looking that the browser is Safari at all. If the function returns true, then I’m good to go with HEVC alpha. For any other case, I’ll load WebM video.

Here’s an example of how this comes together in HTML:

<video id="player" loop muted autoplay playsinline></video>  <script> const player = document.getElementById('player'); player.src = supportsHEVCAlpha() ? 'output.mov' : 'output.webm'; </script>

If you’re curious about those loop muted autoplay playsinline arguments on video element, those are about replicating a GIF-like experience:

  • Without muted, the video will not play without user interaction.
  • With playsinline, on iOS, the video plays right where it is in the layout instead of opening up in fullscreen.
  • With loop, the video repeats over and over.
  • With autoplay, it will start playing the video automatically on page load (as long as we also have muted in place).

That’s it! We have a lightweight, performant and high-quality solution for transparent videos, that works on most modern browsers (at least the last two recent versions of Chrome, Firefox, Safari and Edge). We can add some basic HTML and CSS to integrate static content with it and make it a little bit more real-life, or just use the same idea that’s in my demo. That wasn’t too bad, was it?

Hey, but what about IE 11? My whole company is using it!

In general, I’d recommend limiting a feature like this to modern browsers and hiding the video in IE. The video is probably not a critical element of the website. Although, I used to work on a commercial project where IE 11 support was a must and I was forced to figure something out to show the transparent video there. In the end, I ended up cycling through PNG images with JavaScript. I reduced the amount of frames and switched between them using a timer. Sure, the performance was awful, but it worked. The video was quite important to the whole design so we intentionally decided to sacrifice performance on IE 11 to provide the full experience.


I hope that I’ve saved you some time researching the idea of alpha-transparent video and that now you’re be able to incorporate animated elements like this on your website. I bet you’ll need it someday!

Do you have a different idea of how to use transparent video? Or maybe some experience with that already? Let me know in the comments.

The post Overlaying Video With Transparency While Wrangling Cross-Browser Support appeared first on CSS-Tricks.

You can support CSS-Tricks by being an MVP Supporter.


, , , , ,

The New CSS-Tricks Video Intro by dina Amin

You know we do video screencasts, right? It’s not, like, super regular, but I have done them for a long time, still like doing them, and plan to keep doing them. I publish them here, but you can subscribe over on YouTube as well.

I’ve had a couple of different custom video intro animations over the years, always done by someone far more handy with that kind of thing than I am. When I asked around in May this year, I got some good leads, but none better than Matthias paging Marc, and then Marc helping me out with an introduction to dina Amin.

One look at dina’s work and it’s an obvious: yes! She does stop-motion and a lot of breakin’ stuff:

Just one small example, check out the show reel too!

We chatted back and forth, scoping out the project. She says:

I worked together with Assem Kamal on a new intro for CSS-Tricks YouTube channel. We wanted to make something very short yet interesting so that audiences on YouTube don’t skip the intro or get bored if they watch a couple of CSS-Tricks videos back to back.

She researched and asked a bunch of questions. I like how she was very aware of the role an intro like this plays in a video, especially tutorials. Can’t be too long. Can’t be annoying in any way. It has to have enough detail that it’s almost fun to see multiple times.

The old video started with a keyboard:

We started with an Apple keyboard, because we wanted to keep something from the original intro that Chris’ audience would relate to, and most importantly because I wanted to take the keyboard apart!

“Did we cut up that keyboard?!” Yes, we did. It was too easy to find multiple broken Apple keyboards, it’s a very well-engineered product and it all comes together beautifully with minimum parts, but only Apple can fix this product. You can’t just get your screw kit out and open this up and fix one flawed button connection. So a lot of these keyboards are thrown away because it’s too expensive to fix in countries like Egypt. We got our hands on three keyboards and we cut up one as we animated and used different keyboard buttons from the other two to make the buttons stretch.

It was fun seeing some behind-the-scenes stuff in her Instagram Stories:

And another connection from the original is the idea of websites as components and building out layouts. That was just referenced in the original with some background sketches and now comes to life with paper prototypes in this version.

We thought of showing the ‘how to make a website’ process in very abstract steps where each step quickly transitions and transforms into the other. Starting with keyboard clicks that turn into code, then design blocks that make up a website, which scrolls to reveal the CSS-Tricks logo.

It’s all done quite literally with stop motion! Hand moving bits around, taking photos, and making those into video.

Once we got the concept approved and our props ready, we spent hours and hours moving little pieces to make all this magic.

Check out a time lapse of the creation!

Ultimately, I got a number of versions in different aspect ratios and sizes, which is wonderful as I can switch it up and use different ones that might be more appropriate in different scenarios. Here’s the main one!

I’ve already been putting these on the start and end of videos like this one.

Thanks, dina and Assam!

The post The New CSS-Tricks Video Intro by dina Amin appeared first on CSS-Tricks.

You can support CSS-Tricks by being an MVP Supporter.


, , , ,

Some Innocent Fun With HTML Video and Progress

The idea came while watching a mandatory training video on bullying in the workplace. I can just hear High School Geoff LOL-ing about a wimp like me have to watch that thing.

But here we are.

The video UI was actually lovely, but it was the progress bar that really caught my attention – or rather the [progress].value. It was a simple gradient going from green to blue that grew as the video continued playing.

If only I had this advice in high school…

I already know it’s possible to create the same sort gradient on the <progress> element. Pankaj Parashar demonstrated that in a CSS-Tricks post back in 2016.

I really wanted to mock up something similar but haven’t played around with video all that much. I’m also no expert in JavaScript. But how hard can that actually be? I mean, all I want to do is get know how far we are in the video and use that to update the progress value. Right?

My inner bully made so much fun of me that I decided to give it a shot. It’s not the most complicated thing in the world, but I had some fun with it and wanted to share how I set it up in this demo.

The markup

HTML5 all the way, baby!

<figure>   <video id="video" src="http://html5videoformatconverter.com/data/images/happyfit2.mp4"></video>   <figcaption>     <button id="play" aria-label="Play" role="button">►</button>     <progress id="progress" max="100" value="0">Progress</progress>     </figcaotion> </figure>

The key line is this:

<progress id="progress" max="100" value="0">Progress</progress>

The max attribute tells us we’re working with 100 as the highest value while the value attribute is starting us out at zero. That makes sense since it allows us to think of the video’s progress in terms of a percentage, where 0% is the start and 100% is the end, and where our initial starting point is 0%.


I’m definitely not going to get deep into the process of styling the <progress> element in CSS. The Pankaj post I linked up earlier already does a phenomenal job of that. The CSS we need to paint a gradient on the progress value looks like this:

/* Fallback stuff */ progress[value] {   appearance: none; /* Needed for Safari */   border: none; /* Needed for Firefox */   color: #e52e71; /* Fallback to a solid color */ }  /* WebKit styles */ progress[value]::-webkit-progress-value {   background-image: linear-gradient(     to right,     #ff8a00, #e52e71   );   transition: width 1s linear; }  /* Firefox styles */ progress[value]::-moz-progress-bar {   background-image: -moz-linear-gradient(     to right,     #ff8a00, #e52e71   ); }

The trick is to pay attention to the various nuances that make it cross-browser compatible. Both WebKit and Mozilla browsers have their own particular ways of handling progress elements. That makes the styling a little verbose but, hey, what can you do?

Getting the progress value from a video

I knew there would be some math involved if I wanted to get the current time of the video and display it as a value expressed as a percentage. And if you thought that being a nerd in high school gained me mathematical superpowers, well, sorry to disappoint.

I had to write down an outline of what I thought needed to happen:

  • Get the current time of the video. We have to know where the video is at if we want to display it as the progress value.
  • Get the video duration. Knowing the video’s length will help express the current time as a percent.
  • Calculate the progress value. Again, we’re working in percents. My dusty algebra textbook tells me the formula is part / whole = % / 100. In the context of the video, we can re-write that as currentTime / duration = progress value.

That gives us all the marching orders we need to get started. In fact, we can start creating variables for the elements we need to select and figure out which properties we need to work with to fill in the equation.

// Variables const progress = document.getElementById( "progress" );  // Properties // progress.value = The calculated progress value as a percent of 100 // video.currentTime = The current time of the video in seconds // video.duration = The length of the video in seconds

Not bad, not bad. Now we need to calculate the progress value by plugging those things into our equation.

function progressLoop() {   setInterval(function () {     document.getElementById("progress").value = Math.round(       (video.currentTime / video.duration) * 100     );   }); }

I’ll admit: I forgot that the equation would result to decimal values. That’s where Math.round() comes into play to update those to the nearest whole integer.

That actually gets the gradient progress bar to animate as the video plays!

I thought I could call this a win and walk away happy. Buuuut, there were a couple of things bugging me. Plus, I was getting errors in the console. No bueno.

Showing the current time

Not a big deal, but certainly a nice-to-have. We can chuck a timer next to the progress bar and count seconds as we go. We already have the data to do it, so all we need is the markup and to hook it up.

Let’s add a wrap the time in a <label> since the <progress> element can have one.

<figure>   <video controls id="video" src="http://html5videoformatconverter.com/data/images/happyfit2.mp4"></video>   <figcaption>     <label id="timer" for="progress" role="timer"></label>     <progress id="progress" max="100" value="0">Progress</progress>     </figcaotion> </figure>

Now we can hook it up. We’ll assign it a variable and use innerHTML to print the current value inside the label.

const progress = document.getElementById("progress"); const timer = document.getElementById( "timer" );  function progressLoop() {   setInterval(function () {     progress.value = Math.round((video.currentTime / video.duration) * 100);     timer.innerHTML = Math.round(video.currentTime) + " seconds";   }); }  progressLoop();

Hey, that works!

Extra credit would involve converting the timer to display in HH:MM:SS format.

Adding a play button

The fact there there were two UIs going on at the same time sure bugged me. the <video> element has a controls attribute that, when used, shows the video controls, like play, progress, skip, volume, and such. Let’s leave that out.

But that means we need — at the very minimum — to provide a way to start and stop the video. Let’s button that up.

First, add it to the HTML:

<figure>   <video id="video" src="http://html5videoformatconverter.com/data/images/happyfit2.mp4"></video>   <figcaption>     <label id="timer" for="progress" role="timer"></label>     <button id="play" aria-label="Play" role="button">►</button>     <progress id="progress" max="100" value="0">Progress</progress>     </figcaotion> </figure>

Then, hook it up with a function that toggles the video between play and pause on click.

button = document.getElementById( "play" );  function playPause() {    if ( video.paused ) {     video.play();     button.innerHTML = "❙❙";   }   else  {     video.pause();      button.innerHTML = "►";   } }  button.addEventListener( "click", playPause ); video.addEventListener("play", progressLoop);

Hey, it’s still working!

I know it seems weird to take out the rich set of controls that HTML5 offers right out of the box. I probably wouldn’t do that on a real project, but we’re just playing around here.

Cleaning up my ugly spaghetti code

I really want to thank my buddy Neal Fennimore. He took time to look at this with me and offer advice that not only makes the code more legible, but does a much, much better job defining states…

// States const PAUSED = 'paused'; const PLAYING = 'playing';  // Initial state let state = PAUSED;

…doing a proper check for the state before triggering the progress function while listening for the play, pause and click events…

// Animation loop function progressLoop() {   if(state === PLAYING) {     progress.value = Math.round( ( video.currentTime / video.duration ) * 100 );     timer.innerHTML = Math.round( video.currentTime ) + ' seconds';     requestAnimationFrame(progressLoop);   } }  video.addEventListener('play', onPlay); video.addEventListener('pause', onPause); button.addEventListener('click', onClick);

…and even making the animation more performant by replacing setInterval with requestAnimationFrame as you can see highlighted in that same snippet.

Here it is in all its glory!

Oh, and yes: I was working on this while “watching” the training video. And, I aced the quiz at the end, thank you very much. 🤓

The post Some Innocent Fun With HTML Video and Progress appeared first on CSS-Tricks.


, , , ,

What Does `playsinline` Mean in Web Video?

I got myself confused about this the other day, went around searching for an answer and came up empty on finding something clear. The answer actually is quite clear and I feel a little silly for not knowing it.

With it in place, like this:

<video src="..." controls playsinline></video>

Mobile browsers, will play the video right where it is instead of the default, which is to open it up fullscreen while it plays.

Here’s what it looks like without playsinline:

Here’s what it looks like with playsinline:

(If you’re like me, you tried to play that video from the play button on the video in the video, only to realize it’s not a real play button. 🤯)

(While we’re getting meta, I’m making sure those videos above have playsinline on them right here in this blog post.)

Note that the user still has the ability to go fullscreen with the video even in the playsinline scenario, so I’d say it’s probably a smart default choice. It’s not literally the default — you have to add it, but you know what I mean.

One of the places this shows up regularly is in the attribute soup that is required to make video be a GIF-replacement:

<video autoplay loop muted playsinline src="..."></video>

If you have all four of those attributes, the video will actually autoplay (and loop) on mobile browsers, just like a GIF would, only with far better performance. You can skip loop if you don’t want that, but all the other three are required if you want the autoplay attribute to work.

I also learned that the (only?) way to get a thumbnail to show up on mobile (at least for iOS) is to use a poster attribute. Even if you preload, you don’t see anything but blank white unless you have a poster.

Without a poster:

With a poster:

<video   src="movie.mov"   poster="thumbnail.jpg"    controls ></video>mo

If you can’t be troubled to deal with a poster, you might wanna toss a border or something on video elements to sort of visually block off that area. Otherwise, the floating play button is a little awkward.

The post What Does `playsinline` Mean in Web Video? appeared first on CSS-Tricks.


, ,

Fluid Width Video

IN A WORLD of responsive and fluid layouts on the web, ONE MEDIA TYPE stands in the way of perfect harmony: video. There are lots of ways in which video can be displayed on your site. You might be self-hosting the video and presenting it via the HTML5 <video> tag. You might be using YouTube, Vimeo, or some other video provider that provides <iframe> code to display videos. Let’s cover how to make them all fluid width while maintaining an appropriate height based on their aspect ratio.

In each of these video-embedding scenarios, it is very common for a static width and height to be declared.

<video width="400" height="300" controls ... ></video>  <iframe width="400" height="300" ... ></iframe>  <!-- maybe even super old school --> <object width="400" height="300" ... /> <embed width="400" height="300" ... />

Guess what? Declaring static widths isn’t a good idea in fluid width environments. What if the parent container for that video shrinks narrower than the declared 400px? It will bust out and probably look ridiculous and embarrassing.

Simple and contrived, but still ridiculous and embarassing.

So can’t we just do this?

<video width="100%" ... ></video>

Well, yep, you can! If you are using standard HTML5 video, that will make the video fit the width of the container. It’s important that you remove the height declaration when you do this so that the aspect ratio of the video is maintained as it grows and shrinks, lest you get awkward “bars” to fill the empty space (unlike images, the actual video maintains it’s aspect ratio regardless of the size of the element).

You can get there via CSS (and not worry about what’s declared in the HTML) like this:

video {   /* override other styles to make responsive */   width: 100%    !important;   height: auto   !important; }

<iframe> Video (YouTube, Vimeo, etc.)

Our little trick from above isn’t going to help us when dealing with video that is delivered via <iframe>. Forcing the width to 100% is effective, but when we set height: auto, we end up with a static height of 150px1, which is far too squat for most video and makes for more R&E (Ridiculous and Embarrassing).

Fortunately, there are a couple of possible solutions here. One of them was pioneered by Thierry Koblentz and presented on A List Apart in 2009: Creating Intrinsic Ratios for Video. With this technique, you wrap the video in another element which has an intrinsic aspect ratio, then absolute position the video within that. That gives us fluid width with a reasonable height we can count on.

<div class="videoWrapper">   <!-- Copy & Pasted from YouTube -->   <iframe width="560" height="349" src="http://www.youtube.com/embed/n_dZNLr2cME?rel=0&hd=1" frameborder="0" allowfullscreen></iframe> </div>
.videoWrapper {   position: relative;   padding-bottom: 56.25%; /* 16:9 */   height: 0; } .videoWrapper iframe {   position: absolute;   top: 0;   left: 0;   width: 100%;   height: 100%; }

There is a clever adaptation of this that allows you to adjust the aspect ratio right from the HTML, like:

<div class="videoWrapper" style="--aspect-ratio: 3 / 4;">   <iframe ...>
.videoWrapper {   ...   /* falls back to 16/9, but otherwise uses ratio from HTML */   padding-bottom: calc(var(--aspect-ratio, .5625) * 100%);  }

Some old school video embedding uses <object> and <embed> tags, so if you’re trying to be comprehensive, update that wrapper selector to:

.videoWrapper iframe, .videoWrapper embed, .videoWrapper object { }

But, but… aspect ratios, legacy content, non-tech users, etc.

The above technique is awesome, but it has several possible limitations:

  1. It requires a wrapper element, so just straight up copy-and-pasting code from YouTube is out. Users will need to be a bit savvier.
  2. If you have legacy content and are redesigning to be fluid, all old videos need HTML adjustments.
  3. All videos need to be the same aspect ratio. Otherwise, they’ll be forced into a different aspect ratio and you’ll get the “bars”. Or, you’ll need a toolbox of class names you can apply to adjust it which is an additional complication.

If either of these limitations applies to you, you might consider a JavaScript solution.

Imagine this: when the page loads all videos are looked at and their aspect ratio is saved. Once right away, and whenever the window is resized, all the videos are resized to fill the available width and maintain their aspect ratio. Using the jQuery JavaScript Library, that looks like this:

// Find all YouTube videos // Expand that selector for Vimeo and whatever else var $ allVideos = $ ("iframe[src^='//www.youtube.com']"),    // The element that is fluid width   $ fluidEl = $ ("body");  // Figure out and save aspect ratio for each video $ allVideos.each(function() {    $ (this)     .data('aspectRatio', this.height / this.width)      // and remove the hard coded width/height     .removeAttr('height')     .removeAttr('width');  });  // When the window is resized $ (window).resize(function() {    var newWidth = $ fluidEl.width();    // Resize all videos according to their own aspect ratio   $ allVideos.each(function() {      var $ el = $ (this);     $ el       .width(newWidth)       .height(newWidth * $ el.data('aspectRatio'));    });  // Kick off one resize to fix all videos on page load }).resize();

That’s sorta what became FitVids.js

Except rather than deal with all that resizing business, FitVids.js loops over all the videos and adds the aspect-ratio enabling HTML wrapper and CSS necessary. That’s way more efficient than needing to bind to a window resize handler!

Plain JavaScript instead

jQuery is rather out of favor these days. Fortunately, Dave has a Vanilla version (that is BYO CSS):

  1. Literally all browsers will render iframe, canvas, embed, and object tags as 300px × 150px if not otherwise declared. Even if this isn’t present in the UA stylesheet.

The post Fluid Width Video appeared first on CSS-Tricks.


, ,