diff --git a/.Jules/palette.md b/.Jules/palette.md new file mode 100644 index 0000000..6d1ad99 --- /dev/null +++ b/.Jules/palette.md @@ -0,0 +1,3 @@ +## 2024-06-06 - Dynamic Accessibility Labels in React +**Learning:** Found a pattern where interactive elements (like the SpatialWorkspace delete buttons) use a two-step confirm process managed entirely via local React state without text labels. If aria-labels don't adapt to the current UI state, screen readers will hear "Delete" even when the visual UI says "Confirm?". +**Action:** Always bind `aria-label`s to the same state variable that drives the text or icon conditional rendering so accessibility state perfectly mirrors visual state. \ No newline at end of file diff --git a/ui/components/NodeEditor.tsx b/ui/components/NodeEditor.tsx index 6510592..592e83c 100644 --- a/ui/components/NodeEditor.tsx +++ b/ui/components/NodeEditor.tsx @@ -915,6 +915,7 @@ function NodeEditor({ ? "Unfreeze — allow auto-optimize" : "Freeze — protect from auto-optimize" } + aria-label={editFrozen ? "Unfreeze priority" : "Freeze priority"} > ❄️ diff --git a/ui/components/NodeEditorExpanded.tsx b/ui/components/NodeEditorExpanded.tsx index 6a68b66..ecccc00 100644 --- a/ui/components/NodeEditorExpanded.tsx +++ b/ui/components/NodeEditorExpanded.tsx @@ -944,6 +944,7 @@ export default function NodeEditorExpanded({ ? "Unfreeze — auto-optimize priority" : "Freeze — protect from auto-optimize" } + aria-label={editFrozen ? "Unfreeze priority" : "Freeze priority"} > ❄️ diff --git a/ui/components/SpatialWorkspace.tsx b/ui/components/SpatialWorkspace.tsx index b442d5f..7a499b9 100644 --- a/ui/components/SpatialWorkspace.tsx +++ b/ui/components/SpatialWorkspace.tsx @@ -1450,6 +1450,7 @@ export default function SpatialWorkspace({ setEditingItemType("vault"); setEditValue(vault.name); }} + aria-label={`Edit ${vault.name}`} > ✏️ @@ -1459,6 +1460,11 @@ export default function SpatialWorkspace({ className={`spatial-card-action-btn ${deleteArmedId === vault.id ? "delete-armed" : ""}`} onClick={(e) => handleArmDelete(e, vault.id)} title="Click twice to delete vault card" + aria-label={ + deleteArmedId === vault.id + ? `Confirm delete ${vault.name}` + : `Delete ${vault.name}` + } > {deleteArmedId === vault.id ? "Confirm?" : "🗑️"} @@ -1575,6 +1581,7 @@ export default function SpatialWorkspace({ setEditingItemType("node"); setEditValue(node.title); }} + aria-label={`Edit ${node.title}`} > ✏️ @@ -1586,6 +1593,11 @@ export default function SpatialWorkspace({ e.stopPropagation(); handleArmDelete(e, node.id); }} + aria-label={ + deleteArmedId === node.id + ? `Confirm delete ${node.title}` + : `Delete ${node.title}` + } > {deleteArmedId === node.id ? "Ok?" : "🗑️"} @@ -1657,6 +1669,7 @@ export default function SpatialWorkspace({ setEditingItemType("subvault"); setEditValue(subvault.name); }} + aria-label={`Edit ${subvault.name}`} > ✏️ @@ -1668,6 +1681,11 @@ export default function SpatialWorkspace({ e.stopPropagation(); handleArmDelete(e, subvault.id); }} + aria-label={ + deleteArmedId === subvault.id + ? `Confirm delete ${subvault.name}` + : `Delete ${subvault.name}` + } > {deleteArmedId === subvault.id ? "Ok?" : "🗑️"} @@ -1753,6 +1771,7 @@ export default function SpatialWorkspace({ setEditingItemType("node"); setEditValue(node.title); }} + aria-label={`Edit ${node.title}`} > ✏️ @@ -1764,6 +1783,11 @@ export default function SpatialWorkspace({ e.stopPropagation(); handleArmDelete(e, node.id); }} + aria-label={ + deleteArmedId === node.id + ? `Confirm delete ${node.title}` + : `Delete ${node.title}` + } > {deleteArmedId === node.id ? "Ok?" : "🗑️"}