Giter VIP home page Giter VIP logo

accessible-slick's Introduction

accessible-slick

the last (accessible) carousel you'll ever need

A highly accessible, WCAG 2.0 / 2.1 compliant, drop-in replacement for Slick Slider (1.8.1) intended to make life easier for real-world dev teams who need to pass accessibility audits.

This package implements accessibility and usability improvements crafted and tested by native screen reader users, low vision users, and expert accessibility consultants at Accessible360 based on their experiences helping to make hundreds of carousels accessible for clients around the world. Read on to learn more about why this package exists, its features, how to use it, and how you can get involved!

Demo

https://accessible360.github.io/accessible-slick

Also check out this collection of ready-to-use demos on CodePen for common scenarios like hero banners, scrolling product cards, PDP thumbnail images, and more!

CDN

Example using jsDelivr

Just add a link to the CSS file in your <head>:

<!-- Add the core slick.min.css -->
<link rel="stylesheet" type="text/css" href="//cdn.jsdelivr.net/npm/@accessible360/[email protected]/slick/slick.min.css">

<!-- Add ONE of the theme files (accessible version or original) -->
<link rel="stylesheet" type="text/css" href="//cdn.jsdelivr.net/npm/@accessible360/[email protected]/slick/accessible-slick-theme.min.css">
<link rel="stylesheet" type="text/css" href="//cdn.jsdelivr.net/npm/@accessible360/[email protected]/slick/slick-theme.min.css">

Then, before your closing <body> tag add:

<script type="text/javascript" src="//cdn.jsdelivr.net/npm/@accessible360/[email protected]/slick/slick.min.js"></script>

Package Managers

npm install @accessible360/accessible-slick

Why is this needed?

Almost by design, carousels are pretty hard for screen reader users (especially newbies) to understand and interact with successfully, let alone enjoy. Its hard to know where slides begin and end, how the slide navigation dots work, or where the various controls are. Carousels also vary quite a bit between sites or even just between pages, so it can be difficult for screen reader users to build up a reliable mental model that applies to ALL carousels. And let's not even get started on autoplay functionality (WCAG 2.2.2, anyone?)!

As one of the most widely used carousel packages out there, Slick Slider has many of these same accessibility issues, making it a consistent source of frustration for dev teams when they go through an accessibility audit. Efforts have been made in the Slick Slider repo to improve these issues (especially in version 1.8.1), but those efforts have also introduced new accessibility issues too!

In the long term it'd be great to contribute some improvements to the core Slick Slider repo, but that may or may not be possible considering it's been abandoned (but not deprecated) by it's original author since 2016. A maintainer or two has recently stepped up to resume development, but with over 1,000 open issues and nearly 200 open PRs (some with conflicting approaches), its unlikely that the big fixes needed will make their way to the master branch any time soon.

In the short term, we're releasing our take on an accessible Slick Slider implementation as a fork that respects the original functionality and API features as much as possible so you can improve the accessibility of your carousels right now! We'll make it available through all the same channels (like NPM and jsDelivr) so upgrading is as easy as changing the URLs in your <link> and <script> tags without having to even touch your existing JavaScript code!

What makes this accessible?

This package implements the following changes, all of which have been thoroughly tested and discussed with Accessible360's team of native screen reader users, low vision users, and experienced accessibility engineers:

New features ✨

Feature Why
Wrapper now has role="region" and a configurable aria-label. Tells screen reader users exactly where the carousel begins and ends in the DOM and gives them a landmark they can jump to or skip easily. Use the regionLabel setting to change the aria-label text (defaults to 'carousel').
Each slide now has role="group" with a generic, numbered aria-label. Tells screen reader users exactly where each individual slide begins and ends in the DOM. It should fit the vast majority of use cases, but if you really want to disable it you can do so with the new useGroupRole setting.
Enabling autoplay now automatically adds a pause/play toggle button as the first focusable element (with customizable icons!). WCAG 2.2.2 requires that all auto-updating content comes with a way to pause, stop, or hide it. For carousels, pause/play icon buttons are the most familiar option. Since autoplay is so disruptive for keyboard and screen reader users, as well as people with certain cognitive conditions, the button is the very first piece of content in the slider so it can be reached right away.
Instructions can now be provided for screen reader users If your slider uses complex logic or unconventional interaction behaviors, there is a good chance that screen reader users will have an especially hard time figuring it out. If you're using the asNavFor setting or any of the API methods/events, you should probably explain how your carousel works to screen reader users.
The Previous and Next arrows can now be placed before, after, or on either side of the slides in the DOM to match the visual design. Designers can get really creative with sliders sometimes, making it difficult to ensure that all of the controls and slide contents are in a logical order in the page's DOM (WCAG 1.3.2) and tab sequence (WCAG 2.4.3). Using the new arrowsPlacement setting, you can now control where the previous and next buttons are injected to better match the visual design!
A more accessible CSS theme is now available with better focus styles and larger icons. The original CSS theme (slick-theme.min.css) had very small icons and poor focus indicators. Now an alternative theme is available (accessible-slick-theme.min.css) with larger icons and browser-default focus indicators. To minimize risk of possible breaking styling changes, this theme is opt-in. Just update your <link> reference to use it! Check out the demo page to see it in action.
When Center Mode is enabled, the aria-label of the centered slide will now be appended with the text "(centered)". As shown in the original examples, it is possible to apply custom styles to the centered slide to emphasize it. Since it's not possible to determine when this is done, the safest option is to just always tell screen reader users which slide is the centered one.

Feature changes ⚠️

Feature Why
Previous and Next button markup improved to use less ARIA and to safely hide the icons from screen readers. Per the First Rule of ARIA Use, aria-label has been removed in favor of inner screen-reader-only text. Also, the HTML5 disabled attribute is now used instead of aria-disabled for more consistency across all input modalities. Finally, the custom icons are attached to inner spans that are properly hidden from screen readers with aria-hidden.
Tab markup is no longer used for slide dots or slides. Carousels don't look like tabs, so real users wouldn't expect them to work like tabs, especially when there are multiple slides visible at a time.
Keyboard navigation with the Left and Right arrow keys has been removed. The Left and Right keys are already used by screen readers for virtual cursor navigation, and other users have no reason to expect this functionality exists without visible instructions or clues.
When slides are dynamically added after initialization, they will now automatically get role="group" and a numbered aria-label. The API expects you to supply the complete markup for each slide you add, including its wrapper. However, developers may not realize that they need to add these attributes for accessibility, so adding them automatically guarantees they're there. This also ensures backwards compatibility with any existing implementations.

Usage

All of the original events and methods, and most of the original settings, are still available and work as expected, so your existing configurations won't need to be updated at all! Refer to the original Slick Slider documentation to see how to use them:

New settings ✨

In addition the original functionality, the following new settings have been added:

Setting Type Default Description
arrowsPlacement string ('beforeSlides' | 'afterSlides' | 'split') null Determines where the previous and next arrows are placed in the slider DOM, which determines their tabbing order. Arrows can be placed together before the slides or after the slides, or split so that the previous arrow is before the slides and the next arrow is after (this is the default). Use this setting to ensure the tabbing order is logical based on your visual design to fulfill WCAG 1.3.2 and 2.4.3.
instructionsText string null Instructions for screen reader users placed at the very beginning of the slider markup. If you are using asNavFor or adding custom functionality with API methods/events, you probably need to supply instructions!
pauseIcon string (html | jQuery selector) | object (DOM node | jQuery object) <span class="slick-pause-icon" aria-hidden="true"></span> Custom element to use as the "pause" icon inside the autoplay pause/play toggle button, when autoplay is enabled.
playIcon string (html | jQuery selector) | object (DOM node | jQuery object) <span class="slick-play-icon" aria-hidden="true"></span> Custom element to use as the "play" icon inside the autoplay pause/play toggle button, when autoplay is enabled.
regionLabel string 'carousel' Text to use for the aria-label that is placed on the wrapper.
useGroupRole boolean true Controls whether role="group" and an aria-label are applied to each slide.
useAutoplayToggleButton boolean true Controls whether a pause/play icon button is added when autoplay is enabled. Setting this to false without providing an alternative control would likely violate WCAG 2.2.2, so be careful!

Deprecated settings ❌

The following settings have been removed from the API, but if you pass them in through your initialization function or data attributes nothing bad will happen! If any of these settings are passed in, you'll just get a soft console warning letting you know that the setting is no longer relevant.

Setting Reason(s)
accessibility Equal access should not be a feature that can be turned off. This setting actually made the slides less accessible by introducing unintuitive tab markup, keyboard navigation that conflicts with screen readers, and more. See issue #12.
focusOnChange Per WCAG 3.2.2 and user research, keyboard focus should never be moved unless the user is told ahead of time. Even when explained, moving focus like this would suck for keyboard users, so this setting had to go. See issue #11.
focusOnSelect Unnecessary since keyboard navigation has been removed. Even with keyboard navigation, tab stops on non-actionable elements is very strange for keyboard users, and really just adds work for them. See issue #11.

Development

If you'd like to contribute changes or just make modifications for your own use, use the following process:

  1. Install all the dev dependencies with NPM:
npm install
  1. Make your changes to the source files. You'll want to focus on:

    • slick/slick.js
    • slick/slick.scss
    • slick/slick-theme.scss
  2. Build! Multiple build systems are available to help with future-proofing. Use whichever one you like - the output is the same!

# Build with Gulp (see gulpfile.js)
gulp

# OR build with Grunt (see gruntfile.js)
grunt
  1. Check your changes by loading up docs/index.html in your browser.

Contributing

See the contributing guidelines.

Credits

Massive kudos to Ken Wheeler and the entire Slick Slider community for creating the original package.

This fork was started by Jason Webb, Developer Advocate at Accessible360.

Accessible360 logo with tagline Better. For All.

accessible-slick's People

Contributors

ahmadalfy avatar avr avatar badlamer avatar cielt avatar cohenudi avatar colbyfayock avatar coliff avatar czajkowski avatar engelfrost avatar flaviocysne avatar fozzleberry avatar gflateman avatar jasonwebb avatar kenwheeler avatar marcellscarlett avatar mikedamage avatar millien avatar natewiley avatar pratikbassi avatar prayagverma avatar robharper avatar sebastiancichos avatar sharshenov avatar simeydotme avatar sirknightdragoon avatar stephenway avatar tanmancan avatar taylorkonigsmark avatar tbirdsall avatar wilirius avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

accessible-slick's Issues

Improve the individual slide container markup

  • Eliminate role="tabpanel" since these don't really look like tabs, especially when multiple slides are visible at a time.
  • Remove aria-hidden="false" from all visible slides, since false is the default value and all this really does is make AT work harder for no reason.
  • Remove tabindex="-1", since slides should never receive focus anyway.
  • Add role="group" to each slide
  • Add aria-label="slide".
  • Add useGroupRole setting that can be used to turn off / on the role="group" and aria-label.
  • Add documentation about this feature change in the main README

Improve accessibility of the autoplay setting

Autoplay functionality is pretty distracting for most users, and its a downright experience breaker for screen reader users.

Typically people try to resolve these inherent UX issues by either forcing keyboard focus to move to slides when they come up (see #11) or turning the carousel into an ARIA live region that automatically announces content when slides change, but both of the approaches tend to make things worse for users with disabilities, not better. Here are some reasons why:

  1. Keyboard users rely heavily on their ability to control and track their focus position at all times, so their focus should be considered sacrosanct. Forcefully moving it is usually a very disruptive experience for them, especially when they aren't expecting it. WCAG 3.2.1, 3.2.2, and 3.2.5 all make it clear that keyboard focus should never be moved except in very specific use cases, which carousels don't quite qualify for.
  2. ARIA live regions force screen readers to read out content even if the user is in a completely different part of the page, so they should only be used in very rare situations, and generally only announce very clear, succinct messages. Making the carousel a live region will mean that screen reader users will be overwhelmed with content no matter where they are on the page, and probably won't even finish hearing an entire slide before the next slide comes in and starts being read out. Users will not know where these announcements are coming from or why they are getting them, and therefore have no idea how to make them stop. These constant announcements can make the entire page impossible to use, so a screen reader user is very likely to just leave your site and never come back.

By far the best solution to these issues is to just not have autoplay functionality at all. Unfortunately, that tends to not be practical in the real world because of the mistaken views that many leadership and marketing teams have about the efficacy of autoplay carousels.

Per WCAG 2.2.2, all auto-updating content on a page that lasts more than 5 seconds must have a mechanism for pausing, stopping, or hiding it. Therefore in order to be accessible, the autoplay setting must automatically come with a pause or stop button. Think of this like the previous/next buttons - they automatically show up by default when they are needed.

It might be a good idea to expose a new setting (like hideAutoplayPauseButton) that people can use to explicitly disable this functionality, but this should probably come with documentation and a console warning informing them of the WCAG violation.

  • Automatically add pause/play icon button as the first child of the slider when autoplay is enabled.
  • Document the change in the main README.

Jaws focus reset

Hi using Jaws Screen Reader 2021 we have a very annoying behavior. When the infinite function is active and the carousel restarts from the first slide, the focus of the screen reader is reset and returns to the top of the page or to the bottom of the page.
Can you take this test? The behavior especially in the case of a carousel with autoplay and infinite makes navigation very difficult.

Hard Coded Labels

Hi. I love the accessible sliders. It is about time we can offer them to our clients and to page builder authors. Great job!!

One issue that bothers me is the hard coded labels. All labels are implemented using aria-label. It would have been much more effective to provide a unique ID on the content of the slide and use aria-labelledby on each of the groups. The screen readers will announce the real content (text content, alt content etc.) instead of the slide number.

HTML Structure

Hello,
The carousel should have a different markup structure. Each carousel item should be a list item inside an un-ordered element ul/li.

<ul class="slick-track">
  <li class="slick-slide></li>
  <li class="slick-slide></li>
  <li class="slick-slide></li>
</ul>

Currently, just using div is an error for the accessibility recommandations.
Thanks a lot.

Is thumbnail navigation fundamentally incompatible with accessibility?

We've been using Slick 1.8 (with the Slick module For Drupal) for what I believe is a pretty common "photo gallery" use case, where one large slider is synced with a thumbnail carousel, and a user may navigate directly to a slide on the main slider by clicking a thumbnail. This functionality appears to be deliberately removed in Accessible Slick, according to:

#11

...and the synchronization example on the documentation page does indeed reflect that thumbnail navigation no longer works. The author of the Slick module for Drupal discovered this while testing a patch I worked on to add support for the Accessible Slick library to the module. There's some discussion of the deprecation in an issue here:

https://www.drupal.org/project/slick/issues/3220308

I'm hoping to confirm whether or not this is a deliberate change, or an unintended side effect of the deprecation. Do you think there's a way to restore this functionality while maintaining accessibility, or is this type of navigation just fundamentally incompatible with accessibility?

"refresh" method deletes the slides when reinitialising the slider.

I'm responsively unslicking my slider and with Slick v1.8.1 I was able to $('.my-slider').slick('refresh'); to reinitialise the slider, however with this fork, it does attempt to reinitialise the slider but all the slides are removed and the .slick-track is completely empty.

Improve the wrapper markup

  • Add role="region" to the outermost wrapper.
  • Add a generic but clear aria-label to the wrapper.
    • Consider using "carousel" as the default.
  • Consider making the aria-label configurable with a new setting.
    • This should come with some guidance about what makes an aria-label effect in this use case.

Deprecate the accessibility mode setting

Accessibility should not be something that can be turned on or off with a feature flag. Instead it is the natural result of building clean, modern, and semantic code, and should be considered a necessary core feature.

  • Remove all conditionals using the accessibility variable.
  • Remove the initADA and activateADA methods, integrating any important functionality directly into the core functionality.
    • Besides, none of this functionality is explicitly called out by the ADA (or Section 508, or AODA, or any other civil rights legislation around equitable access). Instead, this legislation (and related case law) leans on the WCAG as a technical criteria.
    • At the very least, this functionality is about basic human equality, which we shouldn't really need a law to make us do. These method names seem to imply that the important thing is that a legal requirement is being targeted and met, when really it should just be something we do automatically.
  • Once this functionality is removed, ensure that passing the accessibility setting via a config or data attribute does not introduce any bugs.
  • Consider emitting a console warning or info message when this setting is passed to alert devs about the change in functionality.
  • Add documentation of this change to the main README.

Improve accessibility of asNavFor setting

Two different sliders can be linked together using the asNavFor setting, which generally means twice the confusion for screen reader and keyboard-only users. This setting is often used on e-commerce PDP pages to display an enlarged product image next to a set of thumbnail images.

Add customization options for autoplay toggle button

A lot of folks will likely want to provide custom icons for the autoplay toggle to match the look and feel of their brand/site.

Since the autoplay button actually contains two different icon/sr-only text pairs (pause and play) that are conditionally toggled, I think it would be better to not allow an entire <button> DOM node or string to be provided so we can guarantee that the visibility toggling logic always works.

Instead, let's provide two new options that people can use to pass jQuery or DOM nodes / strings for just the icons so that they can customize the visual appearance with no risk of making the buttons inaccessible.

  • Expose new settings to allow customization of the pause and play icons (pauseIcon and playIcon).
  • Document these new options in the main README.
  • Create CodePen demo using these settings

reInit breaks tabindex

Hello,

First of all I want to thank you for your awesome work!
Secondly, I have an issue when I'm trying to dynamically change any of the carousel's options by calling setOption with refresh: true. The result is that tabindex: -1 is removed for all focusable elements in all slick-cloned that are appended at the end, so elements inside the cloned slides can be focused (also the carousel moves and breaks).

I've created an example here. I turned off the autoplay, because if the carousel moves, the issue goes away.

The issue seems to be related to reInit, which does not call updateSlideVisibility.

Thanks in advance!

Regards,
Dragos

Translatable/Extendable dot navigation instructions

There does not appear to be a way to extend the sr-only text for dot navigation.

Current markup generated the string "Go to slide' is embedded:

<ul class="slick-dots" style="">
  <li class="slick-active"><button type="button" aria-current="true">
      <span class="slick-dot-icon" aria-hidden="true"></span>
      <span class="slick-sr-only">Go to slide 1</span>
    </button>
  </li>
  <li>
    <button type="button">
      <span class="slick-dot-icon" aria-hidden="true"></span>
      <span class="slick-sr-only">Go to slide 2</span>
    </button>
  </li>
</ul>

There should be something similar to the previous next buttons where a method is provided to override this text at the time the carousel is instantiated.

For example here I am instantiating a carousel within Drupal and utilizing Drupal's translate function to provide alternative language support for the previous/next buttons:

var srPrevious = '<span class="slick-sr-only">' + Drupal.t('Previous') + '</span>';
var srNext = '<span class="slick-sr-only">' + Drupal.t('Next') + '</span>';
$('#accessible-carousel').slick({
        dots: true,
        slidesToShow: 2,
        slidesToScroll: 1,
        prevArrow:'<button class="slick-prev slick-arrow" type="button"><i class="fa fa-chevron-circle-left" aria-hidden="true"></i>' + srPrevious + '</button>',
        nextArrow:'<button class="slick-next slick-arrow" type="button"><i class="fa fa-chevron-circle-right" aria-hidden="true"></i>' + srNext + '</button>',
});

the above works well, I just wish the slide dots had a similar feature. In addition to that it would be great if the goto slide text was more descriptive (maybe include a title for that slide). Current workaround is to add a slide description data-element on the initial slide div then use javascript to lift and shift the contents of that to update the slide dot sr-only text.

Pause button inline display style "inline" causses issue

When the button is visible I want it to be display: flex.

The inline style overrides this, I think there should just be no style added like it is for the default.

The only work around right now is to do this:

.slick-pause-icon, 
.slick-play-icon {
  display: flex;
}

.slick-pause-icon, 
.slick-play-icon {
  &[style='display: inline;'] {
    display: flex !important;
  }
}

Consider accepting non-accessibility contributions?

I was about to make a PR but noticed you're only accepting accessibility contributions. Slick has been inactive for 2 years now and have a lot of issues and PR waiting open. Your repo have a working build, is active and by being accessible can be considered superior quality. I understand this would mean more work, but would you consider accepting non-accessibility related contributions?

Method 'slickPause' not compatible with Play/Pause button

Clicking on the play/pause button triggers the autoPlayToggleHandler function which toggles between the two buttons.
This logic is not called when calling the slickPause method. This can be easily fixed by moving the logic for the play/pause buttons directly into the slickPlay and slickPause function...

Original code:

Slick.prototype.autoPlayToggleHandler = function() {
        var _ = this;

        if(_.paused) {
            _.$playIcon.css('display', 'none'); //to be extracted to slickPlay
            _.$pauseIcon.css('display', 'inline');  //to be extracted to slickPlay

            _.$pauseButton.find('.slick-play-text').attr('style', 'display: none');  //to be extracted to slickPlay
            _.$pauseButton.find('.slick-pause-text').removeAttr('style');  //to be extracted to slickPlay

            _.slickPlay();
        } else {
            _.$playIcon.css('display', 'inline');  //to be extracted to slickPause
            _.$pauseIcon.css('display', 'none');  //to be extracted to slickPause

            _.$pauseButton.find('.slick-play-text').removeAttr('style');  //to be extracted to slickPause
            _.$pauseButton.find('.slick-pause-text').attr('style', 'display: none');  //to be extracted to slickPause

            _.slickPause();
        }
    };

I have solved this exactly as I was suggesting above. I have moved the toggle logic into slickPlay and slickPause functions respectively, which means calling slickPause manually triggers the slickPause function which now also toggles the button states. Works as expected!

New code for Slick.prototype.autoPlayToggleHandler:

Slick.prototype.autoPlayToggleHandler = function() {
        var _ = this;

        if(_.paused) {
            _.slickPlay();
        } else {
            _.slickPause();
        }
    };

New code for Slick.prototype.slickPause

Slick.prototype.pause = Slick.prototype.slickPause = function() {

        var _ = this;

        if ( _.options.autoplay){ //added
          _.$playIcon.css('display', 'inline'); //added
          _.$pauseIcon.css('display', 'none'); //added

          _.$pauseButton.find('.slick-play-text').removeAttr('style'); //added
          _.$pauseButton.find('.slick-pause-text').attr('style', 'display: none'); //added
        } //added

        _.autoPlayClear();
        _.paused = true;

    };

New code for Slick.prototype.slickPlay

    Slick.prototype.play = Slick.prototype.slickPlay = function() {

        var _ = this;

        if ( _.options.autoplay){ //added
          _.$playIcon.css('display', 'none'); //added
          _.$pauseIcon.css('display', 'inline'); //added

          _.$pauseButton.find('.slick-play-text').attr('style', 'display: none'); //added
          _.$pauseButton.find('.slick-pause-text').removeAttr('style'); //added
        } //added

        _.autoPlay();
        _.options.autoplay = true;
        _.paused = false;
        _.focussed = false;
        _.interrupted = false;

    };

Create a more accessible theme with better focus indicators

Currently the focus indicators on the previous / next buttons and slide dots are not very ineffective for most people with vision impairments. All that happens is that their opacity is changed, but that change is very subtle and lacks an outline, border, or shadow that many people would expect.

WCAG 2.4.7 says that focus indicators must be "visible", but does a pretty bad job of defining what exactly "visible" means. Technically a subtle change in opacity might be argued as compliant with the exact wording of the guideline, but it clearly does not honor the spirit of the guideline. In a legal scenario, it could really come down to which side has the better lawyers. Why even risk it? Let's add great focus indicators so legal teams don't have to split hairs!

Unfortunately, adding effective focus indicators would be a very noticeable change that could design / dev teams that are used to the current styles. So rather than introducing these indicators to the main slick-theme.scss file, it might be a better idea to spin up an alternative stylesheet and promoting it over the original (without replacing it) for teams who want it.

  • Create a new theme source file (accessible-slick-theme.scss?).
  • Improve focus indicators for the Previous and Next buttons.
  • Improve focus indicators for the slide dots.
  • Ensure the new Sass file is built properly by all the build systems.
  • Add documentation to the main README, with installation instructions.

Conduct testing with users with disabilities

Before publishing this package out to NPM and elsewhere (#3), it'd be a good idea to do some final testing with users with disabilities to verify that the package is feature-complete (at least for an initial launch).

At Accessible360, a number of our expert auditors are blind or visually impaired, so I will work on snagging some time with them as soon as is feasible to test out key samples from the demo page and the CodePen collection.

  • Demo page samples
    • Single item
    • Multiple items
    • Responsive display (with disabled arrows)
    • Autoplay
    • Center mode - any ideas for conveying the center slide to screen reader users?
  • CodePen samples
    • Dual image sliders for e-commerce PDP main image.
    • Product cards
    • Hero banner

Element classes and labels break when using centerMode with useGroupRole

The addition of the useGroupRole option check in Slick.prototype.setSlideClasses ends up assigning allSlides to nothing.

        allSlides = _.$slider
            .find('.slick-slide')
            .removeClass('slick-active slick-center slick-current')
            .attr('aria-hidden', 'true');

        if (_.options.useGroupRole !== false && _.options.centerMode === true) {
            allSlides = allSlides
                .find('slick-slide')
                .attr('aria-label', function() {
                    return $(this).attr('aria-label').replace(' (centered)', '');
                });
        }

This breaks centerMode from clearing the "(centered)" labelling, and looking for .slick-slide within allSlides yields no results because it's already a listing of matching slides.

Changing the body of that conditional to the following fixed these issues for me:

            allSlides
                .attr('aria-label', function() {
                    return $(this).attr('aria-label').replace(' (centered)', '');
                });

UseGroupRole = false generate an error

Hello :)

I am using slick accessible but when I add option : useGroupRole: false it generates a JS error.

Cannot read property 'replace' of undefined

Any solution? Here is my code :)

$('.carousel-cards').slick({
      dots: true,
      arrows: true,
      slidesToShow: 4,
      infinite: true,
      autoplay: true,
      regionLabel: 'caroussel',
      useGroupRole: false,
      responsive: [{
          breakpoint: 3000,
          settings: {
            slidesToShow: 3
          }
        },
        {
          breakpoint: 800,
          settings: {
            slidesToShow: 2
          }
        },
        {
          breakpoint: 600,
          settings: {
            slidesToShow: 1
          }
        }
      ],
      prevArrow: '<button class="previous-button is-control">' +
        '  <span class="fas fa-angle-left-white" aria-hidden="true"></span>' +
        '  <span class="sr-only">Diapositive précédente</span>' +
        '</button>',
      nextArrow: '<button class="next-button is-control">' +
        '  <span class="fas fa-angle-right-white" aria-hidden="true"></span>' +
        '  <span class="sr-only">Diapositive suivante</span>' +
        '</button>',
    });

Thanks a lot!

Slider Syncing

The original version of Slider Syncing using the navFor option allows you to click on the nav items and that interaction subsequently changes the slide being displayed. I noticed that this "on click" feature no longer exists on the accessibility version, was this by design or is that perhaps a bug?

It would be nice if slides could be clickable anchor elements

If you have as initial structure like:

<div class="clickable-slick-slides">
  <a href="#slide1>
    <img src="/img/slideimage.jpg" />
    <h3>Slide Heading</h3>
    <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do.</p>
    <span>Call to action</span>
  </a>
  <a href="#slide2>
    <img src="/img/slideimage.jpg" />
    <h3>Slide Heading</h3>
    <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do.</p>
    <span>Call to action</span>
  </a>
  <a href="#slide3>
    <img src="/img/slideimage.jpg" />
    <h3>Slide Heading</h3>
    <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do.</p>
    <span>Call to action</span>
  </a>
</div>

Initially the slides are keyboard focusable but new slides that scroll into view (after arrow or dot navigation) are not.

Remove keyboard navigation functionality

Currently the Left and Right arrow keys can be used to move between slides, but this functionality is (at best) really unintuitive for real keyboard-only users and (at worst) unusable for screen reader users. Very few carousels have this functionality, and no visible instructions are provided to explain it, so most users would have no reason to believe it exists.

Additionally, the Left and Right arrow keys are already used by the most popular screen readers (JAWS and NVDA) for virtual cursor navigation to move character-by-character through content. Just by using their virtual cursor normally, screen reader users can potentially trigger slide changes - obviously that's a problem!

  • Remove keyHandler method.
  • Remove all references to keyHandler (on prev/next buttons, slide dots, and slides/track).
  • Document this change in the main README.

Empty ID attributes marked as errors by W3C validator

When cloning slides ID attributes of contained elements get cleared, but not removed. validator.w3.org marks those as error or "duplicate id" respectively. This can be easily fixed by using *.removeAttr('id') instead of *.attr('id', '').

Improve the slide dot markup and behavior

The most important change that needs to be made is that all tab-related markup and behaviors needs to be removed, since carousels do not visually resemble tabs at all and therefore would not be expected to function that way by real sighted keyboard and screen reader users.

Wrapper and structural elements

  • Remove role="tablist" from the wrapper <ul>.
  • Remove role="presentation" from the <li>s.

Each slide dot button

  • Remove role="tab" from each slide dot button.
  • Remove aria-controls from each slide dot button.
  • Remove tabindex from each button, since a roving tabindex is not appropriate for carousels.
  • Remove aria-selected from the active slide dot button.
  • Add aria-current="true" to the button that is currently "active".
  • Move the icon to an inner DOM element so that it can be hidden from screen readers using aria-hidden="true".
  • Wrap the inner text in a <span> and apply CSS to make it visible to screen readers only.
  • Update the inner text of each button to use the format "Go to slide [x]" to make it more clear that these are actionable controls, not slides in themselves.
  • Remove the aria-label from each slide dot button once the above changes are made, since the inner text will act as a more robust accessible name (see the First Rule of ARIA Use).

Documentation

  • Add note about feature change in the main README

Currently the buttons and their wrapper list are marked up like so:

<ul class="slide-dots" role="tablist">
  <li class="slick-active" role="presentation">
    <button type="button" role="tab" id="slick-slide-control00" aria-controls="slick-slide00" aria-label="1 of 6" tabindex="0" aria-selected="true">
      :before
      1
    </button>
  </li>
  <li role="presentation">
    <button type="button" role="tab" id="slick-slide-control01" aria-controls="slick-slide02" aria-label="2 of 6" tabindex="-1">
      :before
      2
    </button>
  </li>
  ...
</ul>

When what we really want is:

<ul class="slide-dots">
  <li class="slick-active">
    <button type="button" id="slick-slide-control00" aria-current="true">
      <span class="slide-dot-icon" aria-hidden="true"></span>
      <span class="slick-sr-only">Go to slide 1 of 6</span>
    </button>
  </li>
  <li>
    <button type="button" id="slick-slide-control01">
      <span class="slide-dot-icon" aria-hidden="true"></span>
      <span class="slick-sr-only">Go to slide 2 of 6</span>
    </button>
  </li>
  ...
</ul>

Add new setting for optional sr-only instructions

For most use cases, the addition of ARIA attributes likerole="region" on the wrapper, role="group" on the slides, and the removal of the tab markup on the slides and slide dots is enough to clearly convey the structure and behaviors of a slider.

However, sometimes sliders are used in strange ways that cannot be conveyed through abstract ARIA attributes, like when the asNavFor mode (slider syncing) is used to connect multiple sliders together. If you have used custom JavaScript to add functionality to a slider, or have connected two or more sliders together somehow (like thumbnail images and main images on an e-commerce PDP), be sure to explain this functionality to screen reader users!

  • Add new instructionsText setting that takes an arbitrary string and places it at the very beginning of the slider as screen-reader-only-text.
  • Document this new setting in the main README.
  • Use it in at least one example in docs/index.html.

Keyboard focus must be moved when buttons become disabled dynamically

Paul Grenier identified an issue dealing with focus management that needs to be resolved.

The issue

Keyboard focus gets lost when a Previous or Next arrow button becomes disabled at the beginning or end of a set of slides in a slider that does not use infinite scrolling.

Steps to replicate:

  1. Go to the demo page and navigate down to the Responsive Display demo using your keyboard (using Tab).
  2. Use your Tab key to reach the Next arrow button.
  3. Activate the button until you reach the last slide.
  4. Note that the keyboard focus is lost.

What happens

Notice that the keyboard focus becomes "lost" when the Next (or Previous) arrow button become disabled. This is because the native disabled attribute is being added, which removes the button from the page's tab sequence automatically. In the original Slick Slider, the aria-disabled attribute was used instead, but this was thought to be not as preferable as disabled due to the First Rule of ARIA Use.

What to do about it

It is clear that keyboard focus needs to be programmatically moved to a nearby element when a focused Previous or Next arrow becomes disabled. However, since sliders can vary tremendously in their configurations and content, it is not clear right now where the focus should be moved to. I'll do some investigation with my team over at Accessible360, but if anyone has ideas I'd love to it hear them!

I'm leaning towards either adding tabindex="-1" to the wrapper element and moving focus there, or reverting back to using aria-disabled.

An easy way to translate slick-sr-only dots ?

Dear Jason,
Thanks for your amazing accessible library!

I would like to use it on multi languages website but I need to translate wording inside dots <span class="slick-sr-only">Go to slide 9</span>

Do you have any suggestions or API able to do this?

Thanks a lot :)

When the slider items are focusable, there should be an easy way for the keyboard user to skip tabbing through each focusable item, to move to the next focusable element on the page

If there is a slider with 10 items, similar to the product items slider in codepen examples: https://codepen.io/A360/pen/mdPBdEz
then by default all the items would be part of the tab navigation flow, so when the user tabs into the slider, they will tab into the nav buttons, and then on to each individual slide. This might be ok for smaller lists, but if it is a bigger list, the user would be frustrated as they need to tab through each item to finally skip the slider and go to the next focusable thing on the page.

Is that something that has been considered? Is it possible to do that in the current setup ?

(This is generally solved by roving tab index, or aria-activedescendent in some cases)

Browser resize processor issue?

When using accessible slick and I resize my browser window (on macOS Big Sur) or emulate different screensizes via the device toolbar via dev tools, browsers take more than 100% of CPU (using Chrome, Safari and FF). As it happens so quickly, I wasn't able to determine the root cause. Eventually, they just die. If I switch back to a kenwheeler/slick implementation using the same config, I don't experience the same issue.

I'd be interested in understanding if others are experiencing the same/similar issue?

Note, I'm unable to reproduce this condition at ...
https://accessible360.github.io/accessible-slick/
... so, I expect the behavior stems from something else interacting w/ slick on the site where I was testing it. Just odd that I couldn't reproduce it with similar config for vanilla slick.

Was just hoping someone else might have some thoughts.

Issue with backwards compatibility with original slick slider

Hello, I am trying to replace slick slider 1.8.0 in a project and so far it seems the JS is 100% backwards compatible so great job there! But the HTML is not. With slick slider each immediate child of the slick container became a .slick-slide, but with accessible slick each immadiate child becomes a child of a child of a .slick-slide on init. What is up with that? Is there any accessibility reasoning behind this? I am wondering if I should rewrite the accessible-slick JS and remove this logic, but if it will break some accessibility then I won't...

With slick slider:
<div class="slick-slider"><div class="custom-class">text</div></div>
becomes

<div class="slick-slider">
  <div class="slick-list">
    <div class="slick-track">
      <div class="slick-slide custom-class">text</div>
    </div>
  </div>
</div>

With accessible slick:
<div class="slick-slider"><div class="custom-class">text</div></div>
becomes

<div class="slick-slider">
  <div class="slick-list">
    <div class="slick-track">
      <div class="slick-slide">
        <div>
          <div class="custom-class" style="width:100%;display:inline-block">text</div>
        </div>
      </div>
    </div>
  </div>
</div>

Additionally, the original item gets inline styles for width and display. The width was there even in slick-slider, but it was set on the .slick-slide. Why are these inline styles set on the child item, like why force the div to be display: inline-block and not just leave it as block?

Improve the Previous and Next button markup

  • Move the icon to an inner DOM element so that it can be hidden from screen readers using aria-hidden="true".
  • Wrap the inner text in a <span> and apply CSS to make it visible to screen readers only.
  • Remove the aria-label once the above changes are made, since the inner text will act as a more robust accessible name (see the First Rule of ARIA Use).
  • Document changes in the main README

Currently the buttons render like so:

<button class="slick-prev slick-arrow" aria-label="Previous" type="button">
  :before
  Previous
</button>

Where an unrecognizable (to screen readers) custom font character is injected via the :before pseudo-class.

What we really want is this:

<button class="slick-prev slick-arrow" type="button">
  <span class="slick-previous-icon" aria-hidden="true"></span>
  <span class="slick-sr-only">Previous</span>
</button>

For reference, see this nice section about icon buttons in the MDN's documentation for the <button> element.

Update the demo page (index.html)

Demo page viewable at: https://accessible360.github.io/accessible-slick/

This should have a similar format to the original package's doc page so its nice and familiar, but without feeling like a rip-off.

  • Set up Github Pages for this repo
  • Update index.html to match the content being served on the original Slick Slider repo.
  • Move index.html to docs/ so supporting files can be added without cluttering up the root folder.
  • Update the color scheme to differentiate it from the original doc page.
  • Fix the heading structure (there is a skipped heading above Single Items)
  • Hide <hr> tags from screen readers to cut down on noise.
  • Remove the heading markup around the "and a whole lot more..." pseudo-heading, since it isn't semantically a heading.
  • Add HTML5 landmark elements (<header>, <nav>, and <main>).
  • Add a "skip to main content" link.
  • Implement better focus styles throughout.
  • Add a "Sponsored by" section at the top.

Deprecate focusOnChange and focusOnSelect settings

Per WCAG 3.2.2, keyboard focus should not be moved automatically when a user interacts with a UI component (like a prev/next button or slide dot button) unless they have been informed of this behavior ahead of time.

To fulfill the criteria we could add some warning text to tell users about this behavior ahead of time, but this text would need to be visible to be effective for sighted / low-vision keyboard-only and screen reader users. This would annoy a lot of designers, and could be considered a breaking change that would make this package undesirable for real teams, so that doesn't seem like the way to go.

From a usability perspective, real users would not expect their focus to be forcefully moved onto non-actionable elements anyway, so the best solution here is really to eliminate this behavior altogether.

It is critical that these settings are not deprecated in a way that breaks existing configurations. Be sure to thoroughly test that configs that pass these settings in via the init method or data attributes do not cause any errors. Instead they should just be silently ignored.

  • Remove all functionality associated with these two settings in the JavaScript without introducing any breaking changes. In other words, if an existing config is passing in these values they should just be ignored, not cause the slider to break.
  • Consider emitting a console.warn or console.info message if the option has been passed in an existing config to let devs know that this functionality has changed.
  • Add documentation about these changes to the main README.

Simplify the repo by removing unnecessary files

  • Remove CONTRIBUTING.markdown.
  • Remove ISSUE_TEMPLATE.md.
  • Remove /slick/config.rb
  • Remove the LESS versions of each stylesheet (/slick/slick-theme.less and /slick/slick.less) so that the Sass versions (*.scss) are the single source of truth going forward.
  • Remove the unminified CSS files and replace them with minified *.min.css versions built from the *.scss files. Hopefully this will also make it more clear that the *.scss files are the true source.

Replace the build system with more modern solutions

  • Delete component.js, since it looks like ComponentJS is now deprecated.
  • Delete Makefile in favor of cross-platform build systems (below).
  • Create a gulpfile.js file for people who prefer Gulp.
  • Create a gruntfile.js file for people who prefer Grunt.
  • Create a webpack.config.js for people who prefer Webpack (if feasible).
  • Consider creating a Parcel build file.

Set up a playground on CodePen

This depends on #3. Once the package has been published to NPM, it can be referenced as an external asset in CodePen.

  • Create a collection of CodePen
  • Link to it at the top of the README

Question: best option for new development of accessible carousels?

Hi,

I was wondering if you had any opinions on the best way to create accessible content of this kind when not trying to re-use existing libraries. You have this slick.js fork and some suggestions for other popular libraries that have existed for a while in your codepen, but it seems like the bottom line is that if given the choice you wouldn't use these approaches.

Is there another approach you'd recommend for people not already tied to an existing library to achieve similar goals and would there be any benefit in mapping out an upgrade path for people currently using slick (and/or other libraries)? For example, do any of the new CSS scroll-snap properties help from an accessibility point of view?

Add option to allow control of where the prev/next arrows are placed

Depending on the visual design and the number of slides shown for any given carousel, the logical tab sequence of all the controls and slide content can vary. Though we do always know how many slides are shown at any time, we can't really infer the visual design in order to programmatically move controls around in the DOM to be logical.

Let's create a new option that developers can use to make accessible-slick place the arrow icons at particular places in the DOM structure of the carousel. I'm thinking the following places are most useful:

  1. Place both arrow buttons before the slides.
  2. Place both arrow buttons after the slides.
  3. Place the previous button before the slides, and the next button after the slides.
  • Expose a new option (arrowPlacement?) that accepts a string that needs to match one of the following: "beforeSlides", "afterSlides", "split".
  • Document this new option and accepted values in the main README
  • Create a CodePen demo using this new setting.

Update the main README

  • Use a familiar format (at least at the beginning) so its easier for devs to directly swap their package references (jsDelivr URLs, pacman commands, etc).
  • Update the jsDelivr URLs and npm package name once published (see #3).
  • Add a short, clear intro that explains the intent of the repo and links to important sections below.
  • Add a "Why is this needed?" section
  • Add a "What makes this accessible?" section listing out all feature additions and changes.
  • Add a "Usage" section that piggy-backs on the original Slick Slider documentation.
  • Add a "Development" section that explains the updates made to the repo and build system, and how to build it locally.
  • Add a "Contributing" section.
  • Add a "Credits" section.
    • Ken Wheeler, contributor community, and Accessible360 (logo).

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.