Accessible Slider Module

Based on the W3C/WAI recommendations here: https://www.w3.org/WAI/tutorials/carousels/

by jorgeluis

HTML

<section class="carousel-container" aria-labelledby="carouselheading">
  <h3 id="carouselheading" role="presentation" class="visuallyhidden">Featured news</h3>

<div id="carousel" class="carousel">
    <ul>
        <li class="slide">
          <div class="slide-content">
            <h4 class="slide-title">Accessible Slider Module</h4>
            <p>Inspired by a <a href="https://www.w3.org/WAI/tutorials/carousels/">W3C/WAI post</a> about accessible carousels.</p>
            <p><a href="https://www.w3.org/WAI/tutorials/carousels/">Read more</a></p>
          </div>
        </li>
        <li class="slide" style="background-image: url(http://placehold.it/600x200/234);">
          <div class="slide-content">
            <h4 class="slide-title">Background image</h4>
            <p>For background images, best to add them as inline style attribute (just background-image) to the .slide element.</p>
            <p>See the html</p>
          </div>
        </li>
        <li class="slide">
          <div class="slide-content">
            <h4 class="slide-title">This example shows the .slidenav at the bottom</h4>
            <p>With play/pause control and numbered buttons</p>
            <p>I also used <code>title</code> attributes on previous and next svg arrows.</p>
           </div>
       </li>
    </ul>
</div>
</section>

SCSS

.visuallyhidden {
	overflow : hidden;
	position : absolute;
	clip : rect(0 0 0 0);
	height : 1px;
	width : 1px;
	margin : -1px;
	padding : 0;
	border : 0;
}
.active .slide {
  display: table !important;
  visibility: hidden;
  transition: left .6s ease-out;
}

.active .slide.current {
  visibility: visible;
  left: 0;
}

.active .slide.next {
  left: 100%;
}

.active .slide.prev {
  left: -100%;
}

.active .slide.next.in-transition,
.active .slide.prev.in-transition {
  visibility:visible;
}

.carousel, .slide {
  padding:0;
  margin: 0;
  overflow: hidden;
}
.carousel {
  position: relative;
}
.carousel ul {
  margin:0;
  padding: 0;
}
.slide {
  display: table;
  height: 360px;
  background-position: center center;
  background-size: cover;
  position: relative;
  margin-bottom:1em;
}

.slide-content {
  display: table-cell;
  padding: 60px;
  vertical-align: middle;
}
.slide-title {
  font-size: 3em;
  margin: 0;
}

.carousel.active {
  height: 360px;
  position:relative;
  background-color: #222;
  color: #f3f3f3;
  -webkit-box-sizing: content-box;
  -moz-box-sizing: content-box;
  box-sizing: content-box;
  
  a, a:visited {
    color: tomato;
  }
}

.active .slide {
  border: none;
  display: none;
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  z-index: 200;
}

.slide.current {
  display:block;
  z-index: 500;
}

.btn-prev,
.btn-next {
  position:absolute;
  z-index: 700;
  transform: translateY(-50%);
  top: 50%;
  border: 0;
  line-height: 1;
  padding: 10px 5px;
  background-color: transparent;
  transition: padding .4s ease-out;
}

.btn-next:hover,
.btn-next:focus,
.btn-prev:hover,
.btn-prev:focus {
  padding-left: 15px;
  padding-right:15px;
}

.btn-prev {
  left: 0;
}
.btn-next {
  right: 0;
}

.btn-prev svg,
.btn-next svg {
  width: 40px;
}
.btn-prev .nav-arrow,
.btn-next .nav-arrow {
  fill: none;
  stroke: rgba(255,255,255, 0.6);
  stroke-width: 3;
  stroke-linecap: round;
  stroke-miterlimit: 10;
}


/* slidenav is the bottom labels...

JavaScript

/* focusin/out event polyfill (firefox) */
!function(){
  var w = window,
  d = w.document;

  if( w.onfocusin === undefined ){
    d.addEventListener('focus' ,addPolyfill ,true);
    d.addEventListener('blur' ,addPolyfill ,true);
    d.addEventListener('focusin' ,removePolyfill ,true);
    d.addEventListener('focusout' ,removePolyfill ,true);
  }
  function addPolyfill(e){
    var type = e.type === 'focus' ? 'focusin' : 'focusout';
    var event = new CustomEvent(type, { bubbles:true, cancelable:false });
    event.c1Generated = true;
    e.target.dispatchEvent( event );
  }
  function removePolyfill(e){
if(!e.c1Generated){ // focus after focusin, so chrome will the first time trigger tow times focusin
  d.removeEventListener('focus' ,addPolyfill ,true);
  d.removeEventListener('blur' ,addPolyfill ,true);
  d.removeEventListener('focusin' ,removePolyfill ,true);
  d.removeEventListener('focusout' ,removePolyfill ,true);
}
setTimeout(function(){
  d.removeEventListener('focusin' ,removePolyfill ,true);
  d.removeEventListener('focusout' ,removePolyfill ,true);
});
}
}();

var myCarousel = (function() {

  var carousel, slides, index, slidenav, settings, timer, setFocus, animationSuspended;

  function forEachElement(elements, fn) {
    for (var i = 0; i < elements.length; i++)
      fn(elements[i], i);
  }

  function removeClass(el, className) {
    if (el.classList) {
      el.classList.remove(className);
    } else {
      el.className = el.className.replace(new RegExp('(^|\\b)' + className.split(' ').join('|') + '(\\b|$)', 'gi'), ' ');
    }
  }

  function hasClass(el, className) {
    if (el.classList) {
      return el.classList.contains(className);
    } else {
      return new RegExp('(^| )' + className + '( |$)', 'gi').test(el.className);
    }
  }

  function init(set) {
    settings = set;
    carousel = document.getElementById(settings.id);
    slides = carousel.querySelectorAll('.slide');

    carousel.className = 'active carousel';

    var ctrls =...