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
4 changes: 4 additions & 0 deletions .jules/palette.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
## 2024-03-16 - Make hover-only actions keyboard accessible
**Learning:** Actions hidden behind `opacity-0` and revealed with `group-hover:opacity-100` are completely inaccessible to keyboard-only users navigating via Tab. This is a common pattern for "secondary" actions like undo/delete buttons in lists.
**Action:** When using `opacity-0 group-hover:opacity-100` to hide secondary actions, always pair it with `focus-visible:opacity-100`, `focus-visible:ring-2`, and `focus-visible:outline-none` to ensure the action becomes visible and clearly highlighted when focused via keyboard navigation.

## 2024-03-30 - Screen Reader Accessibility for Icon-Only Action Buttons
**Learning:** Action buttons composed exclusively of decorative or functional icons (e.g., Lucide React icons like X, Loader2, Check) inside of status/preview dialogs often lack sufficient text context, causing screen readers to announce them cryptically as just "button".
**Action:** Always apply an `aria-label` or `title` containing a clear description (e.g., "Skip organization") to the parent `<button>`, and explicitly set `aria-hidden="true"` on the child `<svg>` icon element to prevent conflicting or duplicate screen reader announcements.
18 changes: 10 additions & 8 deletions frontend_v2/src/components/organize/ClassificationPreview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -169,38 +169,40 @@ export default function ClassificationPreview({ result, onClose }: Classificatio
<button
onClick={handleConfirm}
disabled={isConfirming}
className="flex-1 flex items-center justify-center gap-2 px-4 py-3 bg-success hover:bg-success/90 rounded-xl font-medium transition-colors text-white disabled:opacity-50 disabled:cursor-not-allowed"
className="flex-1 flex items-center justify-center gap-2 px-4 py-3 bg-success hover:bg-success/90 rounded-xl font-medium transition-colors text-white disabled:opacity-50 disabled:cursor-not-allowed focus-visible:ring-2 focus-visible:ring-white focus-visible:outline-none focus-visible:ring-offset-2 focus-visible:ring-offset-gray-900"
>
{isConfirming ? (
<>
<Loader2 size={20} className="animate-spin" />
<Loader2 size={20} className="animate-spin" aria-hidden="true" />
Organizing...
</>
) : (
<>
<Check size={20} />
<Check size={20} aria-hidden="true" />
Confirm & Organize
</>
)}
</button>
<button
onClick={handleReclassify}
className="flex items-center justify-center gap-2 px-4 py-3 bg-white/10 hover:bg-white/20 rounded-xl font-medium transition-colors text-white"
className="flex items-center justify-center gap-2 px-4 py-3 bg-white/10 hover:bg-white/20 rounded-xl font-medium transition-colors text-white focus-visible:ring-2 focus-visible:ring-white focus-visible:outline-none focus-visible:ring-offset-2 focus-visible:ring-offset-gray-900"
>
<RefreshCw size={20} />
<RefreshCw size={20} aria-hidden="true" />
Reclassify
</button>
<button
onClick={handleSkip}
className="p-3 bg-white/10 hover:bg-white/20 rounded-xl transition-colors text-white"
className="p-3 bg-white/10 hover:bg-white/20 rounded-xl transition-colors text-white focus-visible:ring-2 focus-visible:ring-white focus-visible:outline-none focus-visible:ring-offset-2 focus-visible:ring-offset-gray-900"
aria-label="Skip organization"
title="Skip organization"
>
<X size={20} />
<X size={20} aria-hidden="true" />
</button>
</div>

<button
onClick={handleSkip}
className="w-full mt-3 text-sm text-white/60 hover:text-white transition-colors"
className="w-full mt-3 text-sm text-white/60 hover:text-white transition-colors focus-visible:ring-2 focus-visible:ring-white focus-visible:outline-none focus-visible:ring-offset-2 focus-visible:ring-offset-gray-900 rounded"
>
Skip (organize later)
</button>
Expand Down
Loading