Skip to content

🎨 Palette: [UX improvement] Make UploadResumeModal fully accessible#477

Closed
aafre wants to merge 1 commit into
mainfrom
palette-a11y-upload-modal-2822450963525978763
Closed

🎨 Palette: [UX improvement] Make UploadResumeModal fully accessible#477
aafre wants to merge 1 commit into
mainfrom
palette-a11y-upload-modal-2822450963525978763

Conversation

@aafre
Copy link
Copy Markdown
Owner

@aafre aafre commented Apr 15, 2026

💡 What

This PR updates the UploadResumeModal component to significantly improve its keyboard and screen-reader accessibility.

🎯 Why

Custom styled file uploads (which hide the original input type="file") often break keyboard navigation if they are completely hidden from the DOM (e.g., via display: none or Tailwind's hidden). Users navigating by keyboard cannot "tab" to the input to trigger the file dialog. Furthermore, the modal lacked appropriate semantic dialog roles and ARIA labels.

♿ Accessibility

  • Added role="dialog", aria-modal="true", and aria-labelledby to properly announce the modal to screen readers.
  • Removed the hidden class from the file <input> and applied sr-only peer, keeping the element visually hidden but explicitly within the keyboard tab sequence.
  • Added peer-focus-visible classes to the "Choose File" label, creating a clear blue focus ring when a user tabs into the hidden file input.
  • Added an aria-label="Close dialog" to the close icon button, and added high-contrast focus-visible styling for keyboard navigation.
  • Added a journal entry to .Jules/palette.md to ensure this accessible custom file input pattern is standardized across the codebase.

PR created automatically by Jules for task 2822450963525978763 started by @aafre

- Add `role="dialog"`, `aria-modal="true"`, and `aria-labelledby` to modal container for screen readers.
- Hide file input with `sr-only peer` (instead of `hidden`) to keep it in the DOM keyboard navigation tab order.
- Add visible focus rings `peer-focus-visible:ring-2` to the file upload label wrapper for clear keyboard indications.
- Add `aria-label` and visible focus outlines to the modal close button.
- Document custom file input accessibility approach in `.Jules/palette.md`.

Co-authored-by: aafre <8656674+aafre@users.noreply.github.com>
@google-labs-jules
Copy link
Copy Markdown
Contributor

👋 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 @jules. You can find this option in the Pull Request section of your global Jules UI settings. You can always switch back!

New to Jules? Learn more at jules.google/docs.


For security, I will only act on instructions from the user who triggered this task.

Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request enhances the accessibility of the UploadResumeModal by adding ARIA attributes, improving keyboard navigation for file uploads, and updating the project's accessibility documentation. The changes include replacing hidden file inputs with screen-reader-only elements and adding focus-visible styles. Feedback identifies a missing focus trap in the modal and suggests using peer-disabled modifiers for the file input label to correctly represent the disabled state, as standard disabled pseudo-classes are ineffective on labels.

Comment on lines +93 to +95
role="dialog"
aria-modal="true"
aria-labelledby="upload-modal-title"
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

While adding ARIA roles and labels is a significant improvement for screen reader support, this modal is missing a focus trap. According to the general rules, all modals must implement a focus trap to ensure keyboard focus remains within the modal's focusable elements while it is open. This is critical for accessibility, especially when aria-modal="true" is used, as it prevents users from accidentally tabbing out of the modal into the background content.

References
  1. For accessibility, all modals must implement a focus trap to ensure keyboard focus remains within the modal's focusable elements while it is open.

<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-accent peer-focus-visible:ring-offset-2"
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The disabled:opacity-50 utility does not work on a element because labels do not support the :disabled pseudo-class. Since the associated input is a peer, you should use peer-disabled:opacity-50 to visually indicate the disabled state (e.g., when parsing is true). Additionally, adding peer-disabled:cursor-not-allowed improves the UX by providing clear feedback that the action is unavailable.

Suggested change
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-accent peer-focus-visible:ring-offset-2"
className="btn-primary inline-flex items-center gap-2 px-6 py-3 cursor-pointer peer-disabled:opacity-50 peer-disabled:cursor-not-allowed peer-focus-visible:ring-2 peer-focus-visible:ring-accent peer-focus-visible:ring-offset-2"

Comment thread .Jules/palette.md

## 2025-02-14 - Custom File Input Keyboard Accessibility
**Learning:** Using `display: none` or Tailwind's `hidden` on a `<input type="file">` element completely removes it from the browser's accessibility tree and keyboard tab order, breaking keyboard navigation for file uploads.
**Action:** When styling custom file inputs, always place the `<input type="file">` before the visual `<label>` wrapper in the DOM, style it with `sr-only peer` to keep it focusable but visually hidden, and add `peer-focus-visible` styling (like `peer-focus-visible:ring-2`) to the subsequent `<label>` to visually indicate keyboard focus.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Consider updating this action item to include the peer-disabled pattern. When styling custom file inputs where a label acts as the visual trigger, the label must reflect the input's disabled state (e.g., during file processing) to provide a consistent UX, as standard disabled: modifiers are ineffective on labels.

Suggested change
**Action:** When styling custom file inputs, always place the `<input type="file">` before the visual `<label>` wrapper in the DOM, style it with `sr-only peer` to keep it focusable but visually hidden, and add `peer-focus-visible` styling (like `peer-focus-visible:ring-2`) to the subsequent `<label>` to visually indicate keyboard focus.
Action: When styling custom file inputs, always place the <input type="file"> before the visual <label> wrapper in the DOM, style it with sr-only peer to keep it focusable but visually hidden. Add peer-focus-visible styling (like peer-focus-visible:ring-2) and peer-disabled styling (like peer-disabled:opacity-50 peer-disabled:cursor-not-allowed) to the subsequent <label> to ensure the visual state correctly reflects the input's focus and disabled states.
References
  1. Ensure documentation, such as testing plans, is updated to reflect UI and implementation changes made in the same pull request.

@aafre
Copy link
Copy Markdown
Owner Author

aafre commented May 25, 2026

Closing duplicate — the most recent version of this change is PR #537.

@aafre aafre closed this May 25, 2026
@google-labs-jules
Copy link
Copy Markdown
Contributor

Closing duplicate — the most recent version of this change is PR #537.

Understood. Acknowledging that this work is now obsolete and stopping work on this task.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant