From 73ef251771391826e419369dc60bf525aa637bf8 Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Sat, 27 Jun 2026 11:53:32 +0000 Subject: [PATCH] feat(a11y): add contextual aria-labels to generic visual text action buttons in sortable citation list Co-authored-by: aicoder2009 <127642633+aicoder2009@users.noreply.github.com> --- .jules/palette.md | 3 ++ src/components/wiki/sortable-citation.tsx | 34 +++++++++++------------ 2 files changed, 20 insertions(+), 17 deletions(-) diff --git a/.jules/palette.md b/.jules/palette.md index 752c04a..8a90b96 100644 --- a/.jules/palette.md +++ b/.jules/palette.md @@ -5,3 +5,6 @@ ## 2024-05-17 - Generic Visual Text Buttons and ARIA Context **Learning:** Components frequently use generic visual text buttons like `[hide]`, `[show]`, `[edit]`, or `[delete]` within lists or loops. Screen readers read these buttons without surrounding visual context, confusing users. **Action:** Always provide a contextual `aria-label` (e.g., `aria-label="Hide ${title}"`) for generic visual text buttons so screen reader users have proper context. +## 2025-02-13 - Add Contextual ARIA Labels to Generic Action Buttons in Lists +**Learning:** When building list interfaces (like a list of citations) where generic action buttons (`[edit]`, `[delete]`, `[copy]`, `[share]`, `[+ add notes]`, etc.) are repeated for each item, screen reader users face ambiguity if these buttons lack distinct accessible names. Without contextual labels, a screen reader simply announces "edit, button" repeatedly. +**Action:** Always add conditionally dynamic or contextual `aria-label` attributes to generic visual text buttons within lists to reflect the current state and specific item (e.g., `aria-label={\`Edit citation \${citationTitle}\`}`). This ensures screen readers announce accurate and unambiguous context for each action. diff --git a/src/components/wiki/sortable-citation.tsx b/src/components/wiki/sortable-citation.tsx index da2d6d6..791c0a0 100644 --- a/src/components/wiki/sortable-citation.tsx +++ b/src/components/wiki/sortable-citation.tsx @@ -103,6 +103,7 @@ export function SortableCitation({ const [internalIsEditing, setInternalIsEditing] = useState(false); const isEditingMode = internalIsEditing || externalIsEditing; const [isSaving, setIsSaving] = useState(false); + const citationTitle = citation.fields?.title || citation.formattedText?.slice(0, 60) || "this citation"; const [saveError, setSaveError] = useState(null); const [copiedKey, setCopiedKey] = useState<"copy" | "copy-in-text" | null>(null); const { getColor } = useTagColors(); @@ -359,7 +360,7 @@ export function SortableCitation({ setTimeout(() => setCopiedKey(null), 1500); }} className="text-wiki-link text-sm hover:underline focus-visible:outline-dotted focus-visible:outline-1 focus-visible:outline-wiki-text" - > + aria-label={copiedKey === "copy" ? `Copied citation ${citationTitle}!` : `Copy citation ${citationTitle}`}> {copiedKey === "copy" ? "[copied!]" : "[copy]"} {citation.fields && ( @@ -376,28 +377,28 @@ export function SortableCitation({ }} className="text-wiki-link text-sm hover:underline focus-visible:outline-dotted focus-visible:outline-1 focus-visible:outline-wiki-text" title={`In-text citation (${citation.style.toUpperCase()})`} - > + aria-label={copiedKey === "copy-in-text" ? `Copied in-text citation ${citationTitle}!` : `Copy in-text citation ${citationTitle}`}> {copiedKey === "copy-in-text" ? "[copied!]" : "[copy in-text]"} )} {onShare && ( )} @@ -446,17 +447,16 @@ export function SortableCitation({ setEditingNotes(true); }} className="text-wiki-link text-xs hover:underline focus-visible:outline-dotted focus-visible:outline-1 focus-visible:outline-wiki-text" - > + aria-label={`Edit notes for citation ${citationTitle}`}> [edit] @@ -469,7 +469,7 @@ export function SortableCitation({ setEditingNotes(true); }} className="text-wiki-link text-xs hover:underline focus-visible:outline-dotted focus-visible:outline-1 focus-visible:outline-wiki-text" - > + aria-label={`Add notes for citation ${citationTitle}`}> [+ add notes] )} @@ -515,7 +515,7 @@ export function SortableCitation({ setQuotesDraft(quotesDraft.filter((_, idx) => idx !== i)) } className="text-wiki-link text-xs hover:underline pt-1 focus-visible:outline-dotted focus-visible:outline-1 focus-visible:outline-wiki-text" - > + aria-label={`Remove quote ${i + 1} from citation ${citationTitle}`}> [remove] @@ -526,7 +526,7 @@ export function SortableCitation({ setQuotesDraft([...quotesDraft, { text: "", page: "" }]) } className="text-wiki-link text-xs hover:underline focus-visible:outline-dotted focus-visible:outline-1 focus-visible:outline-wiki-text" - > + aria-label={`Add quote to citation ${citationTitle}`}> + add quote @@ -563,7 +563,7 @@ export function SortableCitation({ setEditingQuotes(true); }} className="text-wiki-link text-xs hover:underline focus-visible:outline-dotted focus-visible:outline-1 focus-visible:outline-wiki-text" - > + aria-label={`Edit quotes for citation ${citationTitle}`}> [edit] @@ -587,7 +587,7 @@ export function SortableCitation({ setEditingQuotes(true); }} className="text-wiki-link text-xs hover:underline focus-visible:outline-dotted focus-visible:outline-1 focus-visible:outline-wiki-text" - > + aria-label={`Add quote to citation ${citationTitle}`}> [+ add quote] )} @@ -637,7 +637,7 @@ export function SortableCitation({ {(() => { @@ -682,7 +682,7 @@ export function SortableCitation({ )}