@@ -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