1- import React , { useEffect , useLayoutEffect , useState } from 'react'
1+ import React , { useEffect , useLayoutEffect , useRef , useState } from 'react'
22import Cookies from '@/frame/components/lib/cookies'
33import { UnderlineNav } from '@primer/react'
44import { sendEvent } from '@/events/components/events'
@@ -39,6 +39,11 @@ export const InArticlePicker = ({
3939 const { query, locale } = router
4040 const [ currentValue , setCurrentValue ] = useState ( '' )
4141
42+ // Tracks whether the last currentValue change was triggered by a user click
43+ // (as opposed to initial mount or external navigation). When true, we move
44+ // focus to the newly-selected tab so keyboard users don't lose their place.
45+ const focusAfterNavRef = useRef ( false )
46+
4247 // Run on mount for client-side only features
4348 useEffect ( ( ) => {
4449 const raw = query [ queryStringKey ]
@@ -128,6 +133,7 @@ export const InArticlePicker = ({
128133 } , [ currentValue ] )
129134
130135 function onClickChoice ( value : string ) {
136+ focusAfterNavRef . current = true
131137 const params = new URLSearchParams ( asPathQuery )
132138 params . set ( queryStringKey , value )
133139 const newPath = `/${ locale } ${ asPathRoot } ?${ params } `
@@ -142,6 +148,21 @@ export const InArticlePicker = ({
142148 Cookies . set ( cookieKey , value )
143149 }
144150
151+ // After a user clicks a tab, the shallow route change updates `currentValue`.
152+ // Once the DOM reflects the new selection (aria-current="page" is on the new
153+ // tab), move keyboard focus there so the user's context is preserved.
154+ // WCAG 2.4.3 Focus Order — focus must land on the triggered control.
155+ useEffect ( ( ) => {
156+ if ( ! focusAfterNavRef . current || ! currentValue ) return
157+ focusAfterNavRef . current = false
158+
159+ const container = document . querySelector < HTMLElement > (
160+ `[data-testid="${ queryStringKey } -picker"]` ,
161+ )
162+ const selectedTab = container ?. querySelector < HTMLElement > ( '[aria-current="page"]' )
163+ selectedTab ?. focus ( )
164+ } , [ currentValue , queryStringKey ] )
165+
145166 const sharedContainerProps = {
146167 'aria-label' : ariaLabel ,
147168 }
0 commit comments