️(frontend) add tooltip and sr hint for f2 shortcut to bottom toolbar

helps keyboard and sr users discover the f2 shortcut for toolbar access

Signed-off-by: Cyril <c.gromoff@gmail.com>
This commit is contained in:
Cyril
2025-12-11 12:21:36 +01:00
parent b11cc6e9da
commit 4f2764eef4
6 changed files with 41 additions and 11 deletions

View File

@@ -31,6 +31,7 @@ import { FullScreenShareWarning } from './FullScreenShareWarning'
import { ParticipantName } from './ParticipantName'
import { getParticipantName } from '@/features/rooms/utils/getParticipantName'
import { useTranslation } from 'react-i18next'
import { css } from '@/styled-system/css'
export function TrackRefContextIfNeeded(
props: React.PropsWithChildren<{
@@ -109,14 +110,8 @@ export const ParticipantTile: (
const participantName = getParticipantName(trackReference.participant)
const { t } = useTranslation('rooms', { keyPrefix: 'participantTileFocus' })
// Avoid double announcements: LiveKit's useParticipantTile may set its own
// aria-label / aria-labelledby / aria-describedby. We strip them here
// and provide a single, explicit label for the focusable tile container.
const { 'aria-label': _ignoredAriaLabel, ...safeElementProps } = elementProps
void _ignoredAriaLabel
const interactiveProps = {
...safeElementProps,
...elementProps,
// Ensure the tile is focusable to expose contextual controls to keyboard users.
tabIndex: 0,
'aria-label': t('containerLabel', { name: participantName }),
@@ -232,6 +227,23 @@ export const ParticipantTile: (
)}
</ParticipantContextIfNeeded>
</TrackRefContextIfNeeded>
{hasKeyboardFocus && (
<div
className={css({
position: 'absolute',
top: '0.75rem',
left: '0.75rem',
backgroundColor: 'rgba(0,0,0,0.6)',
color: 'white',
borderRadius: '999px',
paddingInline: '0.5rem',
paddingBlock: '0.1rem',
fontSize: '0.75rem',
})}
>
{t('toolbarHint')}
</div>
)}
</div>
)
})

View File

@@ -11,6 +11,7 @@ import { OptionsButton } from '../../components/controls/Options/OptionsButton'
import { StartMediaButton } from '../../components/controls/StartMediaButton'
import { MoreOptions } from './MoreOptions'
import { useRef } from 'react'
import { useRegisterKeyboardShortcut } from '@/features/shortcuts/useRegisterKeyboardShortcut'
import { VideoDeviceControl } from '../../components/controls/Device/VideoDeviceControl'
import { AudioDevicesControl } from '../../components/controls/Device/AudioDevicesControl'
@@ -19,6 +20,18 @@ export function DesktopControlBar({
}: Readonly<ControlBarAuxProps>) {
const browserSupportsScreenSharing = supportsScreenSharing()
const desktopControlBarEl = useRef<HTMLDivElement>(null)
useRegisterKeyboardShortcut({
shortcut: { key: 'F2' },
handler: () => {
const root = desktopControlBarEl.current
if (!root) return
const firstButton = root.querySelector<HTMLButtonElement>(
'button, [role="button"], [tabindex="0"]'
)
firstButton?.focus()
},
})
return (
<div
ref={desktopControlBarEl}

View File

@@ -512,7 +512,9 @@
}
},
"participantTileFocus": {
"containerLabel": "Optionen für {{name}}",
"containerLabel": "Optionen für {{name}}, F2: zur Symbolleiste unten.",
"toolbarHint": "F2: zur Symbolleiste unten.",
"pin": {
"enable": "Anheften",
"disable": "Lösen"

View File

@@ -512,7 +512,8 @@
}
},
"participantTileFocus": {
"containerLabel": "Options for {{name}}",
"containerLabel": "Options for {{name}}, F2: go to the bottom toolbar.",
"toolbarHint": "F2: go to the bottom toolbar.",
"pin": {
"enable": "Pin",
"disable": "Unpin"

View File

@@ -512,7 +512,8 @@
}
},
"participantTileFocus": {
"containerLabel": "Options pour {{name}}",
"containerLabel": "Options pour {{name}}, raccourci F2 pour aller à la barre d'outils en bas.",
"toolbarHint": "F2: raccourci barre d'outils en bas.",
"pin": {
"enable": "Épingler",
"disable": "Annuler l'épinglage"

View File

@@ -512,7 +512,8 @@
}
},
"participantTileFocus": {
"containerLabel": "Opties voor {{name}}",
"containerLabel": "Opties voor {{name}}, F2: naar de werkbalk onderaan.",
"toolbarHint": "F2: naar de werkbalk onderaan.",
"pin": {
"enable": "Pinnen",
"disable": "Losmaken"