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 @@
## 2026-05-11 - Added keyboard focus to file upload controls
**Learning:** File upload drag-and-drop zones and file removal buttons generated by React components in this app lacked explicit keyboard focus indicators (focus-visible classes), creating a confusing experience for keyboard users trying to submit documents.
**Action:** Always ensure that any interactive element, especially custom divs and dynamically generated utility buttons, have explicit focus-visible styles to ensure full keyboard navigation support.

## 2026-05-31 - Accessible Custom Toggle Groups
**Learning:** When using generic `<button>` elements to build custom toggle or radio groups (like the PDF Tools selector), users navigating with screen readers lack context about the group and selected state, and default styling often removes keyboard focus rings.
**Action:** Always wrap custom button groups in a container with `role="group"` and an `aria-label`, explicitly add `aria-pressed={isActive}` to individual buttons to announce their state, and ensure `focus-visible` utility classes are added to retain keyboard accessibility.
10 changes: 6 additions & 4 deletions app/pdf-tools/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -383,15 +383,16 @@ export default function PDFToolsPage() {
{/* Quick-jump sticky nav */}
<nav className="sticky top-0 z-40 bg-white border-b shadow-sm">
<div className="max-w-6xl mx-auto px-4 py-3 overflow-x-auto">
<div className="flex gap-2 min-w-max">
<div className="flex gap-2 min-w-max" role="group" aria-label="PDF Tools Quick Navigation">
{tools.map((tool) => {
const colors = colorMap[tool.color];
const Icon = tool.icon;
return (
<button
key={tool.id}
onClick={() => handleSelectTool(tool.id)}
className={`flex items-center gap-1.5 px-3 py-1.5 rounded-full text-sm font-medium border transition-all ${activeTool === tool.id
aria-pressed={activeTool === tool.id}
className={`flex items-center gap-1.5 px-3 py-1.5 rounded-full text-sm font-medium border transition-all focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-blue-500 focus-visible:ring-offset-2 ${activeTool === tool.id
? `${colors.bg} ${colors.border} ${colors.text}`
: "bg-gray-50 border-gray-200 text-gray-600 hover:bg-gray-100"
}`}
Expand Down Expand Up @@ -498,7 +499,7 @@ export default function PDFToolsPage() {
</div>
)}

<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6" role="group" aria-label="Select PDF Tool">
{tools.map((tool) => {
const colors = colorMap[tool.color];
const Icon = tool.icon;
Expand All @@ -508,7 +509,8 @@ export default function PDFToolsPage() {
<button
key={tool.id}
onClick={() => handleSelectTool(tool.id)}
className={`text-left rounded-xl border-2 p-6 transition-all ${isActive
aria-pressed={isActive}
className={`text-left rounded-xl border-2 p-6 transition-all focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-blue-500 focus-visible:ring-offset-2 ${isActive
? `${colors.bg} ${colors.border} ring-2 ring-offset-2 ${colors.ring}`
: `bg-white border-gray-200 ${colors.hoverBg} hover:shadow-md`
}`}
Expand Down
Loading