diff --git a/.jules/palette.md b/.jules/palette.md
index 752c04a..c1924d3 100644
--- a/.jules/palette.md
+++ b/.jules/palette.md
@@ -5,3 +5,7 @@
## 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.
+
+## 2024-06-30 - Contextual ARIA labels for generic list buttons
+**Learning:** Generic text buttons in repeated lists (like `[edit]`, `[delete]`, `[copy]`) can be confusing for screen reader users when their context isn't explicit, even if they aren't icon-only buttons. Furthermore, dynamically updating buttons (like a `[copy]` button that briefly changes to `[copied!]`) can cause screen readers to announce incorrect information if the `aria-label` doesn't match the new dynamic state.
+**Action:** Always provide specific context in `aria-label`s for generic action buttons inside repeated items, and conditionally update the `aria-label` to match the visual dynamic state of the button (e.g., `aria-label={isCopied ? "Copied" : "Copy"}`).
diff --git a/src/components/wiki/sortable-citation.tsx b/src/components/wiki/sortable-citation.tsx
index da2d6d6..eee66f2 100644
--- a/src/components/wiki/sortable-citation.tsx
+++ b/src/components/wiki/sortable-citation.tsx
@@ -359,6 +359,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" : "Copy citation"}
>
{copiedKey === "copy" ? "[copied!]" : "[copy]"}
@@ -376,6 +377,7 @@ 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" : "Copy in-text citation"}
>
{copiedKey === "copy-in-text" ? "[copied!]" : "[copy in-text]"}
@@ -383,6 +385,7 @@ export function SortableCitation({
@@ -390,6 +393,7 @@ export function SortableCitation({
@@ -397,6 +401,7 @@ export function SortableCitation({
@@ -446,6 +451,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="Edit notes"
>
[edit]
@@ -469,6 +475,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 to citation"
>
[+ add notes]
@@ -515,6 +522,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}`}
>
[remove]
@@ -526,6 +534,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 another quote"
>
+ add quote
@@ -563,6 +572,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"
>
[edit]
@@ -587,6 +597,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 quotes to citation"
>
[+ add quote]
@@ -637,6 +648,7 @@ export function SortableCitation({
@@ -646,6 +658,7 @@ export function SortableCitation({
setNewTagInput("");
}}
className="text-wiki-text-muted text-xs hover:underline focus-visible:outline-dotted focus-visible:outline-1 focus-visible:outline-wiki-text"
+ aria-label="Cancel adding tag"
>
[cancel]
@@ -669,6 +682,7 @@ export function SortableCitation({
onClick={() => onAddTag(citation.id, tag)}
className={`inline-flex items-center px-2 py-0.5 text-xs border focus-visible:outline-dotted focus-visible:outline-1 focus-visible:outline-wiki-text ${color.bg} ${color.text} ${color.border}`}
title={`Add "${tag}"`}
+ aria-label={`Add tag ${tag}`}
>
+ {tag}
@@ -682,6 +696,7 @@ export function SortableCitation({