Skip to content

[Carousel] Accessibility improvments - v2#3018

Open
renow-luxembourg wants to merge 2 commits intoadobe:mainfrom
renow-luxembourg:renow_fix_carousel2
Open

[Carousel] Accessibility improvments - v2#3018
renow-luxembourg wants to merge 2 commits intoadobe:mainfrom
renow-luxembourg:renow_fix_carousel2

Conversation

@renow-luxembourg
Copy link
Copy Markdown
Contributor

Here is a simplify version of the following PR: #2930

This PR doesn't modify the DOM structure to avoid any backwards compatibility issues.

Suggestions that are regression-free to improve accessibility :

  • Add the title attribute to the action buttons (previous, next, play and pause)
  • Remove the double speech by deleting one of the aria-live attribute
  • Correct accessible name of the the tabpanel by removing the aria-labelledby attribute (an aria-label is already present)
  • Add the tabindex attribute to the tabpanel to improve the keyboard navigation
  • Initially add the tabindex and aria-selected attributes to the tab to improve the keyboard navigation
  • Add a title attribute to the indicator tabs buttons

@codecov
Copy link
Copy Markdown

codecov bot commented Mar 11, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@sonarqubecloud
Copy link
Copy Markdown

id="${carousel.id}"
class="cmp-carousel"
role="group"
role="region"
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Switching from role="group" to role="region" makes sense here since the carousel is a distinct section. Just wondering if we should ensure carousel.accessibilityLabel is always present so the region doesn’t end up unnamed for screen readers.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@renow-luxembourg , do we really need this role change? This component does not guarantee an accessible name because accessibilityLabel is optional, so switching from group to region can turn existing unlabeled carousels into unnamed component. It may be safer to keep it as is.

Copy link
Copy Markdown
Contributor Author

@renow-luxembourg renow-luxembourg Mar 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The role=region attribute value is recommended by the W3C design pattern.
I think that it's only a nice to have.

role="group"
role="region"
aria-label="${carousel.accessibilityLabel}"
aria-live="polite"
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removing aria-live="polite" seems like a good change. Auto-rotating carousels can otherwise keep announcing updates and become noisy for screen reader users.

id="${item.id}-tabpanel"
class="cmp-carousel__item${item.name == carousel.activeItem ? ' cmp-carousel__item--active' : ''}"
role="tabpanel"
aria-labelledby="${item.id}-tab"
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The new "Slide X of Y" label is clearer than relying on aria-labelledby. Quick question: does itemList.count start at 1 here? Just checking to avoid the first slide being announced as “Slide 0”.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This may break the tab to panel labeling relationship. Could we avoid this?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The aria-labelledby attribute has a higher priority than the aria-label attribute in calculating the accessibility name.

data-cmp-data-layer="${item.data.json}"
data-cmp-hook-carousel="item"></div>
data-cmp-hook-carousel="item"
tabindex="-1"></div>
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I noticed tabindex="-1" was added to the slide container. Is this used for programmatic focus when slides change? Just wanted to confirm the intent.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The tabindex attribute of the tabpanel allows the focus to be visible when this element receives focus programmatically.

<button class="cmp-carousel__action cmp-carousel__action--previous"
type="button"
aria-label="${carousel.accessibilityPrevious || 'Previous' @ i18n}"
title="${carousel.accessibilityPrevious || 'Previous' @ i18n}"
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Adding the title attribute on the navigation buttons looks helpful for hover tooltips. Since we already have aria-label, this shouldn’t affect accessibility behavior, right?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The aria-label attribute takes precedence over the title attribute for calculating the accessibility name. Therefore, there will be no difference for the speaker tool reading the text. However, mouse users can obtain additional information by hovering their cursor over the text.

@LSantha
Copy link
Copy Markdown
Contributor

LSantha commented Mar 19, 2026

@renow-luxembourg , you may need to adjust ComponentsIT.testCarousel() since the markup is different now. Please try to execute the ITs locally and see.

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

Successfully merging this pull request may close these issues.

4 participants