Skip to content

Commit a810bac

Browse files
authored
Merge pull request #23 from deco-sites/jonasjesus/deco-5279-slider-a11y-keyboard-autoplay
feat(ui): Slider keyboard nav, autoplay pause, a11y (DECO-5279)
2 parents 7ec6e6a + f90411c commit a810bac

1 file changed

Lines changed: 45 additions & 2 deletions

File tree

src/components/ui/Slider.tsx

Lines changed: 45 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -122,8 +122,10 @@ function useSlider(
122122
const dot = dots?.item(index);
123123
if (entry.isIntersecting) {
124124
dot?.setAttribute("disabled", "");
125+
dot?.setAttribute("aria-current", "true");
125126
} else {
126127
dot?.removeAttribute("disabled");
128+
dot?.removeAttribute("aria-current");
127129
}
128130
if (!infinite) {
129131
if (index === 0) {
@@ -155,11 +157,52 @@ function useSlider(
155157
prev?.addEventListener("click", onClickPrev);
156158
next?.addEventListener("click", onClickNext);
157159

158-
const timer = interval ? setInterval(() => onClickNext(), interval) : null;
160+
// Keyboard navigation: arrow keys move between slides when focus is within
161+
// the carousel (e.g. on a dot or arrow button).
162+
const onKeyDown = (e: KeyboardEvent) => {
163+
if (e.key === "ArrowRight") {
164+
e.preventDefault();
165+
onClickNext();
166+
} else if (e.key === "ArrowLeft") {
167+
e.preventDefault();
168+
onClickPrev(e);
169+
}
170+
};
171+
root.addEventListener("keydown", onKeyDown);
172+
173+
// Autoplay that pauses on hover/focus and when the tab is hidden, so it
174+
// never advances while the user is reading or interacting.
175+
let timer: ReturnType<typeof setInterval> | null = null;
176+
const startTimer = () => {
177+
if (interval && !timer) timer = setInterval(() => onClickNext(), interval);
178+
};
179+
const stopTimer = () => {
180+
if (timer) {
181+
clearInterval(timer);
182+
timer = null;
183+
}
184+
};
185+
const onVisibility = () => (document.hidden ? stopTimer() : startTimer());
186+
if (interval) {
187+
startTimer();
188+
root.addEventListener("pointerenter", stopTimer);
189+
root.addEventListener("pointerleave", startTimer);
190+
root.addEventListener("focusin", stopTimer);
191+
root.addEventListener("focusout", startTimer);
192+
document.addEventListener("visibilitychange", onVisibility);
193+
}
159194

160195
return () => {
161196
observer.disconnect();
162-
if (timer) clearInterval(timer);
197+
stopTimer();
198+
root.removeEventListener("keydown", onKeyDown);
199+
if (interval) {
200+
root.removeEventListener("pointerenter", stopTimer);
201+
root.removeEventListener("pointerleave", startTimer);
202+
root.removeEventListener("focusin", stopTimer);
203+
root.removeEventListener("focusout", startTimer);
204+
document.removeEventListener("visibilitychange", onVisibility);
205+
}
163206
prev?.removeEventListener("click", onClickPrev);
164207
next?.removeEventListener("click", onClickNext);
165208
if (dots) {

0 commit comments

Comments
 (0)