fix: add missing aria-labels to icon-only buttons for accessibility
- ChatInput: add aria-labels to remove file, send shortcut toggle, and stop buttons - CodeBlock: add aria-labels to word wrap, line numbers, and collapse/expand buttons - Header: add aria-labels to session info, copy, close, and compact buttons
This commit is contained in:
@@ -296,6 +296,7 @@ export function ChatInput({ onSend, onAbort, isGenerating, disabled, sessionKey,
|
||||
<button
|
||||
onClick={() => removeFile(f.id)}
|
||||
className="absolute -top-1.5 -right-1.5 h-5 w-5 rounded-full bg-pc-elevated border border-pc-border-strong flex items-center justify-center opacity-0 group-hover:opacity-100 transition-opacity hover:bg-red-500/80"
|
||||
aria-label={`Remove ${f.file.name}`}
|
||||
>
|
||||
<X size={10} className="text-pc-text" />
|
||||
</button>
|
||||
@@ -360,6 +361,7 @@ export function ChatInput({ onSend, onAbort, isGenerating, disabled, sessionKey,
|
||||
onClick={toggleSendShortcut}
|
||||
className="hidden sm:flex shrink-0 h-7 rounded-xl border border-pc-border bg-pc-elevated/30 px-2 items-center gap-1 text-[10px] text-pc-text-muted hover:text-pc-text-secondary hover:bg-[var(--pc-hover)] transition-colors"
|
||||
title={`${t('settings.sendShortcut')}: ${sendOnEnter ? t('settings.sendEnter') : t('settings.sendCtrlEnter')}`}
|
||||
aria-label={`${t('settings.sendShortcut')}: ${sendOnEnter ? t('settings.sendEnter') : t('settings.sendCtrlEnter')}`}
|
||||
>
|
||||
<span>{sendOnEnter ? '↵' : (navigator.userAgent.includes('Mac') ? '⌘↵' : 'Ctrl↵')}</span>
|
||||
</button>
|
||||
@@ -367,6 +369,7 @@ export function ChatInput({ onSend, onAbort, isGenerating, disabled, sessionKey,
|
||||
<button
|
||||
onClick={onAbort}
|
||||
className="shrink-0 h-11 px-4 rounded-2xl border border-red-500/20 bg-red-500/10 text-red-400 hover:bg-red-500/20 transition-colors flex items-center gap-2"
|
||||
aria-label={t('chat.stop')}
|
||||
>
|
||||
<Square size={16} />
|
||||
<span className="text-sm hidden sm:inline">{t('chat.stop')}</span>
|
||||
|
||||
@@ -111,6 +111,7 @@ export function CodeBlock(props: HTMLAttributes<HTMLPreElement>) {
|
||||
onClick={toggleWrap}
|
||||
className="flex items-center gap-1 px-1.5 py-0.5 rounded hover:bg-pc-border/40 transition-colors text-pc-text-muted hover:text-pc-text-secondary"
|
||||
title={wordWrap ? 'Disable word wrap' : 'Enable word wrap'}
|
||||
aria-label={wordWrap ? 'Disable word wrap' : 'Enable word wrap'}
|
||||
type="button"
|
||||
>
|
||||
{wordWrap ? <AlignLeft className="h-3 w-3" /> : <WrapText className="h-3 w-3" />}
|
||||
@@ -120,6 +121,7 @@ export function CodeBlock(props: HTMLAttributes<HTMLPreElement>) {
|
||||
onClick={toggleLineNumbers}
|
||||
className="flex items-center gap-1 px-1.5 py-0.5 rounded hover:bg-pc-border/40 transition-colors text-pc-text-muted hover:text-pc-text-secondary"
|
||||
title={showLineNumbers ? 'Hide line numbers' : 'Show line numbers'}
|
||||
aria-label={showLineNumbers ? 'Hide line numbers' : 'Show line numbers'}
|
||||
type="button"
|
||||
>
|
||||
<Hash className="h-3 w-3" />
|
||||
@@ -155,6 +157,7 @@ export function CodeBlock(props: HTMLAttributes<HTMLPreElement>) {
|
||||
<button
|
||||
onClick={() => setIsCollapsed(prev => !prev)}
|
||||
className="flex items-center justify-center gap-1.5 w-full py-1.5 text-[11px] text-pc-text-muted hover:text-pc-text-secondary bg-pc-elevated/40 hover:bg-pc-elevated/60 border-t border-pc-border transition-colors rounded-b-lg"
|
||||
aria-label={isCollapsed ? `Show all ${lines.length} lines` : 'Collapse code'}
|
||||
type="button"
|
||||
>
|
||||
{isCollapsed ? (
|
||||
|
||||
@@ -62,7 +62,7 @@ export function Header({ status, sessionKey, onToggleSidebar, activeSessionData,
|
||||
</button>
|
||||
<div className="flex items-center gap-3 flex-1 min-w-0 relative" ref={sessionInfoRef}>
|
||||
<img src={agentAvatarUrl || '/logo.png'} alt="PinchChat" className="h-9 w-9 rounded-2xl object-cover" onError={(e) => { const img = e.target as HTMLImageElement; if (img.src !== window.location.origin + '/logo.png') { img.src = '/logo.png'; } else { img.style.display = 'none'; } }} />
|
||||
<button className="min-w-0 text-left group" onClick={() => setShowSessionInfo(v => !v)} title={t('header.sessionInfo')}>
|
||||
<button className="min-w-0 text-left group" onClick={() => setShowSessionInfo(v => !v)} title={t('header.sessionInfo')} aria-label={t('header.sessionInfo')}>
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="font-semibold text-pc-text text-sm tracking-wide">{agentName || t('header.title')}</span>
|
||||
<Sparkles className="h-3.5 w-3.5 text-pc-accent-light/60" />
|
||||
@@ -169,6 +169,7 @@ function CopyField({ value }: { value: string }) {
|
||||
className="ml-auto p-0.5 rounded hover:bg-[var(--pc-hover)] text-pc-text-faint hover:text-pc-text-secondary transition-colors"
|
||||
onClick={() => { copyToClipboard(value).then((ok) => { if (ok) { setCopied(true); setTimeout(() => setCopied(false), 1500); } }); }}
|
||||
title="Copy"
|
||||
aria-label="Copy to clipboard"
|
||||
>
|
||||
{copied ? <Check size={11} className="text-emerald-400" /> : <Copy size={11} />}
|
||||
</button>
|
||||
@@ -206,7 +207,7 @@ const SessionInfoPopover = forwardRef<HTMLDivElement, { session: Session; sessio
|
||||
>
|
||||
<div className="p-3 border-b border-pc-border flex items-center justify-between">
|
||||
<span className="text-xs font-semibold text-pc-text">{t('header.sessionInfo')}</span>
|
||||
<button onClick={onClose} className="text-pc-text-faint hover:text-pc-text text-xs">✕</button>
|
||||
<button onClick={onClose} className="text-pc-text-faint hover:text-pc-text text-xs" aria-label="Close">✕</button>
|
||||
</div>
|
||||
<div className="p-3 space-y-2">
|
||||
{rows.map(({ label, value, copyable }) => (
|
||||
@@ -241,6 +242,7 @@ function CompactButton({ sessionKey, onCompact }: { sessionKey: string; onCompac
|
||||
disabled={compacting}
|
||||
className="inline-flex items-center gap-1 text-[10px] text-pc-text-muted hover:text-pc-text shrink-0 px-1.5 py-0.5 rounded hover:bg-[var(--pc-hover)] transition-colors disabled:opacity-50"
|
||||
title={t('header.compact')}
|
||||
aria-label={t('header.compact')}
|
||||
>
|
||||
<Minimize2 className={`h-3 w-3 ${compacting ? 'animate-pulse' : ''}`} />
|
||||
<span className="hidden sm:inline">{compacting ? t('header.compacting') : t('header.compact')}</span>
|
||||
|
||||
Reference in New Issue
Block a user