Compare commits

...

4 Commits

Author SHA1 Message Date
lebaudantoine 7cb4498643 ⬆️(frontend) update react-aria-components to a newer version
The previously pinned version (July release) did not support
passing the aria-disabled prop to React Aria Button.

A more recent release (August) introduced this capability.
Upgrade is required to make Cyril's proposal work.
2026-02-27 18:09:38 +01:00
Cyril 87c992460c ️(frontend) keep carousel nav buttons focusable at first and last slide
use aria-disabled  to prevent focus loss when reaching slide limits
2026-02-27 18:09:38 +01:00
Cyril c874c48aae ️(frontend) fix carousel focus ring visibility with NVDA
add :focus fallback for nav buttons when focus-visible detection fails
2026-02-27 18:04:23 +01:00
Cyril d07d68c0df ️(frontend) improve IntroSlider accessibility for screen readers
add aria-labels with slide position, carousel semantics, live region
2026-02-27 18:04:23 +01:00
8 changed files with 955 additions and 867 deletions
+1
View File
@@ -25,6 +25,7 @@ and this project adheres to
- ♿️(frontend) fix focus ring on tab container components #1012
- ♿️(frontend) upgrade join meeting modal accessibility #1027
- ⬆️(python) bump minimal required python version to 3.13 #1033
- ♿️(frontend) improve accessibility of the IntroSlider carousel #1026
### Fixed
+854 -837
View File
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -36,7 +36,7 @@
"livekit-client": "2.17.1",
"posthog-js": "1.342.1",
"react": "18.3.1",
"react-aria-components": "1.10.1",
"react-aria-components": "1.14.0",
"react-dom": "18.3.1",
"react-i18next": "15.1.1",
"use-sound": "5.0.0",
@@ -4,6 +4,7 @@ import { Button } from '@/primitives'
import { RiArrowLeftSLine, RiArrowRightSLine } from '@remixicon/react'
import { useState } from 'react'
import { useTranslation } from 'react-i18next'
import { useScreenReaderAnnounce } from '@/hooks/useScreenReaderAnnounce'
const Heading = styled('h2', {
base: {
@@ -144,6 +145,21 @@ type Slide = {
isAvailableInBeta?: boolean
}
const carouselNavButton = css({
_focusVisible: {
outline: '2px solid var(--colors-focus-ring) !important',
outlineOffset: '1px',
},
_disabled: {
color: 'greyscale.400',
cursor: 'default',
pointerEvents: 'none',
_pressed: {
backgroundColor: 'transparent',
},
},
})
// todo - optimize how images are imported
const SLIDES: Slide[] = [
{
@@ -163,11 +179,45 @@ const SLIDES: Slide[] = [
export const IntroSlider = () => {
const [slideIndex, setSlideIndex] = useState(0)
const { t } = useTranslation('home', { keyPrefix: 'introSlider' })
const announce = useScreenReaderAnnounce()
const NUMBER_SLIDES = SLIDES.length
const goPrev = () => {
if (slideIndex === 0) return
const newIndex = slideIndex - 1
setSlideIndex(newIndex)
announce(
t('slidePosition', { current: newIndex + 1, total: NUMBER_SLIDES }),
'polite',
'global'
)
}
const goNext = () => {
if (slideIndex === NUMBER_SLIDES - 1) return
const newIndex = slideIndex + 1
setSlideIndex(newIndex)
announce(
t('slidePosition', { current: newIndex + 1, total: NUMBER_SLIDES }),
'polite',
'global'
)
}
const ariaLabelParams = {
current: slideIndex + 1,
total: NUMBER_SLIDES,
}
const previousAriaLabel = t('previous.labelWithPosition', ariaLabelParams)
const nextAriaLabel = t('next.labelWithPosition', ariaLabelParams)
return (
<Container>
<Container
role="region"
aria-roledescription="carousel"
aria-label={t('carouselLabel')}
>
<div
className={css({
display: 'flex',
@@ -180,10 +230,10 @@ export const IntroSlider = () => {
<Button
variant="secondaryText"
square
aria-label={t('previous.label')}
tooltip={t('previous.tooltip')}
onPress={() => setSlideIndex(slideIndex - 1)}
isDisabled={slideIndex == 0}
className={carouselNavButton}
aria-label={previousAriaLabel}
aria-disabled={slideIndex === 0}
onPress={goPrev}
>
<RiArrowLeftSLine />
</Button>
@@ -191,7 +241,11 @@ export const IntroSlider = () => {
</ButtonContainer>
<SlideContainer>
{SLIDES.map((slide, index) => (
<Slide visible={index == slideIndex} key={index}>
<Slide
aria-hidden={index !== slideIndex}
visible={index === slideIndex}
key={index}
>
<Image src={slide.src} alt="" role="presentation" />
<TextAnimation visible={index == slideIndex}>
<Heading>{t(`${slide.key}.title`)}</Heading>
@@ -205,10 +259,10 @@ export const IntroSlider = () => {
<Button
variant="secondaryText"
square
aria-label={t('next.label')}
tooltip={t('next.tooltip')}
onPress={() => setSlideIndex(slideIndex + 1)}
isDisabled={slideIndex == NUMBER_SLIDES - 1}
className={carouselNavButton}
aria-label={nextAriaLabel}
aria-disabled={slideIndex === NUMBER_SLIDES - 1}
onPress={goNext}
>
<RiArrowRightSLine />
</Button>
+9 -5
View File
@@ -31,12 +31,14 @@
},
"introSlider": {
"previous": {
"label": "Zurück",
"tooltip": "Zurück"
"label": "Vorherige Folie",
"labelWithPosition": "Vorherige Folie ({{current}} von {{total}})",
"tooltip": "Vorherige Folie"
},
"next": {
"label": "Weiter",
"tooltip": "Weiter"
"label": "Nächste Folie",
"labelWithPosition": "Nächste Folie ({{current}} von {{total}})",
"tooltip": "Nächste Folie"
},
"beta": {
"text": "An der Beta teilnehmen",
@@ -53,6 +55,8 @@
"slide3": {
"title": "Verwandeln Sie Ihre Meetings mit KI",
"body": "Erhalten Sie präzise und verwertbare Transkripte zur Steigerung Ihrer Produktivität. Funktion in der Beta jetzt testen!"
}
},
"carouselLabel": "Einführungs-Diashow",
"slidePosition": "Folie {{current}} von {{total}}"
}
}
+9 -5
View File
@@ -31,12 +31,14 @@
},
"introSlider": {
"previous": {
"label": "previous",
"tooltip": "previous"
"label": "Previous slide",
"labelWithPosition": "Previous slide ({{current}} of {{total}})",
"tooltip": "Previous slide"
},
"next": {
"label": "next",
"tooltip": "next"
"label": "Next slide",
"labelWithPosition": "Next slide ({{current}} of {{total}})",
"tooltip": "Next slide"
},
"beta": {
"text": "Join the beta",
@@ -53,6 +55,8 @@
"slide3": {
"title": "Transform your meetings with AI",
"body": "Get accurate and actionable transcripts to boost your productivity. Feature in beta—try it now!"
}
},
"carouselLabel": "Introduction slideshow",
"slidePosition": "Slide {{current}} of {{total}}"
}
}
+8 -4
View File
@@ -30,14 +30,18 @@
}
},
"introSlider": {
"carouselLabel": "Diaporama de présentation",
"previous": {
"label": "précédent",
"tooltip": "précédent"
"label": "Diapositive précédente",
"labelWithPosition": "Diapositive précédente ({{current}} sur {{total}})",
"tooltip": "Diapositive précédente"
},
"next": {
"label": "suivant",
"tooltip": "suivant"
"label": "Diapositive suivante",
"labelWithPosition": "Diapositive suivante ({{current}} sur {{total}})",
"tooltip": "Diapositive suivante"
},
"slidePosition": "Diapositive {{current}} sur {{total}}",
"beta": {
"text": "Essayer la beta",
"tooltip": "Accéder au formulaire"
+9 -5
View File
@@ -31,12 +31,14 @@
},
"introSlider": {
"previous": {
"label": "vorige",
"tooltip": "vorige"
"label": "Vorige dia",
"labelWithPosition": "Vorige dia ({{current}} van {{total}})",
"tooltip": "Vorige dia"
},
"next": {
"label": "volgende",
"tooltip": "volgende"
"label": "Volgende dia",
"labelWithPosition": "Volgende dia ({{current}} van {{total}})",
"tooltip": "Volgende dia"
},
"beta": {
"text": "Word lid van de bèta",
@@ -53,6 +55,8 @@
"slide3": {
"title": "Transformeer uw vergaderingen met AI",
"body": "Krijg nauwkeurige en bruikbare transcripties om uw productiviteit te stimuleren. Deze mogelijkheid is in bèta, probeer het nu!"
}
},
"carouselLabel": "Introductie-diavoorstelling",
"slidePosition": "Dia {{current}} van {{total}}"
}
}