add aria-labels to elements for screen readers (#1106)

This commit is contained in:
KDSBrowne 2023-01-15 06:16:06 -05:00 committed by GitHub
parent 60728e069a
commit 6f87ff022a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 136 additions and 24 deletions

View file

@ -89,6 +89,7 @@ export function ToolButtonWithTooltip({
isToolLocked={isLocked && rest.isActive} isToolLocked={isLocked && rest.isActive}
onDoubleClick={handleDoubleClick} onDoubleClick={handleDoubleClick}
onKeyDown={handleKeyDown} onKeyDown={handleKeyDown}
aria-label={label[0].toUpperCase() + label.slice(1)}
/> />
</Tooltip> </Tooltip>
) )

View file

@ -189,7 +189,7 @@ export function ActionButton() {
return ( return (
<DropdownMenu.Root dir="ltr" onOpenChange={handleMenuOpenChange}> <DropdownMenu.Root dir="ltr" onOpenChange={handleMenuOpenChange}>
<DropdownMenu.Trigger dir="ltr" asChild id="TD-Tools-Dots"> <DropdownMenu.Trigger dir="ltr" asChild id="TD-Tools-Dots">
<ToolButton variant="circle"> <ToolButton aria-label={intl.formatMessage({ id: 'shape.options' })} variant="circle">
<DotsHorizontalIcon /> <DotsHorizontalIcon />
</ToolButton> </ToolButton>
</DropdownMenu.Trigger> </DropdownMenu.Trigger>
@ -197,12 +197,20 @@ export function ActionButton() {
<> <>
<ButtonsRow> <ButtonsRow>
<Tooltip label={intl.formatMessage({ id: 'duplicate' })} kbd={`#D`} id="TD-Tools-Copy"> <Tooltip label={intl.formatMessage({ id: 'duplicate' })} kbd={`#D`} id="TD-Tools-Copy">
<ToolButton disabled={!hasSelection} onClick={handleDuplicate}> <ToolButton
aria-label={intl.formatMessage({ id: 'duplicate' })}
disabled={!hasSelection}
onClick={handleDuplicate}
>
<CopyIcon /> <CopyIcon />
</ToolButton> </ToolButton>
</Tooltip> </Tooltip>
<Tooltip label={intl.formatMessage({ id: 'rotate' })} id="TD-Tools-Rotate"> <Tooltip label={intl.formatMessage({ id: 'rotate' })} id="TD-Tools-Rotate">
<ToolButton disabled={!hasSelection} onClick={handleRotate}> <ToolButton
aria-label={intl.formatMessage({ id: 'rotate' })}
disabled={!hasSelection}
onClick={handleRotate}
>
<RotateCounterClockwiseIcon /> <RotateCounterClockwiseIcon />
</ToolButton> </ToolButton>
</Tooltip> </Tooltip>
@ -211,7 +219,11 @@ export function ActionButton() {
kbd={`#L`} kbd={`#L`}
id="TD-Tools-Lock" id="TD-Tools-Lock"
> >
<ToolButton disabled={!hasSelection} onClick={handleToggleLocked}> <ToolButton
aria-label={intl.formatMessage({ id: isAllLocked ? 'unlock' : 'lock' })}
disabled={!hasSelection}
onClick={handleToggleLocked}
>
{isAllLocked ? <LockClosedIcon /> : <LockOpen1Icon />} {isAllLocked ? <LockClosedIcon /> : <LockOpen1Icon />}
</ToolButton> </ToolButton>
</Tooltip> </Tooltip>
@ -221,12 +233,19 @@ export function ActionButton() {
})} })}
id="TD-Tools-AspectRatio" id="TD-Tools-AspectRatio"
> >
<ToolButton disabled={!hasSelection} onClick={handleToggleAspectRatio}> <ToolButton
aria-label={intl.formatMessage({
id: isAllAspectLocked ? 'unlock.aspect.ratio' : 'lock.aspect.ratio',
})}
disabled={!hasSelection}
onClick={handleToggleAspectRatio}
>
{isAllAspectLocked ? <AspectRatioIcon /> : <BoxIcon />} {isAllAspectLocked ? <AspectRatioIcon /> : <BoxIcon />}
</ToolButton> </ToolButton>
</Tooltip> </Tooltip>
<Tooltip label={intl.formatMessage({ id: 'group' })} kbd={`#G`} id="TD-Tools-Group"> <Tooltip label={intl.formatMessage({ id: 'group' })} kbd={`#G`} id="TD-Tools-Group">
<ToolButton <ToolButton
aria-label={intl.formatMessage({ id: 'group' })}
disabled={!hasSelection || (!isAllGrouped && !hasMultipleSelection)} disabled={!hasSelection || (!isAllGrouped && !hasMultipleSelection)}
onClick={handleGroup} onClick={handleGroup}
> >
@ -240,7 +259,11 @@ export function ActionButton() {
kbd={`#⇧[`} kbd={`#⇧[`}
id="TD-Tools-PinBottom" id="TD-Tools-PinBottom"
> >
<ToolButton disabled={!hasSelection} onClick={handleMoveToBack}> <ToolButton
aria-label={intl.formatMessage({ id: 'move.to.back' })}
disabled={!hasSelection}
onClick={handleMoveToBack}
>
<PinBottomIcon /> <PinBottomIcon />
</ToolButton> </ToolButton>
</Tooltip> </Tooltip>
@ -249,7 +272,11 @@ export function ActionButton() {
kbd={`#[`} kbd={`#[`}
id="TD-Tools-ArrowDown" id="TD-Tools-ArrowDown"
> >
<ToolButton disabled={!hasSelection} onClick={handleMoveBackward}> <ToolButton
aria-label={intl.formatMessage({ id: 'move.backward' })}
disabled={!hasSelection}
onClick={handleMoveBackward}
>
<ArrowDownIcon /> <ArrowDownIcon />
</ToolButton> </ToolButton>
</Tooltip> </Tooltip>
@ -258,7 +285,11 @@ export function ActionButton() {
kbd={`#]`} kbd={`#]`}
id="TD-Tools-ArrowUp" id="TD-Tools-ArrowUp"
> >
<ToolButton disabled={!hasSelection} onClick={handleMoveForward}> <ToolButton
aria-label={intl.formatMessage({ id: 'move.forward' })}
disabled={!hasSelection}
onClick={handleMoveForward}
>
<ArrowUpIcon /> <ArrowUpIcon />
</ToolButton> </ToolButton>
</Tooltip> </Tooltip>
@ -267,12 +298,20 @@ export function ActionButton() {
kbd={`#⇧]`} kbd={`#⇧]`}
id="TD-Tools-PinTop" id="TD-Tools-PinTop"
> >
<ToolButton disabled={!hasSelection} onClick={handleMoveToFront}> <ToolButton
aria-label={intl.formatMessage({ id: 'move.to.front' })}
disabled={!hasSelection}
onClick={handleMoveToFront}
>
<PinTopIcon /> <PinTopIcon />
</ToolButton> </ToolButton>
</Tooltip> </Tooltip>
<Tooltip label={intl.formatMessage({ id: 'reset.angle' })} id="TD-Tools-ResetAngle"> <Tooltip label={intl.formatMessage({ id: 'reset.angle' })} id="TD-Tools-ResetAngle">
<ToolButton disabled={!hasSelection} onClick={handleResetAngle}> <ToolButton
aria-label={intl.formatMessage({ id: 'reset.angle' })}
disabled={!hasSelection}
onClick={handleResetAngle}
>
<AngleIcon /> <AngleIcon />
</ToolButton> </ToolButton>
</Tooltip> </Tooltip>
@ -280,7 +319,11 @@ export function ActionButton() {
<Divider /> <Divider />
<ButtonsRow> <ButtonsRow>
<Tooltip label={intl.formatMessage({ id: 'align.left' })} id="TD-Tools-AlignLeft"> <Tooltip label={intl.formatMessage({ id: 'align.left' })} id="TD-Tools-AlignLeft">
<ToolButton disabled={!hasTwoOrMore} onClick={alignLeft}> <ToolButton
aria-label={intl.formatMessage({ id: 'align.left' })}
disabled={!hasTwoOrMore}
onClick={alignLeft}
>
<AlignLeftIcon /> <AlignLeftIcon />
</ToolButton> </ToolButton>
</Tooltip> </Tooltip>
@ -288,12 +331,20 @@ export function ActionButton() {
label={intl.formatMessage({ id: 'align.center.x' })} label={intl.formatMessage({ id: 'align.center.x' })}
id="TD-Tools-AlignCenterHorizontal" id="TD-Tools-AlignCenterHorizontal"
> >
<ToolButton disabled={!hasTwoOrMore} onClick={alignCenterHorizontal}> <ToolButton
aria-label={intl.formatMessage({ id: 'align.center.x' })}
disabled={!hasTwoOrMore}
onClick={alignCenterHorizontal}
>
<AlignCenterHorizontallyIcon /> <AlignCenterHorizontallyIcon />
</ToolButton> </ToolButton>
</Tooltip> </Tooltip>
<Tooltip label={intl.formatMessage({ id: 'align.right' })} id="TD-Tools-AlignRight"> <Tooltip label={intl.formatMessage({ id: 'align.right' })} id="TD-Tools-AlignRight">
<ToolButton disabled={!hasTwoOrMore} onClick={alignRight}> <ToolButton
aria-label={intl.formatMessage({ id: 'align.right' })}
disabled={!hasTwoOrMore}
onClick={alignRight}
>
<AlignRightIcon /> <AlignRightIcon />
</ToolButton> </ToolButton>
</Tooltip> </Tooltip>
@ -301,7 +352,11 @@ export function ActionButton() {
label={intl.formatMessage({ id: 'stretch.x' })} label={intl.formatMessage({ id: 'stretch.x' })}
id="TD-Tools-StretchHorizontal" id="TD-Tools-StretchHorizontal"
> >
<ToolButton disabled={!hasTwoOrMore} onClick={stretchHorizontally}> <ToolButton
aria-label={intl.formatMessage({ id: 'stretch.x' })}
disabled={!hasTwoOrMore}
onClick={stretchHorizontally}
>
<StretchHorizontallyIcon /> <StretchHorizontallyIcon />
</ToolButton> </ToolButton>
</Tooltip> </Tooltip>
@ -309,14 +364,22 @@ export function ActionButton() {
label={intl.formatMessage({ id: 'distribute.x' })} label={intl.formatMessage({ id: 'distribute.x' })}
id="TD-Tools-SpaceEvenlyHorizontal" id="TD-Tools-SpaceEvenlyHorizontal"
> >
<ToolButton disabled={!hasThreeOrMore} onClick={distributeHorizontally}> <ToolButton
aria-label={intl.formatMessage({ id: 'distribute.x' })}
disabled={!hasThreeOrMore}
onClick={distributeHorizontally}
>
<SpaceEvenlyHorizontallyIcon /> <SpaceEvenlyHorizontallyIcon />
</ToolButton> </ToolButton>
</Tooltip> </Tooltip>
</ButtonsRow> </ButtonsRow>
<ButtonsRow> <ButtonsRow>
<Tooltip label={intl.formatMessage({ id: 'align.top' })} id="TD-Tools-AlignTop"> <Tooltip label={intl.formatMessage({ id: 'align.top' })} id="TD-Tools-AlignTop">
<ToolButton disabled={!hasTwoOrMore} onClick={alignTop}> <ToolButton
aria-label={intl.formatMessage({ id: 'align.top' })}
disabled={!hasTwoOrMore}
onClick={alignTop}
>
<AlignTopIcon /> <AlignTopIcon />
</ToolButton> </ToolButton>
</Tooltip> </Tooltip>
@ -324,17 +387,29 @@ export function ActionButton() {
label={intl.formatMessage({ id: 'align.center.y' })} label={intl.formatMessage({ id: 'align.center.y' })}
id="TD-Tools-AlignCenterVertical" id="TD-Tools-AlignCenterVertical"
> >
<ToolButton disabled={!hasTwoOrMore} onClick={alignCenterVertical}> <ToolButton
aria-label={intl.formatMessage({ id: 'align.center.y' })}
disabled={!hasTwoOrMore}
onClick={alignCenterVertical}
>
<AlignCenterVerticallyIcon /> <AlignCenterVerticallyIcon />
</ToolButton> </ToolButton>
</Tooltip> </Tooltip>
<Tooltip label={intl.formatMessage({ id: 'align.bottom' })} id="TD-Tools-AlignBottom"> <Tooltip label={intl.formatMessage({ id: 'align.bottom' })} id="TD-Tools-AlignBottom">
<ToolButton disabled={!hasTwoOrMore} onClick={alignBottom}> <ToolButton
aria-label={intl.formatMessage({ id: 'align.bottom' })}
disabled={!hasTwoOrMore}
onClick={alignBottom}
>
<AlignBottomIcon /> <AlignBottomIcon />
</ToolButton> </ToolButton>
</Tooltip> </Tooltip>
<Tooltip label={intl.formatMessage({ id: 'stretch.y' })} id="TD-Tools-StretchVertical"> <Tooltip label={intl.formatMessage({ id: 'stretch.y' })} id="TD-Tools-StretchVertical">
<ToolButton disabled={!hasTwoOrMore} onClick={stretchVertically}> <ToolButton
aria-label={intl.formatMessage({ id: 'stretch.y' })}
disabled={!hasTwoOrMore}
onClick={stretchVertically}
>
<StretchVerticallyIcon /> <StretchVerticallyIcon />
</ToolButton> </ToolButton>
</Tooltip> </Tooltip>
@ -342,7 +417,11 @@ export function ActionButton() {
label={intl.formatMessage({ id: 'distribute.y' })} label={intl.formatMessage({ id: 'distribute.y' })}
id="TD-Tools-SpaceEvenlyVertical" id="TD-Tools-SpaceEvenlyVertical"
> >
<ToolButton disabled={!hasThreeOrMore} onClick={distributeVertically}> <ToolButton
aria-label={intl.formatMessage({ id: 'distribute.y' })}
disabled={!hasThreeOrMore}
onClick={distributeVertically}
>
<SpaceEvenlyVerticallyIcon /> <SpaceEvenlyVerticallyIcon />
</ToolButton> </ToolButton>
</Tooltip> </Tooltip>

View file

@ -21,7 +21,12 @@ export function DeleteButton() {
return ( return (
<Tooltip label={intl.formatMessage({ id: 'delete' })} kbd="⌫" id="TD-Delete"> <Tooltip label={intl.formatMessage({ id: 'delete' })} kbd="⌫" id="TD-Delete">
<ToolButton variant="circle" disabled={!hasSelection} onSelect={handleDelete}> <ToolButton
aria-label={intl.formatMessage({ id: 'delete' })}
variant="circle"
disabled={!hasSelection}
onSelect={handleDelete}
>
<TrashIcon /> <TrashIcon />
</ToolButton> </ToolButton>
</Tooltip> </Tooltip>

View file

@ -84,6 +84,7 @@ export const ShapesMenu = React.memo(function ShapesMenu({
isToolLocked={isActive && isToolLocked} isToolLocked={isActive && isToolLocked}
isActive={isActive} isActive={isActive}
onKeyDown={handleKeyDown} onKeyDown={handleKeyDown}
aria-label={intl.formatMessage({ id: 'shapes' })}
> >
{shapeShapeIcons[lastActiveTool]} {shapeShapeIcons[lastActiveTool]}
</ToolButton> </ToolButton>
@ -99,6 +100,7 @@ export const ShapesMenu = React.memo(function ShapesMenu({
> >
<DropdownMenu.Item asChild> <DropdownMenu.Item asChild>
<ToolButton <ToolButton
aria-label={intl.formatMessage({ id: shape })}
variant="primary" variant="primary"
onClick={() => { onClick={() => {
app.selectTool(shape) app.selectTool(shape)

View file

@ -6,7 +6,7 @@ import {
TextAlignRightIcon, TextAlignRightIcon,
} from '@radix-ui/react-icons' } from '@radix-ui/react-icons'
import * as React from 'react' import * as React from 'react'
import { FormattedMessage } from 'react-intl' import { FormattedMessage, useIntl } from 'react-intl'
import { Divider } from '~components/Primitives/Divider' import { Divider } from '~components/Primitives/Divider'
import { DMCheckboxItem, DMContent, DMRadioItem } from '~components/Primitives/DropdownMenu' import { DMCheckboxItem, DMContent, DMRadioItem } from '~components/Primitives/DropdownMenu'
import { ToolButton } from '~components/Primitives/ToolButton' import { ToolButton } from '~components/Primitives/ToolButton'
@ -105,6 +105,8 @@ const optionsSelector = (s: TDSnapshot) => {
export const StyleMenu = React.memo(function ColorMenu() { export const StyleMenu = React.memo(function ColorMenu() {
const app = useTldrawApp() const app = useTldrawApp()
const intl = useIntl()
const theme = app.useStore(themeSelector) const theme = app.useStore(themeSelector)
const keepOpen = app.useStore(keepOpenSelector) const keepOpen = app.useStore(keepOpenSelector)
@ -194,7 +196,7 @@ export const StyleMenu = React.memo(function ColorMenu() {
modal={false} modal={false}
> >
<DropdownMenu.Trigger asChild id="TD-Styles"> <DropdownMenu.Trigger asChild id="TD-Styles">
<ToolButton variant="text"> <ToolButton aria-label={intl.formatMessage({ id: 'styles' })} variant="text">
<FormattedMessage id="styles" /> <FormattedMessage id="styles" />
<OverlapIcons <OverlapIcons
style={{ style={{
@ -229,6 +231,7 @@ export const StyleMenu = React.memo(function ColorMenu() {
variant="icon" variant="icon"
isActive={displayedStyle.color === style} isActive={displayedStyle.color === style}
onClick={() => app.style({ color: style as ColorStyle })} onClick={() => app.style({ color: style as ColorStyle })}
aria-label={intl.formatMessage({ id: style })}
> >
<CircleIcon <CircleIcon
size={18} size={18}
@ -262,6 +265,7 @@ export const StyleMenu = React.memo(function ColorMenu() {
onSelect={preventEvent} onSelect={preventEvent}
bp={breakpoints} bp={breakpoints}
id={`TD-Styles-Dash-${style}`} id={`TD-Styles-Dash-${style}`}
aria-label={intl.formatMessage({ id: style })}
> >
{DASH_ICONS[style as DashStyle]} {DASH_ICONS[style as DashStyle]}
</DMRadioItem> </DMRadioItem>
@ -279,6 +283,7 @@ export const StyleMenu = React.memo(function ColorMenu() {
onSelect={preventEvent} onSelect={preventEvent}
bp={breakpoints} bp={breakpoints}
id={`TD-Styles-Dash-${sizeStyle}`} id={`TD-Styles-Dash-${sizeStyle}`}
aria-label={intl.formatMessage({ id: sizeStyle })}
> >
{SIZE_ICONS[sizeStyle as SizeStyle]} {SIZE_ICONS[sizeStyle as SizeStyle]}
</DMRadioItem> </DMRadioItem>

View file

@ -52,6 +52,8 @@
"new.page": "New Page", "new.page": "New Page",
"page.name": "Page Name", "page.name": "Page Name",
"duplicate": "Duplicate", "duplicate": "Duplicate",
"shape.options": "Shape Options",
"shapes": "Shapes",
"cancel": "Cancel", "cancel": "Cancel",
"copy.invite.link": "Copy Invite Link", "copy.invite.link": "Copy Invite Link",
"copy.readonly.link": "Copy ReadOnly Link", "copy.readonly.link": "Copy ReadOnly Link",
@ -124,5 +126,23 @@
"dialog.no": "No", "dialog.no": "No",
"dialog.yes": "Yes", "dialog.yes": "Yes",
"enter.file.name": "Enter file name", "enter.file.name": "Enter file name",
"tldraw-beta": "Try the new tldraw" "tldraw-beta": "Try the new tldraw",
"white": "White",
"lightGray": "Light gray",
"gray": "Gray",
"black": "Black",
"green": "Green",
"cyan": "Cyan",
"blue": "Blue",
"indigo": "Indigo",
"violet": "Violet",
"red": "Red",
"orange": "Orange",
"yellow": "Yellow",
"solid": "Solid",
"dashed": "Dashed",
"dotted": "Dotted",
"small": "Small",
"medium": "Medium",
"large": "Large"
} }