Adding additional single A and AA acceptance criteria to the Bootstrap Carousel.

I’m a huge fan of Bootstrap, and even more of their position on Accessibility.  They make it clear where they stand, the work they’ve put into their features and what is missing and left to the user.

To be clear, I don’t claim to be an expert on Accessibility.  I’ve been fortunate enough to give a few talks on it, be with a company that takes it seriously and have been able to assistant on numerous other Accessibility issues.  The example below is me following the WCAG guidelines for Carousel Accessibility and how that may implemented in a project looking to be A or AA compliant.  Use at your discretion, but more importantly, please read the docs as well to better familiarize yourself with the standards in place.

// bootstrap carousel ADA updates
// id - carousel id
// currentIndicator - text for buttons, ex: 'current slide is,'
function accessibleCarousel(id, currentIndicator) {
    const $bsCarousel = $(id);
    const $bsSlides = $bsCarousel.find('.carousel-item');
    const $bsIndicators = $bsCarousel.find('[data-js="update-indicator-text"]'); // this data attr was added
    const $bsPausePlay = $bsCarousel.find('[data-js="pause-play-carousel"]'); // pause play
    const $clickElements = $('.bs-carousel__prev, .bs-carousel__next, .bs-carousel__indicator-button');
    let activeSlide;
    //
    // once the carousel has transitioned
    // do the following below

    $bsCarousel.on('slide.bs.carousel', (e) => {
        // current slide, e lists out where the element was, where it's going etc
        const currentSlide = e.to;
        activeSlide = e.relatedTarget;
        // on slide, reset slides to hidden with an negative index
        $bsSlides.attr('aria-hidden', 'true').attr('tabindex', '-1');
        // update current slide so it's no longer hidden and remove tabindex
        $($bsSlides[currentSlide]).attr('aria-hidden', 'false').removeAttr('tabindex');
        // reset indicators
        $bsIndicators.html('');
        // update current indicator
        $($bsIndicators[currentSlide]).html(currentIndicator);
    });
        //
        // based on WCAG requirements, there should be a play pause button
        // this needs to update accordinly on click
    $bsPausePlay.on('click', function () {
        const $this = $(this);
        // check if the aria label is play or pause, then play or pause
        if ($this.attr('aria-label') === 'play carousel') {
            $bsCarousel.carousel('cycle');
            $this.attr('aria-label', 'pause carousel');
            $this.find('.fa-pause').removeClass('d-none');
            $this.find('.fa-play').addClass('d-none');
        } else {
            $bsCarousel.carousel('pause');
            $this.attr('aria-label', 'play carousel');
            $this.find('.fa-pause').addClass('d-none');
            $this.find('.fa-play').removeClass('d-none');
        }
    });
        //
        // essentially if there is content, when the slide has transitioned, the focus should be on said content and or link
    // if it has a tag to hit, then fire
    // delay is .05 longer than transition
    if ($bsCarousel.find('.carousel-item a').length) {
        $clickElements.on('click', () => {
            setTimeout(() => {
                // console.log(activeSlide);
                $(activeSlide).find('a').focus();
            }, 650);
        });
    }
}
//
// initialize the function
// pass your id and default text
accessibleCarousel('#carousel-home-intro', 'current slide is,');

 

Here is the HTML mark up to match the JS above.

Notes:

  1. This current example is in razor but you could easily change it to anything else ( manually create the other elements, php etc )
  2. Play / Pause added for Accessibility requirements
  3. Please note additional Accessibility edits with screen reader only copy, etc.
<div id="carousel-home-intro" class="bs-carousel carousel slide" data-ride="carousel">
    @* slides *@
    <div class="carousel-inner">
        @foreach (var carouselItem in carouselItems)
        {
            var activeClass = carouselCount++ == 0 ? "active" : "";
            <div class="carousel-item @activeClass">
                <div class="bs-carousel__bg" style="background-image:url(@(carouselItem.BackgroundImage?.Url))"> </div>
                <div class="carousel-caption bs-carousel__caption">
                    <div class="bs-carousel__text">
                        <h2 class="bs-carousel__text-title">
                            ( Your title )
                        </h2>
                        <p>
                           ( Your text )
                        </p>
                        <a href="#" class="button button--default">
                            ( You get the idea )
                        </a>
                    </div>
                </div>
            </div>
        }

    </div>

    @* arrows *@
    <a class="carousel-control-prev bs-carousel__prev" href="#carousel-home-intro" role="button" data-slide="prev">
        <span class="carousel-control-prev-icon bs-carousel__arrow-icon" aria-hidden="true">
            <i class="fa fa-angle-left" aria-hidden="true"></i>
        </span>
        <span class="sr-only">Previous</span>
    </a>
    <a class="carousel-control-next bs-carousel__next" href="#carousel-home-intro" role="button" data-slide="next">
        <span class="carousel-control-next-icon bs-carousel__arrow-icon" aria-hidden="true">
            <i class="fa fa-angle-right" aria-hidden="true"></i>
        </span>
        <span class="sr-only">Next</span>
    </a>

    @* pagination *@
    <ol class="carousel-indicators bs-carousel__indicators align-items-center">
        <li data-target="#carousel-home-intro" data-slide-to="0" class="active">
            <button class="bs-carousel__indicator-button">
                <span class="sr-only">
                    <span data-js="update-indicator-text">
                        current slide is
                    </span>
                    slide 1
                </span>
            </button>
        </li>
        <li data-target="#carousel-home-intro" data-slide-to="1">
            <button class="bs-carousel__indicator-button"><span class="sr-only"><span data-js="update-indicator-text"></span>slide 2</span></button>
        </li>
        <li data-target="#carousel-home-intro" data-slide-to="2">
            <button class="bs-carousel__indicator-button"><span class="sr-only"><span data-js="update-indicator-text"></span>slide 3</span></button>
        </li>
        <li data-target="#carousel-home-intro" data-slide-to="3">
            <button class="bs-carousel__indicator-button"><span class="sr-only"><span data-js="update-indicator-text"></span>slide 4</span></button>
        </li>
        <li>
            <button aria-label="pause carousel" data-js="pause-play-carousel" class="bs-carousel__play-pause">
                <i class="fa fa-pause" aria-hidden="true"></i>
                <i class="fa fa-play d-none" aria-hidden="true"></i>
            </button>
        </li>
    </ol>
</div>

 

Questions / comments / concerns please feel free to fill out the contact form and I’ll be more than help out if I can.

Also please share if you’re so inclined, the web needs to be an more accessible place and if there are features out there you can use, they should be available to anyone who wants to try.