-
Notifications
You must be signed in to change notification settings - Fork 34
Improve scrollable carousel and onwards content carousel accessibility #15390
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
Changes from all commits
6f62ab9
bdf2332
0e5badb
8c7e55a
5e63e17
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -16,6 +16,7 @@ type Props = { | |
| sectionId?: string; | ||
| shouldStackCards?: { desktop: boolean; mobile: boolean }; | ||
| gapSizes?: GapSizes; | ||
| isBelowTabletBreakpoint?: boolean; | ||
| }; | ||
|
|
||
| /** | ||
|
|
@@ -234,6 +235,7 @@ export const ScrollableCarousel = ({ | |
| sectionId, | ||
| shouldStackCards = { desktop: false, mobile: false }, | ||
| gapSizes = { column: 'large', row: 'large' }, | ||
| isBelowTabletBreakpoint = false, | ||
| }: Props) => { | ||
| const carouselRef = useRef<HTMLOListElement | null>(null); | ||
| const [previousButtonEnabled, setPreviousButtonEnabled] = useState(false); | ||
|
|
@@ -369,7 +371,7 @@ export const ScrollableCarousel = ({ | |
|
|
||
| return ( | ||
| <div css={containerStyles}> | ||
| <ol | ||
| <ul | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. preferred to |
||
| ref={carouselRef} | ||
| css={[ | ||
| carouselStyles, | ||
|
|
@@ -383,9 +385,15 @@ export const ScrollableCarousel = ({ | |
| ]} | ||
| data-heatphan-type="carousel" | ||
| onFocus={scrollToCardOnFocus} | ||
| {...(isBelowTabletBreakpoint && { | ||
| role: 'region', | ||
| 'aria-roledescription': 'carousel', | ||
| 'aria-labelledby': `${sectionId}-title`, | ||
| 'aria-live': 'polite', | ||
| })} | ||
| > | ||
| {children} | ||
| </ol> | ||
| </ul> | ||
|
|
||
| {showNavigation && ( | ||
| <CarouselNavigationButtons | ||
|
|
@@ -412,10 +420,12 @@ ScrollableCarousel.Item = ({ | |
| isStackingCarousel = false, | ||
| children, | ||
| borderColour = palette('--card-border-top'), | ||
| isBelowTabletBreakpoint = false, | ||
| }: { | ||
| isStackingCarousel?: boolean; | ||
| children: React.ReactNode; | ||
| borderColour?: string; | ||
| isBelowTabletBreakpoint?: boolean; | ||
| }) => ( | ||
| <li | ||
| css={[ | ||
|
|
@@ -424,6 +434,10 @@ ScrollableCarousel.Item = ({ | |
| ? stackedRowLeftBorderStyles(borderColour) | ||
| : singleRowLeftBorderStyles(borderColour), | ||
| ]} | ||
| {...(isBelowTabletBreakpoint && { | ||
| role: 'group', | ||
| 'aria-roledescription': 'slide', | ||
| })} | ||
| > | ||
| {children} | ||
| </li> | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,4 +1,6 @@ | ||
| import { until } from '@guardian/source/foundations'; | ||
| import { ArticleDesign } from '../lib/articleFormat'; | ||
| import { removeMediaRulePrefix, useMatchMedia } from '../lib/useMatchMedia'; | ||
| import type { | ||
| AspectRatio, | ||
| DCRContainerPalette, | ||
|
|
@@ -31,14 +33,22 @@ export const ScrollableFeature = ({ | |
| aspectRatio, | ||
| collectionId, | ||
| }: Props) => { | ||
| const isBelowTabletBreakpoint = useMatchMedia( | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I did consider whether the added accessibility was worth the extra javascript. I've come to the conclusion that if we are going to use carousels in this way then the extra javascript needed to make them more accessible should be built in to that decision. |
||
| removeMediaRulePrefix(until.tablet), | ||
| ); | ||
|
|
||
| return ( | ||
| <ScrollableCarousel | ||
| carouselLength={trails.length} | ||
| visibleCarouselSlidesOnMobile={1} | ||
| visibleCarouselSlidesOnTablet={3} | ||
| isBelowTabletBreakpoint={isBelowTabletBreakpoint} | ||
| > | ||
| {trails.map((card, index) => ( | ||
| <ScrollableCarousel.Item key={card.url}> | ||
| <ScrollableCarousel.Item | ||
| key={card.url} | ||
| isBelowTabletBreakpoint={isBelowTabletBreakpoint} | ||
| > | ||
| <FeatureCard | ||
| linkTo={card.url} | ||
| format={card.format} | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So that
aria-labelledbycan target the appropriate element. This is better than prop drilling the title name down to the components that refer to it.