Video

<div class="video" data-video-inline>
    <div class="video__overlay">
        <button class="video__play-btn" aria-label="Play Video">
      <svg viewBox="0 0 12.86 25.72" role="presentation" aria-hidden="true">
        <polygon points="0,0 12.86,12.86 0,25.72"/>
      </svg>
    </button>
        <img src="https://via.placeholder.com/1280x720" srcset="" alt="Sample Alt Text" />
    </div>
    <div class="video__content">
        <iframe src="https://www.youtube.com/embed/-tbjPxtBfhM?enablejsapi=1" frameborder="0" allowfullscreen></iframe>
    </div>
</div>
<div class="video" data-video-inline>
  <div class="video__overlay">
    <button class="video__play-btn" aria-label="Play Video">
      <svg viewBox="0 0 12.86 25.72" role="presentation" aria-hidden="true">
        <polygon points="0,0 12.86,12.86 0,25.72"/>
      </svg>
    </button>
    <img src="{{ placeholder.src }}" srcset="{{ placeholder.srcset }}" alt="{{ placeholder.alt }}" />
  </div>
  <div class="video__content">
    <iframe src="{{ 'https://www.youtube.com/embed/' ~ youtubeId ~ '?enablejsapi=1'}}" frameborder="0" allowfullscreen></iframe>
  </div>
</div>
{
  "youtubeId": "-tbjPxtBfhM",
  "placeholder": {
    "src": "https://via.placeholder.com/1280x720",
    "alt": "Sample Alt Text"
  }
}
  • Content:
    .video {
        position: relative;
    
        &__overlay {
            z-index: 1;
            position: absolute;
            top: 0;
            left: 0;
            display: flex;
            align-items: center;
            justify-content: center;
            width: 100%;
            height: 100%;
            overflow: hidden;
            background-color: color(neutral, lightest);
    
            .video--active & {
                opacity: 0;
                visibility: hidden;
            }
    
            img {
                position: absolute;
                top: 0;
                left: 0;
                width: 100%;
                min-width: 100%;
                min-height: 100%;
                object-fit: cover;
            }
        }
    
        &__play-btn {
            z-index: 1;
            position: relative;
            display: flex;
            align-items: center;
            justify-content: center;
            border-radius: 50%;
            border: 0;
            padding: 0;
            width: 90px;
            height: 90px;
            background-color: color(primary, base);
    
            &:focus,
            &:hover {
                background-color: color(primary, hover);
                cursor: pointer;
            }
    
            svg {
                // Negative margin here is applied to offest the play icon
                // positioning from the center as it aesthetically is too
                // left-justified without it
                margin-right: -5px;
                width: 14px;
                height: 28px;
                fill: color(neutral, lightest);
            }
        }
    
        &__content {
            position: relative;
            // Padding here is used to maintain a perfect aspect ratio
            // for video rectangle, making it responsive as it scales
            padding-bottom: 56.25%;
            max-width: 100%;
            height: 0;
            overflow: hidden;
    
            iframe,
            object,
            embed {
                position: absolute;
                top: 0;
                left: 0;
                width: 100%;
                height: 100%;
                visibility: hidden;
    
                .video--active & {
                    visibility: visible;
                }
            }
        }
    }
    
  • URL: /components/raw/video/_video.scss
  • Filesystem Path: components/video/_video.scss
  • Size: 2 KB
  • Content:
    // Polyfills
    if (window.NodeList && !NodeList.prototype.forEach) {
      NodeList.prototype.forEach = Array.prototype.forEach;
    }
    
    // Self invoking function to setup API script in the DOM
    (function() {
      const apiRef = document.createElement('script');
      const firstScript = document.getElementsByTagName('script')[0];
    
      apiRef.src = "//www.youtube.com/player_api";
      firstScript.parentNode.insertBefore(apiRef, firstScript);
    })();
    
    window.onYouTubePlayerAPIReady = function() {
      const videos = document.querySelectorAll('[data-video-inline]');
      const apiVideos = [];
    
      videos.forEach((video, i) => {
    
        // assign all iframes on the page to a cooresponding YT Player object
        apiVideos.push(new YT.Player(videos[i].querySelectorAll('iframe')[0]));
    
        // click event to insert and play youtube video
        videos[i].addEventListener('click', function() {
    
          if (!this.classList.contains('video--active')) {
    
            this.classList.add('video--active');
    
            apiVideos[i].playVideo();
          }
        });
    
        // manually set controls for space bar
        // this is neccesary because Youtube keyboard controls are unavailable until
        // after manual click on iframe (as opposed to current button)
        videos[i].addEventListener('keypress', function(e) {
    
            if (e.keyCode === 32) {
              if (apiVideos[i].getPlayerState() == 1) {
                apiVideos[i].pauseVideo();
              } else {
                apiVideos[i].playVideo();
              }
            }
          });
      });
    };
    
  • URL: /components/raw/video/video.js
  • Filesystem Path: components/video/video.js
  • Size: 1.5 KB

Usage

This video player is setup to specifically work with youtube videos and comes with a data attribute, data-video-inline, which links the video the the JS video array in the event that there is more than one video on a page.

Labelling Expectations

  • Each video play button should have an aria-label attribute to describe its function.
  • If the video play button uses an SVG, the SVG should include the role="presentation" and aria-hidden="true" attributes.

Focus Expectations

  • Play buttons should have visible keyboard focus state

Keyboard Expectations

  • Enter or Space = Play/Pause video