Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enforce ARIA rule #1535

Open
diazmaria opened this issue Apr 15, 2019 · 20 comments
Open

Enforce ARIA rule #1535

diazmaria opened this issue Apr 15, 2019 · 20 comments

Comments

@diazmaria
Copy link

DESCRIPTION

We can find slick-slides that have aria-hidden="true" and tabindex="-1" attributes. This breaks the ARIA rules to have any tabindex value (indicating the element is focusable) whilst also having the hidden flag for an element.

Found with 'Axe' Chrome extension to flag accessibility issues
Docs: https://dequeuniversity.com/rules/axe/3.2/aria-hidden-focus?application=AxeChrome

Replicate here: https://codesandbox.io/s/ppwkk5l6xx

CURRENT:

<div data-index="2" class="slick-slide" tabindex="-1" aria-hidden="true" style="outline: none; width: 430px;"></div>

EXPECTED:

<div data-index="2" class="slick-slide" aria-hidden="true" style="outline: none; width: 430px;"></div>

Solution: tabindex attibute should be removed when aria-hidden="true"

@Mikeks81
Copy link

What would be the solution to prevent keyboard tabbing to those elements? According to the documention that you linked to it's ok to use tabindex -1 . It's when you have a tabindex of 0 and aria-hidden = true that you violates the Axe recommendation. Maybe there is a bug with Axe reporting aria-hidden=true and tabindex=-1??

@diazmaria
Copy link
Author

diazmaria commented Apr 23, 2019

Divs aren't focusable elements unless we add the tabindex so by just not adding it the problem should be solved.

As per in the documentation:
The aria-hidden="true" elements do not contain focusable elements rule

Content made unfocusable through tabindex:

<div aria-hidden="true">
   <button tabindex="-1">Some button</button>
</div>

This particular example is different, there is a parent element, the div, that has aria-hidden=true and it's child, the button, tabindex="-1". There's no example as the one above

@simonsmith
Copy link

simonsmith commented May 30, 2019

I think the main issue here is that when aria-hidden="true" is applied to the container it is necessary to add tab-index="-1" to any elements contained within (links, buttons etc). Feels like we need a way to hook into knowing which slides are currently visible so this could be added

@ksb86
Copy link

ksb86 commented Jul 2, 2019

Also seeing this issue. We are required to maintain a certain level of accessibility standing.
In additions to @simonsmith 's suggestion, it's necessary to remove the tab-index attribute from the parent element if the aria-hidden attribute is present.

@alldrops
Copy link

Any updates on this issue? Is someone actively working on this? I'm also experiencing the same problem and the removal of tabindex="-1" would make life easier

@JoeMethven
Copy link

Is there any update on this? I am getting google lighthouse accessibility ranked down due to this issue.

@ki1
Copy link

ki1 commented Nov 4, 2020

I am also getting this issue. I think the suggested fix is not correct. As you can use tab to move the hidden slides into view, then the aria-hidden attribute should not be set on those slides.

@aganglada
Copy link

aganglada commented Dec 29, 2020

Hey @diazmaria!

Just fixed the problem externally by adding a hidden attribute to the container of non-active slides.

  1. Hold activeSlide => const [activeSlide, setActiveSlide] = useState(0)
  2. Hook into beforeChange={(_, next) => setActiveSlide(next)} on react-slick
  3. Apply hidden={activeSlide !== i ? true : undefined} on the container of the slide.

Reference:
https://web.dev/aria-hidden-focus/#how-to-fix-partially-hidden-focusable-elements

@eduinfo96
Copy link

eduinfo96 commented Jan 12, 2021

Hey @diazmaria!

Just fixed the problem externally by adding a hidden attribute to the container of non-active slides.

  1. Hold activeSlide => const [activeSlide, setActiveSlide] = useState(0)
  2. Hook into beforeChange={(_, next) => setActiveSlide(next)} on react-slick
  3. Apply hidden={activeSlide !== i ? true : undefined} on the container of the slide.

Reference:
https://web.dev/aria-hidden-focus/#how-to-fix-partially-hidden-focusable-elements

@aganglada Thanks but, how do you hook into beforeChange in react-slick? And what do you mean by "Hold" in step one?

@aganglada
Copy link

aganglada commented Jan 12, 2021

do you hook into beforeChange

Passing beforeChange={(_, next) => setActiveSlide(next)} to the Slider

what do you mean by "Hold" in step one?

Using const [activeSlide, setActiveSlide] = useState(0) on your component

@mindmergedesign
Copy link

Hey @diazmaria!
Just fixed the problem externally by adding a hidden attribute to the container of non-active slides.

  1. Hold activeSlide => const [activeSlide, setActiveSlide] = useState(0)
  2. Hook into beforeChange={(_, next) => setActiveSlide(next)} on react-slick
  3. Apply hidden={activeSlide !== i ? true : undefined} on the container of the slide.

Reference:
https://web.dev/aria-hidden-focus/#how-to-fix-partially-hidden-focusable-elements

@aganglada Thanks but, how do you hook into beforeChange in react-slick? And what do you mean by "Hold" in step one?

I believe there's a typo here hidden={activeSlide !== i ? true : undefined} i should be 1 hidden={activeSlide !== 1 ? true : undefined}

adamoliveri added a commit to rue21/react-slick that referenced this issue May 27, 2021
akiran#1535

Remove tabindex when aria-hidden="true" in track.js
Update snapshots
adamoliveri added a commit to rue21/react-slick that referenced this issue May 28, 2021
akiran#1535

Set tabindex to -1 when aria-hidden="true" in track.js
Update snapshots
@fanczerni
Copy link

3. Apply hidden={activeSlide !== i ? true : undefined} on the container of the slide.

Hi @aganglada , how can I apply this property on the container of the slide? Plugin adds two divs above each slide, so it doesn't work for me. :/

@oh-roman
Copy link

oh-roman commented Jul 2, 2021

@fanczerni
Just fixed the problem externally by adding a hidden attribute to the container of non-active slides.

  1. Install https://www.npmjs.com/package/patch-package
  2. In node_modules/react-slick/lib/track.js replace all
    style: _objectSpread(_objectSpread({}, child.props.style || {}), childStyle),
    to
    style: _objectSpread(_objectSpread({ visibility: !slideClasses["slick-active"] ? "hidden" : "visible", }, child.props.style || {}), childStyle)
    (As for me its line 192 and 219)
  3. Replace (in line 161)
    style: _objectSpread(_objectSpread({ outline: "none" }, child.props.style || {}), childStyle)
    to
    style: _objectSpread(_objectSpread({ outline: "none", visibility: !slideClasses["slick-active"] ? "hidden" : "visible", }, child.props.style || {}), childStyle)
  4. Create and aply this patch with patch-package

@gilhanan
Copy link

gilhanan commented Jul 11, 2021

@fanczerni @aganglada @oh-roman

When there is more than one slide to show, managing the active slides for hidden others is too complicated when there are responsive settings for slides to show. So this code solved that:

componentDidUpdate() {
  this.hideAriaHiddenTiles();
}

const settings: Settings = {
  afterChange: () => this.hideAriaHiddenTiles(),
}

hideAriaHiddenTiles() {
  Array.from(document.querySelectorAll('.slick-slide')).forEach((slide: HTMLElement) => {
    slide.style.visibility = slide.classList?.contains('slick-active') ? 'visible' : 'hidden';
  });
}

@mtergel
Copy link

mtergel commented Aug 4, 2021

Is there any update on this? I am getting google lighthouse accessibility ranked down due to this issue.

@bublinec
Copy link

bublinec commented May 3, 2022

I made a small extension for showing also the slide being replaced while the animation is happening:


const [activeSlides, setActiveSlides] = useState([0]);
  settings.beforeChange = (_, next) => setActiveSlides([...activeSlides, next]);
  settings.afterChange = (currentSlide) => setActiveSlides([currentSlide]);

hidden={activeSlides.includes(i) ? undefined : true}

@Grandnainconnu
Copy link

@fanczerni Just fixed the problem externally by adding a hidden attribute to the container of non-active slides.

  1. Install https://www.npmjs.com/package/patch-package
  2. In node_modules/react-slick/lib/track.js replace all
    style: _objectSpread(_objectSpread({}, child.props.style || {}), childStyle),
    to
    style: _objectSpread(_objectSpread({ visibility: !slideClasses["slick-active"] ? "hidden" : "visible", }, child.props.style || {}), childStyle)
    (As for me its line 192 and 219)
  3. Replace (in line 161)
    style: _objectSpread(_objectSpread({ outline: "none" }, child.props.style || {}), childStyle)
    to
    style: _objectSpread(_objectSpread({ outline: "none", visibility: !slideClasses["slick-active"] ? "hidden" : "visible", }, child.props.style || {}), childStyle)
  4. Create and aply this patch with patch-package

This works but not correctly for infinite, it'd require some additional modifications

@coolsoftwaretyler
Copy link

Hopping in here to say we're also experiencing this issue about a year later from the last comment. Thanks for all your work and attention here. Hopefully something gets fixed soon.

@girishboke
Copy link

girishboke commented Jun 22, 2023

It is still open even after 2+ years.
Is there proper fix available for this issue?
The accessibility check fails due to this issue and Lighthouse is showing the same.

@alieffring
Copy link

@fanczerni @aganglada @oh-roman

When there is more than one slide to show, managing the active slides for hidden others is too complicated when there are responsive settings for slides to show. So this code solved that:

componentDidUpdate() {
  this.hideAriaHiddenTiles();
}

const settings: Settings = {
  afterChange: () => this.hideAriaHiddenTiles(),
}

hideAriaHiddenTiles() {
  Array.from(document.querySelectorAll('.slick-slide')).forEach((slide: HTMLElement) => {
    slide.style.visibility = slide.classList?.contains('slick-active') ? 'visible' : 'hidden';
  });
}

This more or less fixed the issue for me, but the way that new slides advanced while hidden and then popped into visibility after the animation wasn't acceptable. I ended up going with this:

let toggleSlideVisibility = function (event, slick, currentSlide, nextSlide) {
  Array.from(document.querySelectorAll('.slick-slide')).forEach((slide) => {
    slide.style.visibility = slide.classList?.contains('slick-active') ? 'visible' : 'hidden';
  });
}

paragraphSlideShow.on('init', toggleSlideVisibility).slick({
// config settings
}).on('beforeChange', function(event, slick, currentSlide) {
  Array.from(document.querySelectorAll('.slick-slide')).forEach((slide) => {
    slide.style.visibility = 'visible';
  });
}).on('afterChange', toggleSlideVisibility);

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests