📢 fix: Resolved Screen Reader Issues with TooltipAnchor (#10580)

TooltipAnchor was automatically adding an `aria-describedby`
tag which often duplicated the labeling already present inside
of the anchor. E.g., the screen reader might say
"New Chat, New Chat, button" instead of just "New Chat, button."

I've removed the TooltipAnchor's automatic `aria-describedby` and
worked to make sure that anyone using TooltipAnchor properly defines
its labeling.
This commit is contained in:
Daniel Lew 2025-11-19 16:10:10 -06:00 committed by GitHub
parent 8b9afd5965
commit 014eb10662
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 49 additions and 19 deletions

View file

@ -59,7 +59,12 @@ export default function ArtifactVersion({
<TooltipAnchor
description={localize('com_ui_change_version')}
render={
<Button size="icon" variant="ghost" asChild>
<Button
size="icon"
variant="ghost"
asChild
aria-label={localize('com_ui_change_version')}
>
<MenuButton>
<History
size={18}

View file

@ -166,6 +166,7 @@ export default function Landing({ centerFormOnLanding }: { centerFormOnLanding:
<TooltipAnchor
className="absolute bottom-[27px] right-2"
description={localize('com_ui_happy_birthday')}
aria-label={localize('com_ui_happy_birthday')}
>
<BirthdayIcon />
</TooltipAnchor>

View file

@ -180,6 +180,10 @@ export default function DialogImage({ isOpen, onOpenChange, src = '', downloadIm
}
}, [isPromptOpen, zoom]);
const imageDetailsLabel = isPromptOpen
? localize('com_ui_hide_image_details')
: localize('com_ui_show_image_details');
return (
<OGDialog open={isOpen} onOpenChange={onOpenChange}>
<OGDialogContent
@ -198,6 +202,7 @@ export default function DialogImage({ isOpen, onOpenChange, src = '', downloadIm
onClick={() => onOpenChange(false)}
variant="ghost"
className="h-10 w-10 p-0 hover:bg-surface-hover"
aria-label={localize('com_ui_close')}
>
<X className="size-7 sm:size-6" />
</Button>
@ -208,7 +213,12 @@ export default function DialogImage({ isOpen, onOpenChange, src = '', downloadIm
<TooltipAnchor
description={localize('com_ui_reset_zoom')}
render={
<Button onClick={resetZoom} variant="ghost" className="h-10 w-10 p-0">
<Button
onClick={resetZoom}
variant="ghost"
className="h-10 w-10 p-0"
aria-label={localize('com_ui_reset_zoom')}
>
<RotateCcw className="size-6" />
</Button>
}
@ -217,22 +227,24 @@ export default function DialogImage({ isOpen, onOpenChange, src = '', downloadIm
<TooltipAnchor
description={localize('com_ui_download')}
render={
<Button onClick={() => downloadImage()} variant="ghost" className="h-10 w-10 p-0">
<Button
onClick={() => downloadImage()}
variant="ghost"
className="h-10 w-10 p-0"
aria-label={localize('com_ui_download')}
>
<ArrowDownToLine className="size-6" />
</Button>
}
/>
<TooltipAnchor
description={
isPromptOpen
? localize('com_ui_hide_image_details')
: localize('com_ui_show_image_details')
}
description={imageDetailsLabel}
render={
<Button
onClick={() => setIsPromptOpen(!isPromptOpen)}
variant="ghost"
className="h-10 w-10 p-0"
aria-label={imageDetailsLabel}
>
{isPromptOpen ? (
<PanelLeftOpen className="size-7 sm:size-6" />

View file

@ -113,6 +113,8 @@ export default function SharedLinkButton({
}
};
const qrCodeLabel = showQR ? localize('com_ui_hide_qr') : localize('com_ui_show_qr');
return (
<>
<div className="flex gap-2">
@ -130,6 +132,7 @@ export default function SharedLinkButton({
<Button
{...props}
onClick={() => updateSharedLink()}
aria-label={localize('com_ui_refresh_link')}
variant="outline"
disabled={isUpdateLoading}
>
@ -143,9 +146,14 @@ export default function SharedLinkButton({
/>
<TooltipAnchor
description={showQR ? localize('com_ui_hide_qr') : localize('com_ui_show_qr')}
description={qrCodeLabel}
render={(props) => (
<Button {...props} onClick={() => setShowQR(!showQR)} variant="outline">
<Button
{...props}
onClick={() => setShowQR(!showQR)}
variant="outline"
aria-label={qrCodeLabel}
>
<QrCode className="size-4" />
</Button>
)}
@ -154,7 +162,12 @@ export default function SharedLinkButton({
<TooltipAnchor
description={localize('com_ui_delete')}
render={(props) => (
<Button {...props} onClick={() => setShowDeleteDialog(true)} variant="destructive">
<Button
{...props}
onClick={() => setShowDeleteDialog(true)}
variant="destructive"
aria-label={localize('com_ui_delete')}
>
<Trash2 className="size-4" />
</Button>
)}

View file

@ -59,6 +59,10 @@ const AssistantConversationStarters: React.FC<AssistantConversationStartersProps
const hasReachedMax = field.value.length >= Constants.MAX_CONVO_STARTERS;
const addConversationStarterLabel = hasReachedMax
? localize('com_assistants_max_starters_reached')
: localize('com_ui_add');
return (
<div className="relative">
<label className={labelClass} htmlFor="conversation_starters">
@ -108,11 +112,8 @@ const AssistantConversationStarters: React.FC<AssistantConversationStartersProps
>
<TooltipAnchor
side="top"
description={
hasReachedMax
? localize('com_assistants_max_starters_reached')
: localize('com_ui_add')
}
description={addConversationStarterLabel}
aria-label={addConversationStarterLabel}
className="flex size-7 items-center justify-center rounded-lg transition-colors duration-200 hover:bg-surface-hover"
onClick={handleAddStarter}
disabled={hasReachedMax}
@ -140,6 +141,7 @@ const AssistantConversationStarters: React.FC<AssistantConversationStartersProps
<TooltipAnchor
side="top"
description={localize('com_ui_delete')}
aria-label={localize('com_ui_delete')}
className="absolute right-1 top-1 flex size-7 items-center justify-center rounded-lg transition-colors duration-200 hover:bg-surface-hover"
onClick={() => handleDeleteStarter(index)}
>