🎨 Palette: [UX improvement] Fix file input keyboard accessibility#443
🎨 Palette: [UX improvement] Fix file input keyboard accessibility#443aafre wants to merge 1 commit into
Conversation
- Refactored multiple `<input type="file">` elements in `UploadResumeModal`, `TemplateStartModal`, and `IconManager`. - Replaced inaccessible `hidden` (display: none) CSS classes with screen-reader friendly `sr-only peer` classes. - Updated accompanying `<label>` elements to use `htmlFor` and apply `peer-focus-visible` styles to ensure proper keyboard tab order focus rings. - Added a UX journal entry documenting this critical accessibility learning. Co-authored-by: aafre <8656674+aafre@users.noreply.github.com>
|
👋 Jules, reporting for duty! I'm here to lend a hand with this pull request. When you start a review, I'll add a 👀 emoji to each comment to let you know I've read it. I'll focus on feedback directed at me and will do my best to stay out of conversations between you and other bots or reviewers to keep the noise down. I'll push a commit with your requested changes shortly after. Please note there might be a delay between these steps, but rest assured I'm on the job! For more direct control, you can switch me to Reactive Mode. When this mode is on, I will only act on comments where you specifically mention me with New to Jules? Learn more at jules.google/docs. For security, I will only act on instructions from the user who triggered this task. |
There was a problem hiding this comment.
Code Review
This pull request enhances file input accessibility across several components by replacing hidden classes with sr-only peer and adding peer-focus-visible styles to ensure keyboard navigability, while also documenting these patterns in the project's palette. Feedback highlights that disabled pseudo-classes are ineffective on label elements and recommends removing non-functional UI components during processing states to avoid user confusion. Additionally, it is suggested to use unique identifiers or element nesting to prevent potential ID collisions in the DOM.
| <label | ||
| htmlFor="resume-file-input" | ||
| className="btn-primary inline-flex items-center gap-2 px-6 py-3 cursor-pointer disabled:opacity-50" | ||
| className="btn-primary inline-flex items-center gap-2 px-6 py-3 cursor-pointer disabled:opacity-50 peer-focus-visible:ring-2 peer-focus-visible:ring-offset-2 peer-focus-visible:ring-accent" |
There was a problem hiding this comment.
The disabled:opacity-50 utility does not work on <label> elements because they do not support the :disabled pseudo-class. Furthermore, per repository rules, if this call-to-action is non-functional while parsing is true, it should be removed from the UI entirely rather than just styled as disabled to provide better visual feedback and prevent user confusion.
References
- Non-functional UI elements should be removed from the DOM rather than just visually disabled to prevent user confusion, particularly for call-to-action elements.
| @@ -154,7 +154,15 @@ const IconManager: React.FC<IconManagerProps> = ({ | |||
| return ( | |||
| <div className={`icon-manager relative w-12 h-12 ${className}`}> | |||
| <label className={`cursor-pointer relative group ${disabled ? 'pointer-events-none opacity-50' : ''}`}> | |||
There was a problem hiding this comment.
The label for the icon upload is non-functional while isUploading is true. According to repository rules, non-functional call-to-action elements should be removed from the UI to prevent user confusion, rather than just being visually disabled with CSS. Consider conditionally rendering this element only when an upload is not in progress.
References
- Avoid rendering non-functional UI elements. If a call-to-action button cannot perform its intended action, it should be removed to prevent user confusion.
| id="template-start-file-input" | ||
| ref={fileInputRef} | ||
| type="file" | ||
| accept=".pdf,.docx" | ||
| className="hidden" | ||
| className="sr-only peer" | ||
| onChange={handleFileInput} | ||
| /> | ||
| <label | ||
| htmlFor="template-start-file-input" |
There was a problem hiding this comment.
Using a hardcoded ID (template-start-file-input) in a component can lead to collisions if multiple instances of this modal or component are rendered in the DOM simultaneously. Consider nesting the <input> inside the <label> (as seen in IconManager.tsx) or using the useId hook to generate a unique identifier, which avoids the need for manual ID management.
There was a problem hiding this comment.
Code Review
This pull request improves file input accessibility across several components by replacing hidden inputs with sr-only peer elements and adding focus-visible styling. The review identified a potential issue with redundant event propagation in the TemplateStartModal and suggested using peer-disabled modifiers instead of disabled pseudo-classes on labels to correctly reflect the input state.
| onChange={handleFileInput} | ||
| /> |
There was a problem hiding this comment.
Since the parent div (line 159) has an onClick handler that programmatically triggers a click on this input, any click event originating from the input itself (whether via the associated label or direct interaction) will bubble up and cause the parent's handler to fire again. This results in the file selection dialog opening twice in many browsers. Adding onClick={(e) => e.stopPropagation()} to the input prevents this redundant event propagation.
onChange={handleFileInput}
onClick={(e) => e.stopPropagation()}
/>
| <label | ||
| htmlFor="resume-file-input" | ||
| className="btn-primary inline-flex items-center gap-2 px-6 py-3 cursor-pointer disabled:opacity-50" | ||
| className="btn-primary inline-flex items-center gap-2 px-6 py-3 cursor-pointer disabled:opacity-50 peer-focus-visible:ring-2 peer-focus-visible:ring-offset-2 peer-focus-visible:ring-accent" |
There was a problem hiding this comment.
The disabled: utility in Tailwind targets the :disabled pseudo-class, which is not applicable to label elements. To visually reflect the disabled state of the associated input (controlled by the parsing variable), you should use the peer-disabled: modifier instead.
| className="btn-primary inline-flex items-center gap-2 px-6 py-3 cursor-pointer disabled:opacity-50 peer-focus-visible:ring-2 peer-focus-visible:ring-offset-2 peer-focus-visible:ring-accent" | |
| className="btn-primary inline-flex items-center gap-2 px-6 py-3 cursor-pointer peer-disabled:opacity-50 peer-focus-visible:ring-2 peer-focus-visible:ring-offset-2 peer-focus-visible:ring-accent" |
💡 What: Refactored custom file input components to use screen-reader friendly styling instead of
display: noneand added explicit keyboard focus states to their labels.🎯 Why: Previously, using Tailwind's
hiddenclass completely removed the file inputs from the DOM's accessibility tree, meaning users navigating with keyboards (tabbing) or screen readers could not interact with the file upload functionality.📸 Before/After: No visual changes for mouse users, but keyboard users will now see a bright focus ring when tabbing onto the file upload areas.
♿ Accessibility: Replaced
hiddenwithsr-only peeron inputs and addedpeer-focus-visible:ring-2to the corresponding labels, ensuring full keyboard navigation and screen reader support for file uploads.PR created automatically by Jules for task 10385395827234519467 started by @aafre