🎨 Palette: Improve accessibility with ARIA labels for icon-only buttons#530
🎨 Palette: Improve accessibility with ARIA labels for icon-only buttons#530ngoiyaeric wants to merge 1 commit intomainfrom
Conversation
This commit adds `aria-label` attributes to various icon-only buttons across the application to improve accessibility for screen reader users. Components updated include Header, MobileIconsBar, ChatPanel, FollowupPanel, History, HeaderSearchButton, and CalendarNotepad. Key improvements: - Added descriptive labels to navigation and action buttons. - Ensured all icon-only buttons provide contextual information to assistive technologies. - Added a label to the note textarea in the calendar notepad. - Verified changes with Playwright screenshots. Co-authored-by: ngoiyaeric <115367894+ngoiyaeric@users.noreply.github.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
👋 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. |
|
|
WalkthroughThe PR adds ARIA accessibility labels to interactive UI elements across multiple components without changing behavior or logic. Components updated include calendar-notepad, chat-panel, followup-panel, header-search-button, header, history, and mobile-icons-bar, plus accessibility guidance documentation. Changes
Estimated code review effort🎯 2 (Simple) | ⏱️ ~10 minutes Possibly related PRs
Suggested labels
Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Overall the change is a clear a11y improvement by providing accessible names for icon-only controls. The main follow-ups are about consistency and specificity: ensure labels are unique where multiple similar controls exist, keep title and aria-label text aligned when both are present, and prefer label/aria-labelledby for form fields when feasible to reduce future drift. No functional issues are evident in the diff.
Additional notes (3)
-
Readability |
components/header.tsx:55-61
aria-label="Toggle history"is used both here and incomponents/history.tsx, but this button is specifically tied to the logo interaction (data-testid="logo-history-toggle"). Consider a more specific accessible name so screen reader users can distinguish it from other history toggles (especially if both can appear in the accessibility tree in different layouts). -
Readability |
components/header.tsx:80-87
The button usestitle="Open Calendar"andaria-label="Open calendar". Having both is fine, but they should ideally match exactly to avoid inconsistent announcements/tooltips and to reduce translation/i18n drift (if applicable). -
Maintainability |
components/calendar-notepad.tsx:131-131
Thetextareanow hasaria-label="Add a note"while also having a descriptiveplaceholder. That’s good for accessible naming, but if you also render a visible label elsewhere in the component (not shown in diff),aria-labelwould override it and can cause duplication/mismatched names. Preferaria-labelledbywhen there is (or could be) a visible label element.
Summary of changes
Summary
This PR improves accessibility across the UI by adding explicit accessible names to icon-only controls.
Key updates
- Added
aria-labelattributes to multiple icon-onlyButton/buttonelements in:components/header.tsx(history toggle, calendar, usage)components/mobile-icons-bar.tsx(new chat, calendar, search, upgrade, attachment, submit)components/chat-panel.tsx(new chat, attachment, clear attachment)components/followup-panel.tsx(submit)components/history.tsx(toggle)components/header-search-button.tsx(analyze/search action)components/calendar-notepad.tsx(prev/next week, tag location, fly-to location, and textarea)
Documentation
- Added
.jules/palette.mdentry capturing the a11y learning/action standard for icon-only buttons.
| <a href="https://buy.stripe.com/14A3cv7K72TR3go14Nasg02" target="_blank" rel="noopener noreferrer"> | ||
| <Button variant="ghost" size="icon"> | ||
| <Button variant="ghost" size="icon" aria-label="Upgrade"> | ||
| <TentTree className="h-[1.2rem] w-[1.2rem] transition-all rotate-0 scale-100" /> | ||
| </Button> | ||
| </a> |
There was a problem hiding this comment.
For links that wrap an icon-only button, the accessible name should ideally be on the interactive element that receives focus/activation. Depending on the underlying Button implementation, focus may land on the <a> rather than the inner control, and the label on the inner button may not be used.
Consider moving the accessible name to the anchor (or ensuring the anchor is the only interactive element) to avoid nested interactive semantics and to guarantee the label is announced correctly.
Suggestion
Avoid nested interactive controls and place the label on the element that actually receives focus:
<a
href="..."
target="_blank"
rel="noopener noreferrer"
aria-label="Upgrade"
className="inline-flex"
>
<TentTree ... />
</a>Alternatively, use your Button component’s asChild pattern (if available) so the anchor is the rendered element while keeping button styling. Reply with "@CharlieHelps yes please" if you want me to prepare a commit applying this pattern here.
There was a problem hiding this comment.
Actionable comments posted: 2
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
components/chat-panel.tsx (1)
219-232:⚠️ Potential issue | 🟡 MinorTest selector mismatch prevents desktop attachment button interactions in tests.
The desktop attachment button has
data-testid="desktop-attachment-button", but tests target[data-testid="attachment-button"](chat.spec.ts, responsive.spec.ts, images.spec.ts). No component carries the generic "attachment-button" ID—only the prefixed variants exist ("desktop-attachment-button" and "mobile-attachment-button"). Tests will silently fail to interact with the attachment buttons because the selector cannot be found.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@components/chat-panel.tsx` around lines 219 - 232, The test selector mismatch arises because the desktop attachment Button (in the JSX block rendering Paperclip) uses data-testid="desktop-attachment-button" while tests look for "attachment-button"; update the component so the Button rendered when !isMobile includes the generic data-testid="attachment-button" (you may keep "desktop-attachment-button" as an additional attribute if desired) so tests that query "[data-testid='attachment-button']" can find and interact with the desktop attachment button; ensure this change is applied to the Button that calls handleAttachmentClick (and mirror for the mobile variant if present).
📜 Review details
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro
📒 Files selected for processing (8)
.jules/palette.mdcomponents/calendar-notepad.tsxcomponents/chat-panel.tsxcomponents/followup-panel.tsxcomponents/header-search-button.tsxcomponents/header.tsxcomponents/history.tsxcomponents/mobile-icons-bar.tsx
🧰 Additional context used
🧬 Code graph analysis (3)
components/calendar-notepad.tsx (1)
tests/calendar.spec.ts (1)
test(3-154)
components/chat-panel.tsx (1)
tests/chat.spec.ts (2)
testInfo(64-77)test(4-89)
components/mobile-icons-bar.tsx (3)
components/profile-toggle.tsx (1)
ProfileToggle(9-82)components/map-toggle.tsx (1)
MapToggle(14-39)components/chat.tsx (1)
Chat(29-216)
🪛 markdownlint-cli2 (0.21.0)
.jules/palette.md
[warning] 1-1: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below
(MD022, blanks-around-headings)
[warning] 1-1: First line in a file should be a top-level heading
(MD041, first-line-heading, first-line-h1)
🔇 Additional comments (13)
components/history.tsx (1)
22-22: LGTM. The label accurately reflects the toggle semantics regardless of which icon variant is rendered.components/followup-panel.tsx (1)
62-62: LGTM. Label is descriptive and consistent with the equivalent button label inchat-panel.tsx.components/chat-panel.tsx (2)
178-185: LGTM. New chat button label is correct and consistent withmobile-icons-bar.tsx.
298-303: LGTM. Clear attachment label is descriptive and aligned with the existingdata-testid.components/header-search-button.tsx (1)
150-165: LGTM.aria-labelprovides the accessible name for the icon-only desktop button; the coexistingtitlestill serves as a hover tooltip for mouse users without conflict.components/calendar-notepad.tsx (3)
97-103: LGTM. Navigation labels are clear and follow thePrevious/Next weekweek-navigation convention.
136-152: LGTM.aria-label="Add a note"gives the textarea a concise accessible name, andaria-label="Tag current location"correctly describes the icon-only MapPin action.
167-173: LGTM. "Fly to tagged location" is descriptive and distinct from the "Tag current location" label applied to the textarea's MapPin button.components/mobile-icons-bar.tsx (2)
40-41: LGTM. New chat label is consistent with the desktop equivalent.
56-61: LGTM. Attach file and Send message labels are correct and match chat-panel equivalents.components/header.tsx (3)
80-89: LGTM. Calendar toggle label is consistent withmobile-icons-bar.tsx.
93-117: LGTM. Usage toggle is correctly labeled in both desktop and mobile slots; the mobile and desktop buttons are mutually exclusive viahidden md:flex/flex md:hidden, so there is no duplicate-label issue.
55-69: The duplicatearia-label="Toggle history"accessibility issue does not exist; only the logo button is ever visible.The
HistoryContainerat line 104 is placed inside<div className="flex-1 hidden md:flex ...">(shown at md+ breakpoints) but internally wraps its content with<div className="sm:hidden block">(hidden at md+ breakpoints). This means theHistorycomponent is never actually rendered to users at any screen size due to the conflicting visibility classes. Only the logo button (lines 55–69) witharia-label="Toggle history"remains visible and accessible. There is no duplicate button to differentiate.However, the
HistoryContainerplacement inside the desktop-only container combined with its own mobile-only visibility class suggests dead code or a configuration error that should be clarified.Likely an incorrect or invalid review comment.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In @.jules/palette.md:
- Around line 1-3: Change the level-2 heading to a top-level heading and add a
blank line after it to satisfy MD041 and MD022: replace "## 2025-05-15 -
[Accessibility: Icon-Only Button Labels]" with a single leading '#' heading and
insert an empty line immediately after that heading so the file starts with a
top-level heading and a following blank line.
In `@components/mobile-icons-bar.tsx`:
- Around line 48-50: The Search Button (Button with
data-testid="mobile-search-button" and aria-label="Search") is labeled but has
no onClick handler, causing a silent failure for screen-reader users; either
wire this Button to the correct click action (add the onClick handler that
triggers the mobile search flow) or mark it as non-interactive until implemented
(add disabled={true} and ensure aria-disabled="true") and keep the aria-label;
update the Button in components/mobile-icons-bar.tsx accordingly so the
accessible state matches functionality.
---
Outside diff comments:
In `@components/chat-panel.tsx`:
- Around line 219-232: The test selector mismatch arises because the desktop
attachment Button (in the JSX block rendering Paperclip) uses
data-testid="desktop-attachment-button" while tests look for
"attachment-button"; update the component so the Button rendered when !isMobile
includes the generic data-testid="attachment-button" (you may keep
"desktop-attachment-button" as an additional attribute if desired) so tests that
query "[data-testid='attachment-button']" can find and interact with the desktop
attachment button; ensure this change is applied to the Button that calls
handleAttachmentClick (and mirror for the mobile variant if present).
| ## 2025-05-15 - [Accessibility: Icon-Only Button Labels] | ||
| **Learning:** Icon-only buttons without explicit `aria-label` attributes are inaccessible to screen reader users, as they often lack descriptive text content. While `title` attributes provide some information on hover, they are not a substitute for `aria-label` in terms of accessibility standards. | ||
| **Action:** Always ensure icon-only buttons have an `aria-label` or an `sr-only` span describing their action. This project now consistently uses `aria-label` for common interface actions like toggling history, opening calendars, and submitting forms. |
There was a problem hiding this comment.
Fix markdown heading level and missing blank line (MD041, MD022).
The file opens with a level-2 heading (##) — MD041 requires the first line of a file to be a top-level heading (#). MD022 also requires a blank line after the heading.
📝 Proposed fix
-## 2025-05-15 - [Accessibility: Icon-Only Button Labels]
+# 2025-05-15 - [Accessibility: Icon-Only Button Labels]
+
**Learning:** Icon-only buttons without explicit `aria-label` attributes...📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| ## 2025-05-15 - [Accessibility: Icon-Only Button Labels] | |
| **Learning:** Icon-only buttons without explicit `aria-label` attributes are inaccessible to screen reader users, as they often lack descriptive text content. While `title` attributes provide some information on hover, they are not a substitute for `aria-label` in terms of accessibility standards. | |
| **Action:** Always ensure icon-only buttons have an `aria-label` or an `sr-only` span describing their action. This project now consistently uses `aria-label` for common interface actions like toggling history, opening calendars, and submitting forms. | |
| # 2025-05-15 - [Accessibility: Icon-Only Button Labels] | |
| **Learning:** Icon-only buttons without explicit `aria-label` attributes are inaccessible to screen reader users, as they often lack descriptive text content. While `title` attributes provide some information on hover, they are not a substitute for `aria-label` in terms of accessibility standards. | |
| **Action:** Always ensure icon-only buttons have an `aria-label` or an `sr-only` span describing their action. This project now consistently uses `aria-label` for common interface actions like toggling history, opening calendars, and submitting forms. |
🧰 Tools
🪛 markdownlint-cli2 (0.21.0)
[warning] 1-1: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below
(MD022, blanks-around-headings)
[warning] 1-1: First line in a file should be a top-level heading
(MD041, first-line-heading, first-line-h1)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In @.jules/palette.md around lines 1 - 3, Change the level-2 heading to a
top-level heading and add a blank line after it to satisfy MD041 and MD022:
replace "## 2025-05-15 - [Accessibility: Icon-Only Button Labels]" with a single
leading '#' heading and insert an empty line immediately after that heading so
the file starts with a top-level heading and a following blank line.
| <Button variant="ghost" size="icon" data-testid="mobile-search-button" aria-label="Search"> | ||
| <Search className="h-[1.2rem] w-[1.2rem] transition-all rotate-0 scale-100" /> | ||
| </Button> |
There was a problem hiding this comment.
Labeled but non-functional button worsens screen reader UX.
The Search button has aria-label="Search" but no onClick handler. Before this change the unlabeled icon was unlikely to be interacted with by assistive technology users. Adding a label now makes it fully discoverable, so screen reader users will attempt to activate it and receive no response — a silent failure. Either wire up the handler or add disabled until the feature is implemented.
🛡️ Minimal fix until onClick is available
- <Button variant="ghost" size="icon" data-testid="mobile-search-button" aria-label="Search">
+ <Button variant="ghost" size="icon" data-testid="mobile-search-button" aria-label="Search" disabled>🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@components/mobile-icons-bar.tsx` around lines 48 - 50, The Search Button
(Button with data-testid="mobile-search-button" and aria-label="Search") is
labeled but has no onClick handler, causing a silent failure for screen-reader
users; either wire this Button to the correct click action (add the onClick
handler that triggers the mobile search flow) or mark it as non-interactive
until implemented (add disabled={true} and ensure aria-disabled="true") and keep
the aria-label; update the Button in components/mobile-icons-bar.tsx accordingly
so the accessible state matches functionality.
I have implemented a series of micro-UX improvements focused on accessibility (a11y) by adding
aria-labelattributes to icon-only buttons and inputs throughout the application.💡 What
aria-labelto buttons incomponents/header.tsx(history, calendar, usage).aria-labelto buttons incomponents/mobile-icons-bar.tsx(new chat, profile, map, calendar, search, upgrade, attachment, submit).aria-labelto buttons incomponents/chat-panel.tsx(new chat, attachment, clear attachment).aria-labelto the submit button incomponents/followup-panel.tsx.aria-labelto the toggle button incomponents/history.tsx.aria-labelto the search button incomponents/header-search-button.tsx.aria-labelto navigation and action buttons incomponents/calendar-notepad.tsx.aria-labelto the textarea incomponents/calendar-notepad.tsx.🎯 Why
Icon-only buttons are often inaccessible to screen readers as they lack text content. By adding
aria-label, we ensure that users relying on assistive technologies can understand the purpose and action of each button.♿ Accessibility
This change specifically targets WCAG guidelines for accessible names and roles, making the core functionality of the application more inclusive.
✅ Verification
Verified the presence and visibility of these labels using a Playwright script and captured screenshots of the UI.
PR created automatically by Jules for task 7996752336833846116 started by @ngoiyaeric
Summary by CodeRabbit
Accessibility
Documentation