mirror of
https://github.com/danny-avila/LibreChat.git
synced 2026-01-08 19:48:51 +01:00
🔇 fix: Hide Button Icons from Screen Readers (#10776)
If you've got a screen reader that is reading out the whole page, each icon button (i.e., `<button><SVG></button>`) will have both the button's aria-label read out as well as the title from the SVG (which is usually just "image"). Since we are pretty good about setting aria-labels, we should instead use `aria-hidden="true"` on these images, since they are not useful to be read out. I don't consider this a comprehensive review of all icons in the app, but I knocked out all the low hanging fruit in this commit.
This commit is contained in:
parent
b288d81f5a
commit
1143f73f59
175 changed files with 340 additions and 183 deletions
|
|
@ -93,7 +93,7 @@ function AccountSettings() {
|
|||
value="logout"
|
||||
className="select-item text-sm"
|
||||
>
|
||||
<LogOut className="icon-md" />
|
||||
<LogOut className="icon-md" aria-hidden="true" />
|
||||
{localize('com_nav_log_out')}
|
||||
</Select.SelectItem>
|
||||
</Select.SelectPopover>
|
||||
|
|
|
|||
|
|
@ -58,7 +58,7 @@ export default function AgentMarketplaceButton({
|
|||
className="rounded-full border-none bg-transparent p-2 hover:bg-surface-hover md:rounded-xl"
|
||||
onClick={handleAgentMarketplace}
|
||||
>
|
||||
<LayoutGrid className="icon-lg text-text-primary" />
|
||||
<LayoutGrid className="icon-lg text-text-primary" aria-hidden="true" />
|
||||
</Button>
|
||||
}
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -144,7 +144,7 @@ const SearchBar = forwardRef((props: SearchBarProps, ref: React.Ref<HTMLDivEleme
|
|||
tabIndex={showClearIcon ? 0 : -1}
|
||||
disabled={!showClearIcon}
|
||||
>
|
||||
<X className="h-5 w-5 cursor-pointer" />
|
||||
<X className="h-5 w-5 cursor-pointer" aria-hidden="true" />
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -80,17 +80,17 @@ export default function Settings({ open, onOpenChange }: TDialogProps) {
|
|||
},
|
||||
{
|
||||
value: SettingsTabValues.CHAT,
|
||||
icon: <MessageSquare className="icon-sm" />,
|
||||
icon: <MessageSquare className="icon-sm" aria-hidden="true" />,
|
||||
label: 'com_nav_setting_chat',
|
||||
},
|
||||
{
|
||||
value: SettingsTabValues.COMMANDS,
|
||||
icon: <Command className="icon-sm" />,
|
||||
icon: <Command className="icon-sm" aria-hidden="true" />,
|
||||
label: 'com_nav_commands',
|
||||
},
|
||||
{
|
||||
value: SettingsTabValues.SPEECH,
|
||||
icon: <SpeechIcon className="icon-sm" />,
|
||||
icon: <SpeechIcon className="icon-sm" aria-hidden="true" />,
|
||||
label: 'com_nav_setting_speech',
|
||||
},
|
||||
...(hasAnyPersonalizationFeature
|
||||
|
|
|
|||
|
|
@ -170,7 +170,7 @@ function Avatar() {
|
|||
<span>{localize('com_nav_profile_picture')}</span>
|
||||
<OGDialogTrigger asChild>
|
||||
<Button variant="outline">
|
||||
<FileImage className="mr-2 flex w-[22px] items-center" />
|
||||
<FileImage className="mr-2 flex w-[22px] items-center" aria-hidden="true" />
|
||||
<span>{localize('com_nav_change_picture')}</span>
|
||||
</Button>
|
||||
</OGDialogTrigger>
|
||||
|
|
@ -211,7 +211,7 @@ function Avatar() {
|
|||
{!isDragging && (
|
||||
<div className="pointer-events-none absolute inset-0 flex items-center justify-center opacity-0 transition-opacity hover:opacity-100">
|
||||
<div className="rounded-full bg-black/50 p-2">
|
||||
<Move className="h-6 w-6 text-white" />
|
||||
<Move className="h-6 w-6 text-white" aria-hidden="true" />
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
|
@ -236,7 +236,7 @@ function Avatar() {
|
|||
aria-label={localize('com_ui_zoom_out')}
|
||||
className="shrink-0"
|
||||
>
|
||||
<ZoomOut className="h-4 w-4" />
|
||||
<ZoomOut className="h-4 w-4" aria-hidden="true" />
|
||||
</Button>
|
||||
<Slider
|
||||
id="zoom-slider"
|
||||
|
|
@ -257,7 +257,7 @@ function Avatar() {
|
|||
aria-label={localize('com_ui_zoom_in')}
|
||||
className="shrink-0"
|
||||
>
|
||||
<ZoomIn className="h-4 w-4" />
|
||||
<ZoomIn className="h-4 w-4" aria-hidden="true" />
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -270,7 +270,7 @@ function Avatar() {
|
|||
className="flex items-center space-x-2"
|
||||
aria-label={localize('com_ui_rotate_90')}
|
||||
>
|
||||
<RotateCw className="h-4 w-4" />
|
||||
<RotateCw className="h-4 w-4" aria-hidden="true" />
|
||||
<span className="text-sm">{localize('com_ui_rotate')}</span>
|
||||
</Button>
|
||||
<Button
|
||||
|
|
@ -280,7 +280,7 @@ function Avatar() {
|
|||
className="flex items-center space-x-2"
|
||||
aria-label={localize('com_ui_reset_adjustments')}
|
||||
>
|
||||
<X className="h-4 w-4" />
|
||||
<X className="h-4 w-4" aria-hidden="true" />
|
||||
<span className="text-sm">{localize('com_ui_reset')}</span>
|
||||
</Button>
|
||||
</div>
|
||||
|
|
@ -312,7 +312,7 @@ function Avatar() {
|
|||
{isUploading ? (
|
||||
<Spinner className="icon-sm mr-2" />
|
||||
) : (
|
||||
<Upload className="mr-2 h-4 w-4" />
|
||||
<Upload className="mr-2 h-4 w-4" aria-hidden="true" />
|
||||
)}
|
||||
{localize('com_ui_upload')}
|
||||
</Button>
|
||||
|
|
|
|||
|
|
@ -171,7 +171,7 @@ const BackupCodesItem: React.FC = () => {
|
|||
{isLoading ? (
|
||||
<Spinner className="mr-2" />
|
||||
) : (
|
||||
<RefreshCcw className="mr-2 h-4 w-4" />
|
||||
<RefreshCcw className="mr-2 h-4 w-4" aria-hidden="true" />
|
||||
)}
|
||||
{isLoading
|
||||
? localize('com_ui_regenerating')
|
||||
|
|
|
|||
|
|
@ -123,12 +123,12 @@ const renderDeleteButton = (
|
|||
<>
|
||||
{isLocked ? (
|
||||
<>
|
||||
<LockIcon className="size-5" />
|
||||
<LockIcon className="size-5" aria-hidden="true" />
|
||||
<span className="ml-2">{localize('com_ui_locked')}</span>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<Trash className="size-5" />
|
||||
<Trash className="size-5" aria-hidden="true" />
|
||||
<span className="ml-2">{localize('com_nav_delete_account_button')}</span>
|
||||
</>
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -216,7 +216,7 @@ const TwoFactorAuthentication: React.FC = () => {
|
|||
>
|
||||
<OGDialogHeader>
|
||||
<OGDialogTitle className="mb-2 flex items-center gap-3 text-2xl font-bold">
|
||||
<SmartphoneIcon className="h-6 w-6 text-primary" />
|
||||
<SmartphoneIcon className="h-6 w-6 text-primary" aria-hidden="true" />
|
||||
{user?.twoFactorEnabled
|
||||
? localize('com_ui_2fa_disable')
|
||||
: localize('com_ui_2fa_setup')}
|
||||
|
|
|
|||
|
|
@ -48,7 +48,7 @@ export const BackupPhase: React.FC<BackupPhaseProps> = ({
|
|||
</div>
|
||||
<div className="flex gap-4">
|
||||
<Button variant="outline" onClick={onDownload} className="flex-1 gap-2">
|
||||
<Download className="h-4 w-4" />
|
||||
<Download className="h-4 w-4" aria-hidden="true" />
|
||||
<span className="hidden sm:inline">{localize('com_ui_download_backup')}</span>
|
||||
</Button>
|
||||
<Button onClick={onNext} disabled={!downloaded} className="flex-1">
|
||||
|
|
|
|||
|
|
@ -53,7 +53,11 @@ export const QRPhase: React.FC<QRPhaseProps> = ({ secret, otpauthUrl, onNext })
|
|||
onClick={handleCopy}
|
||||
className={cn('h-auto shrink-0', isCopying ? 'cursor-default' : '')}
|
||||
>
|
||||
{isCopying ? <Check className="size-4" /> : <Copy className="size-4" />}
|
||||
{isCopying ? (
|
||||
<Check className="size-4" aria-hidden="true" />
|
||||
) : (
|
||||
<Copy className="size-4" aria-hidden="true" />
|
||||
)}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -33,7 +33,11 @@ export const SetupPhase: React.FC<SetupPhaseProps> = ({ isGenerating, onGenerate
|
|||
className="flex w-full"
|
||||
disabled={isGenerating}
|
||||
>
|
||||
{isGenerating ? <Spinner className="size-5" /> : <QrCode className="size-5" />}
|
||||
{isGenerating ? (
|
||||
<Spinner className="size-5" />
|
||||
) : (
|
||||
<QrCode className="size-5" aria-hidden="true" />
|
||||
)}
|
||||
{isGenerating ? localize('com_ui_generating') : localize('com_ui_generate_qrcode')}
|
||||
</Button>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -55,7 +55,7 @@ const DangerButton = (props: TDangerButtonProps, ref: ForwardedRef<HTMLButtonEle
|
|||
id={`${id}-text`}
|
||||
data-testid={dataTestIdConfirm}
|
||||
>
|
||||
{renderMutation(<CheckIcon className="h-5 w-5" />)}
|
||||
{renderMutation(<CheckIcon className="h-5 w-5" aria-hidden="true" />)}
|
||||
{mutation && mutation.isLoading ? null : localize(confirmActionTextCode)}
|
||||
</div>
|
||||
) : (
|
||||
|
|
|
|||
|
|
@ -120,7 +120,7 @@ function ImportConversations() {
|
|||
{isUploading ? (
|
||||
<Spinner className="mr-1 w-4" />
|
||||
) : (
|
||||
<Import className="mr-1 flex h-4 w-4 items-center stroke-1" />
|
||||
<Import className="mr-1 flex h-4 w-4 items-center stroke-1" aria-hidden="true" />
|
||||
)}
|
||||
<span>{localize('com_ui_import')}</span>
|
||||
</Button>
|
||||
|
|
|
|||
|
|
@ -234,7 +234,7 @@ export default function ArchivedChatsTable({
|
|||
{unarchiveMutation.isLoading ? (
|
||||
<Spinner />
|
||||
) : (
|
||||
<ArchiveRestore className="size-4" />
|
||||
<ArchiveRestore className="size-4" aria-hidden="true" />
|
||||
)}
|
||||
</Button>
|
||||
}
|
||||
|
|
@ -251,7 +251,7 @@ export default function ArchivedChatsTable({
|
|||
}}
|
||||
title={localize('com_ui_delete')}
|
||||
>
|
||||
<TrashIcon className="size-4" />
|
||||
<TrashIcon className="size-4" aria-hidden="true" />
|
||||
</Button>
|
||||
}
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -167,7 +167,7 @@ function Speech() {
|
|||
value="simple"
|
||||
style={{ userSelect: 'none' }}
|
||||
>
|
||||
<Lightbulb />
|
||||
<Lightbulb aria-hidden="true" />
|
||||
{localize('com_ui_simple')}
|
||||
</Tabs.Trigger>
|
||||
<Tabs.Trigger
|
||||
|
|
@ -180,7 +180,7 @@ function Speech() {
|
|||
value="advanced"
|
||||
style={{ userSelect: 'none' }}
|
||||
>
|
||||
<Cog />
|
||||
<Cog aria-hidden="true" />
|
||||
{localize('com_ui_advanced')}
|
||||
</Tabs.Trigger>
|
||||
</Tabs.List>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue