feat(a11y): landmark regions, skip link, dialog labels, icon button labels
C-3: nav/main landmark roles in ClientLayout (nav + main areas) C-4: Skip-to-main-content link in ClientLayout (visually hidden, focusable) H-2: aria-labelledby on LeaveRoomPrompt and RoomTopicViewer dialogs C-1: aria-label on ~15 icon-only buttons (back, menu, close, folder, account) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -56,7 +56,7 @@ export function LeaveRoomPrompt({ roomId, onDone, onCancel }: LeaveRoomPromptPro
|
||||
escapeDeactivates: stopPropagation,
|
||||
}}
|
||||
>
|
||||
<Dialog variant="Surface">
|
||||
<Dialog variant="Surface" aria-labelledby="leave-room-dialog-title">
|
||||
<Header
|
||||
style={{
|
||||
padding: `0 ${config.space.S200} 0 ${config.space.S400}`,
|
||||
@@ -66,7 +66,7 @@ export function LeaveRoomPrompt({ roomId, onDone, onCancel }: LeaveRoomPromptPro
|
||||
size="500"
|
||||
>
|
||||
<Box grow="Yes">
|
||||
<Text size="H4">Leave Room</Text>
|
||||
<Text size="H4" id="leave-room-dialog-title">Leave Room</Text>
|
||||
</Box>
|
||||
<IconButton size="300" onClick={onCancel} radii="300" aria-label="Cancel">
|
||||
<Icon src={Icons.Cross} />
|
||||
|
||||
@@ -17,12 +17,13 @@ export const RoomTopicViewer = as<
|
||||
size="300"
|
||||
flexHeight
|
||||
className={classNames(css.ModalFlex, className)}
|
||||
aria-labelledby="room-topic-title"
|
||||
{...props}
|
||||
ref={ref}
|
||||
>
|
||||
<Header className={css.ModalHeader} variant="Surface" size="500">
|
||||
<Box grow="Yes">
|
||||
<Text size="H4" truncate>
|
||||
<Text size="H4" truncate id="room-topic-title">
|
||||
{name}
|
||||
</Text>
|
||||
</Box>
|
||||
|
||||
@@ -7,9 +7,29 @@ type ClientLayoutProps = {
|
||||
};
|
||||
export function ClientLayout({ nav, children }: ClientLayoutProps) {
|
||||
return (
|
||||
<Box grow="Yes">
|
||||
<Box shrink="No">{nav}</Box>
|
||||
<Box grow="Yes">{children}</Box>
|
||||
</Box>
|
||||
<>
|
||||
<a
|
||||
href="#main-content"
|
||||
style={{
|
||||
position: 'absolute',
|
||||
top: '-40px',
|
||||
left: 0,
|
||||
padding: '8px 16px',
|
||||
background: '#000',
|
||||
color: '#fff',
|
||||
zIndex: 9999,
|
||||
borderRadius: '0 0 4px 0',
|
||||
transition: 'top 0.1s',
|
||||
}}
|
||||
onFocus={(e) => { (e.currentTarget as HTMLElement).style.top = '0'; }}
|
||||
onBlur={(e) => { (e.currentTarget as HTMLElement).style.top = '-40px'; }}
|
||||
>
|
||||
Skip to main content
|
||||
</a>
|
||||
<Box grow="Yes">
|
||||
<Box shrink="No" as="nav" aria-label="Room navigation">{nav}</Box>
|
||||
<Box grow="Yes" as="main" id="main-content">{children}</Box>
|
||||
</Box>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -69,6 +69,7 @@ function ClientRootOptions({ mx }: { mx?: MatrixClient }) {
|
||||
variant="Background"
|
||||
fill="None"
|
||||
onClick={handleToggle}
|
||||
aria-label="Account options"
|
||||
>
|
||||
<Icon size="200" src={Icons.VerticalDots} />
|
||||
<PopOut
|
||||
|
||||
@@ -107,7 +107,7 @@ function DirectHeader() {
|
||||
</Text>
|
||||
</Box>
|
||||
<Box>
|
||||
<IconButton aria-pressed={!!menuAnchor} variant="Background" onClick={handleOpenMenu}>
|
||||
<IconButton aria-pressed={!!menuAnchor} variant="Background" onClick={handleOpenMenu} aria-label="Direct messages options">
|
||||
<Icon src={Icons.VerticalDots} size="200" />
|
||||
</IconButton>
|
||||
</Box>
|
||||
|
||||
@@ -44,7 +44,7 @@ export function DirectCreate() {
|
||||
<Box grow="Yes" alignItems="Center" gap="200">
|
||||
<BackRouteHandler>
|
||||
{(onBack) => (
|
||||
<IconButton onClick={onBack}>
|
||||
<IconButton onClick={onBack} aria-label="Back">
|
||||
<Icon src={Icons.ArrowLeft} />
|
||||
</IconButton>
|
||||
)}
|
||||
|
||||
@@ -96,7 +96,7 @@ export function AddServer() {
|
||||
<Box grow="Yes">
|
||||
<Text size="H4">Add Server</Text>
|
||||
</Box>
|
||||
<IconButton size="300" onClick={() => setDialog(false)} radii="300">
|
||||
<IconButton size="300" onClick={() => setDialog(false)} radii="300" aria-label="Close">
|
||||
<Icon src={Icons.Cross} />
|
||||
</IconButton>
|
||||
</Header>
|
||||
|
||||
@@ -33,7 +33,7 @@ export function FeaturedRooms() {
|
||||
<Box shrink="No">
|
||||
<BackRouteHandler>
|
||||
{(onBack) => (
|
||||
<IconButton onClick={onBack}>
|
||||
<IconButton onClick={onBack} aria-label="Back">
|
||||
<Icon src={Icons.ArrowLeft} />
|
||||
</IconButton>
|
||||
)}
|
||||
|
||||
@@ -499,7 +499,7 @@ export function PublicRooms() {
|
||||
{screenSize === ScreenSize.Mobile && (
|
||||
<BackRouteHandler>
|
||||
{(onBack) => (
|
||||
<IconButton onClick={onBack}>
|
||||
<IconButton onClick={onBack} aria-label="Back">
|
||||
<Icon src={Icons.ArrowLeft} />
|
||||
</IconButton>
|
||||
)}
|
||||
|
||||
@@ -25,7 +25,7 @@ export function HomeCreateRoom() {
|
||||
<Box grow="Yes" alignItems="Center" gap="200">
|
||||
<BackRouteHandler>
|
||||
{(onBack) => (
|
||||
<IconButton onClick={onBack}>
|
||||
<IconButton onClick={onBack} aria-label="Back">
|
||||
<Icon src={Icons.ArrowLeft} />
|
||||
</IconButton>
|
||||
)}
|
||||
|
||||
@@ -121,7 +121,7 @@ function HomeHeader() {
|
||||
</Text>
|
||||
</Box>
|
||||
<Box>
|
||||
<IconButton aria-pressed={!!menuAnchor} variant="Background" onClick={handleOpenMenu}>
|
||||
<IconButton aria-pressed={!!menuAnchor} variant="Background" onClick={handleOpenMenu} aria-label="Home options">
|
||||
<Icon src={Icons.VerticalDots} size="200" />
|
||||
</IconButton>
|
||||
</Box>
|
||||
|
||||
@@ -19,7 +19,7 @@ export function HomeSearch() {
|
||||
{screenSize === ScreenSize.Mobile && (
|
||||
<BackRouteHandler>
|
||||
{(onBack) => (
|
||||
<IconButton onClick={onBack}>
|
||||
<IconButton onClick={onBack} aria-label="Back">
|
||||
<Icon src={Icons.ArrowLeft} />
|
||||
</IconButton>
|
||||
)}
|
||||
|
||||
@@ -753,7 +753,7 @@ export function Invites() {
|
||||
{screenSize === ScreenSize.Mobile && (
|
||||
<BackRouteHandler>
|
||||
{(onBack) => (
|
||||
<IconButton onClick={onBack}>
|
||||
<IconButton onClick={onBack} aria-label="Back">
|
||||
<Icon src={Icons.ArrowLeft} />
|
||||
</IconButton>
|
||||
)}
|
||||
|
||||
@@ -642,7 +642,7 @@ export function Notifications() {
|
||||
{screenSize === ScreenSize.Mobile && (
|
||||
<BackRouteHandler>
|
||||
{(onBack) => (
|
||||
<IconButton onClick={onBack}>
|
||||
<IconButton onClick={onBack} aria-label="Back">
|
||||
<Icon src={Icons.ArrowLeft} />
|
||||
</IconButton>
|
||||
)}
|
||||
|
||||
@@ -522,7 +522,7 @@ function OpenedSpaceFolder({ folder, onClose, children }: OpenedSpaceFolderProps
|
||||
>
|
||||
<SidebarFolderDropTarget ref={aboveTargetRef} position="Top" />
|
||||
<SidebarAvatar size="300">
|
||||
<IconButton data-id={folder.id} size="300" variant="Background" onClick={onClose}>
|
||||
<IconButton data-id={folder.id} size="300" variant="Background" onClick={onClose} aria-label="Close folder">
|
||||
<Icon size="400" src={Icons.ChevronTop} filled />
|
||||
</IconButton>
|
||||
</SidebarAvatar>
|
||||
|
||||
@@ -34,7 +34,7 @@ export function SpaceSearch() {
|
||||
{screenSize === ScreenSize.Mobile && (
|
||||
<BackRouteHandler>
|
||||
{(onBack) => (
|
||||
<IconButton onClick={onBack}>
|
||||
<IconButton onClick={onBack} aria-label="Back">
|
||||
<Icon src={Icons.ArrowLeft} />
|
||||
</IconButton>
|
||||
)}
|
||||
|
||||
@@ -273,7 +273,7 @@ function SpaceHeader() {
|
||||
{joinRules?.join_rule !== JoinRule.Public && <Icon src={Icons.Lock} size="50" />}
|
||||
</Box>
|
||||
<Box shrink="No">
|
||||
<IconButton aria-pressed={!!menuAnchor} variant="Background" onClick={handleOpenMenu}>
|
||||
<IconButton aria-pressed={!!menuAnchor} variant="Background" onClick={handleOpenMenu} aria-label="Space options">
|
||||
<Icon src={Icons.VerticalDots} size="200" />
|
||||
</IconButton>
|
||||
</Box>
|
||||
|
||||
Reference in New Issue
Block a user