Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .jules/palette.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
34 changes: 17 additions & 17 deletions src/components/wiki/sortable-citation.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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<string | null>(null);
const [copiedKey, setCopiedKey] = useState<"copy" | "copy-in-text" | null>(null);
const { getColor } = useTagColors();
Expand Down Expand Up @@ -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]"}
</button>
{citation.fields && (
Expand All @@ -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]"}
</button>
)}
<button
onClick={startEditing}
className="text-wiki-link text-sm hover:underline focus-visible:outline-dotted focus-visible:outline-1 focus-visible:outline-wiki-text"
>
aria-label={`Edit citation ${citationTitle}`}>
[edit]
</button>
{onShare && (
<button
onClick={() => onShare(citation.id)}
className="text-wiki-link text-sm hover:underline focus-visible:outline-dotted focus-visible:outline-1 focus-visible:outline-wiki-text"
>
aria-label={`Share citation ${citationTitle}`}>
[share]
</button>
)}
<button
onClick={() => onDelete(citation.id)}
className="text-wiki-link text-sm hover:underline focus-visible:outline-dotted focus-visible:outline-1 focus-visible:outline-wiki-text"
>
aria-label={`Delete citation ${citationTitle}`}>
[delete]
</button>
</div>
Expand Down Expand Up @@ -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]
</button>
<button
onClick={() => {
const label = citation.fields?.title || citation.formattedText?.slice(0, 60) || "this citation";
// eslint-disable-next-line no-alert
if (confirm(`Clear notes for "${label}"?`)) onSaveNotes(citation.id, "");
if (confirm(`Clear notes for "${citationTitle}"?`)) onSaveNotes(citation.id, "");
}}
className="text-wiki-link text-xs hover:underline focus-visible:outline-dotted focus-visible:outline-1 focus-visible:outline-wiki-text"
>
aria-label={`Clear notes for citation ${citationTitle}`}>
[clear]
</button>
</div>
Expand All @@ -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]
</button>
)}
Expand Down Expand Up @@ -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]
</button>
</div>
Expand All @@ -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
</button>
</div>
Expand Down Expand Up @@ -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]
</button>
</div>
Expand All @@ -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]
</button>
)}
Expand Down Expand Up @@ -637,7 +637,7 @@ export function SortableCitation({
<button
onClick={() => onAddTag(citation.id, newTagInput)}
className="text-wiki-link text-xs hover:underline focus-visible:outline-dotted focus-visible:outline-1 focus-visible:outline-wiki-text"
>
aria-label={`Add tag to citation ${citationTitle}`}>
[add]
</button>
<button
Expand All @@ -646,7 +646,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 to citation ${citationTitle}`}>
[cancel]
</button>
{(() => {
Expand Down Expand Up @@ -682,7 +682,7 @@ export function SortableCitation({
<button
onClick={() => setEditingTagsId(citation.id)}
className="text-wiki-link text-xs hover:underline focus-visible:outline-dotted focus-visible:outline-1 focus-visible:outline-wiki-text"
>
aria-label={`Add tag to citation ${citationTitle}`}>
[+ add tag]
</button>
)}
Expand Down
Loading