️(frontend) shortcuts table: semantic structure and kbd badge

caption, th scope, <kbd> for keys, no Tab stops on rows
This commit is contained in:
Cyril
2026-03-02 11:02:36 +01:00
parent f1ddd7fa2f
commit 8362ac0e24
6 changed files with 36 additions and 13 deletions

View File

@@ -41,6 +41,7 @@ and this project adheres to
- ♿️(frontend) improve accessibility of the IntroSlider carousel #1026
- ♿️(frontend) add skip link component for keyboard navigation #1019
- ♿️(frontend) announce mic/camera state to SR on shortcut toggle #1052
- ✨(frontend) add Ctrl+Shift+/ to open shortcuts settings #1050
### Fixed

View File

@@ -32,7 +32,7 @@ import { ScreenShareErrorModal } from '../components/ScreenShareErrorModal'
import { useConnectionObserver } from '../hooks/useConnectionObserver'
import { useNoiseReduction } from '../hooks/useNoiseReduction'
import { useRegisterKeyboardShortcut } from '@/features/shortcuts/useRegisterKeyboardShortcut'
import { settingsStore } from '@/stores/settings'
import { useSettingsDialog } from '@/features/settings'
import { SettingsDialogExtendedKey } from '@/features/settings/type'
import { useVideoResolutionSubscription } from '../hooks/useVideoResolutionSubscription'
import { SettingsDialogProvider } from '@/features/settings/components/SettingsDialogProvider'
@@ -100,6 +100,7 @@ export function VideoConference({ ...props }: VideoConferenceProps) {
const { t: tRooms } = useTranslation('rooms')
const room = useRoomContext()
const announce = useScreenReaderAnnounce()
const { toggleSettingsDialog } = useSettingsDialog()
const getAnnouncementName = useCallback(
(participant?: Participant | null) => {
@@ -117,9 +118,8 @@ export function VideoConference({ ...props }: VideoConferenceProps) {
useRegisterKeyboardShortcut({
id: 'open-shortcuts',
handler: useCallback(() => {
settingsStore.defaultSelectedTab = SettingsDialogExtendedKey.SHORTCUTS
settingsStore.areSettingsOpen = true
}, []),
toggleSettingsDialog(SettingsDialogExtendedKey.SHORTCUTS)
}, [toggleSettingsDialog]),
})
const tracks = useTracks(

View File

@@ -3,15 +3,20 @@ import { ShortcutRow } from '@/features/shortcuts/components/ShortcutRow'
import { css } from '@/styled-system/css'
import { useTranslation } from 'react-i18next'
import { TabPanel, type TabPanelProps } from '@/primitives/Tabs'
import { H } from '@/primitives'
const tableStyle = css({
width: '100%',
borderCollapse: 'collapse',
overflowY: 'auto',
'& caption': {
fontWeight: 'bold',
marginBottom: '0.75rem',
textAlign: 'left',
},
'& th, & td': {
padding: '0.65rem 0',
textAlign: 'left',
fontWeight: 'normal',
},
'& tbody tr': {
borderBottom: '1px solid rgba(255,255,255,0.08)',
@@ -29,12 +34,11 @@ export const ShortcutTab = ({ id }: Pick<TabPanelProps, 'id'>) => {
className={css({
display: 'flex',
flexDirection: 'column',
gap: '0.75rem',
})}
>
<H lvl={2}>{t('shortcuts.listLabel')}</H>
<table className={tableStyle}>
<thead className="sr-only">
<caption>{t('shortcuts.listLabel')}</caption>
<thead>
<tr>
<th scope="col">{t('shortcuts.columnAction')}</th>
<th scope="col">{t('shortcuts.columnShortcut')}</th>

View File

@@ -14,7 +14,25 @@ export const useSettingsDialog = () => {
settingsStore.areSettingsOpen = true
}
const closeSettingsDialog = () => {
settingsStore.areSettingsOpen = false
}
const toggleSettingsDialog = (
defaultSelectedTab?: SettingsDialogExtendedKey
) => {
if (areSettingsOpen) {
closeSettingsDialog()
} else {
if (defaultSelectedTab)
settingsStore.defaultSelectedTab = defaultSelectedTab
settingsStore.areSettingsOpen = true
}
}
return {
openSettingsDialog,
closeSettingsDialog,
toggleSettingsDialog,
}
}

View File

@@ -25,9 +25,9 @@ export const ShortcutBadge: React.FC<ShortcutBadgeProps> = ({
}) => {
return (
<>
<div className={cx(badgeStyle, className)} aria-hidden="true">
<span>{visualLabel}</span>
</div>
<kbd className={cx(badgeStyle, className)} aria-hidden="true">
{visualLabel}
</kbd>
{srLabel && <span className="sr-only">{srLabel}</span>}
</>
)

View File

@@ -31,9 +31,9 @@ export const ShortcutRow: React.FC<ShortcutRowProps> = ({ descriptor }) => {
return (
<tr>
<td className={text({ variant: 'body' })}>
<th scope="row" className={text({ variant: 'body' })}>
{t(`actions.${descriptor.id}`)}
</td>
</th>
<td className={shortcutCellStyle}>
<ShortcutBadge visualLabel={visualShortcut} srLabel={srShortcut} />
</td>