Skip to content

🎨 Palette: Improve accessibility and focus states of AgentCard icon buttons#80

Open
bobdivx wants to merge 1 commit into
devfrom
palette-agentcard-a11y-17503076025238926114
Open

🎨 Palette: Improve accessibility and focus states of AgentCard icon buttons#80
bobdivx wants to merge 1 commit into
devfrom
palette-agentcard-a11y-17503076025238926114

Conversation

@bobdivx
Copy link
Copy Markdown
Owner

@bobdivx bobdivx commented May 31, 2026

💡 What: Added aria-label attributes to the icon-only buttons in AgentCard.tsx (Start, Stop, Configure), along with aria-hidden='true' on their purely decorative inner SVGs. Added focus-visible classes to render the standard green focus ring for keyboard navigation.

🎯 Why: To improve accessibility for screen reader users and keyboard navigators. Previously, these icon-only buttons lacked programmatic names, meaning screen readers would provide no context, and they lacked distinct focus indicators, making keyboard navigation difficult.

📸 Before/After: The visual layout is unchanged for pointer users, but keyboard focus now triggers a highly visible green ring (#175B37). Screen readers now correctly announce 'Lancer une mission', 'Arrêter', or 'Configurer cet agent'.

Accessibility: Solves a major accessibility compliance issue regarding icon-only interactive controls and keyboard focus visibility.


PR created automatically by Jules for task 17503076025238926114 started by @bobdivx

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

👋 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.

@vercel
Copy link
Copy Markdown

vercel Bot commented May 31, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
forge Ready Ready Preview, Comment May 31, 2026 5:34pm

Copy link
Copy Markdown

@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 formats the AgentCard.tsx component to use double quotes and multi-line formatting, while also introducing accessibility enhancements such as focus rings and ARIA attributes on several interactive elements. The review feedback highlights further opportunities to improve accessibility and correctness, specifically by adding visible focus states to the main card link and the model selector dropdown, providing an accessible label for the model selector, and explicitly setting type="button" on the action and configuration buttons to prevent accidental form submissions.

Comment on lines +137 to +140
<a
href={swarmHref}
class="block flex-1 p-4 pb-3 text-inherit no-underline"
>
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

Accessibility: Missing Focus State on Main Card Link

While focus states have been added to the icon buttons, the main card link (<a>) still lacks a visible focus indicator. When a keyboard user navigates through the cards, there is no visual feedback showing which card is currently focused.

Adding standard focus-visible styles to the link will greatly improve keyboard navigation.

Suggested change
<a
href={swarmHref}
class="block flex-1 p-4 pb-3 text-inherit no-underline"
>
<a
href={swarmHref}
class="block flex-1 p-4 pb-3 text-inherit no-underline focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[#175B37] focus-visible:rounded-2xl"
>

Comment on lines 300 to 309
<button
key={cmd}
onClick={() => onSwarmCommand?.(agent.id, cmd)}
disabled={commandBusy}
class={`rounded-xl border p-2 transition-all disabled:opacity-50 ${cmd === 'start_work' ? 'bg-gray-900 border-gray-900 text-white hover:bg-black' : 'bg-white border-gray-200 text-gray-400 hover:text-rose-500 hover:border-rose-200'}`}
title={cmd === 'start_work' ? 'Lancer une mission' : 'Arrêter'}
class={`rounded-xl border p-2 transition-all disabled:opacity-50 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[#175B37] ${cmd === "start_work" ? "bg-gray-900 border-gray-900 text-white hover:bg-black" : "bg-white border-gray-200 text-gray-400 hover:text-rose-500 hover:border-rose-200"}`}
title={cmd === "start_work" ? "Lancer une mission" : "Arrêter"}
aria-label={
cmd === "start_work" ? "Lancer une mission" : "Arrêter"
}
>
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

Correctness: Missing type="button" on Swarm Command Buttons

In HTML, the default type for a <button> element is "submit". If this card component is ever rendered inside a <form>, clicking these buttons will trigger an unintended form submission.

Explicitly setting type="button" prevents this behavior and is a recommended best practice.

Suggested change
<button
key={cmd}
onClick={() => onSwarmCommand?.(agent.id, cmd)}
disabled={commandBusy}
class={`rounded-xl border p-2 transition-all disabled:opacity-50 ${cmd === 'start_work' ? 'bg-gray-900 border-gray-900 text-white hover:bg-black' : 'bg-white border-gray-200 text-gray-400 hover:text-rose-500 hover:border-rose-200'}`}
title={cmd === 'start_work' ? 'Lancer une mission' : 'Arrêter'}
class={`rounded-xl border p-2 transition-all disabled:opacity-50 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[#175B37] ${cmd === "start_work" ? "bg-gray-900 border-gray-900 text-white hover:bg-black" : "bg-white border-gray-200 text-gray-400 hover:text-rose-500 hover:border-rose-200"}`}
title={cmd === "start_work" ? "Lancer une mission" : "Arrêter"}
aria-label={
cmd === "start_work" ? "Lancer une mission" : "Arrêter"
}
>
<button
key={cmd}
type="button"
onClick={() => onSwarmCommand?.(agent.id, cmd)}
disabled={commandBusy}
class={`rounded-xl border p-2 transition-all disabled:opacity-50 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[#175B37] ${cmd === "start_work" ? "bg-gray-900 border-gray-900 text-white hover:bg-black" : "bg-white border-gray-200 text-gray-400 hover:text-rose-500 hover:border-rose-200"}`}
title={cmd === "start_work" ? "Lancer une mission" : "Arrêter"}
aria-label={
cmd === "start_work" ? "Lancer une mission" : "Arrêter"
}
>

setConfigOpen(true);
}}
class="rounded-xl border border-gray-200 bg-white p-2 text-gray-400 transition-all hover:border-[#175B37]/30 hover:text-[#175B37]"
class="rounded-xl border border-gray-200 bg-white p-2 text-gray-400 transition-all hover:border-[#175B37]/30 hover:text-[#175B37] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[#175B37]"
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

Correctness: Missing type="button" on Configuration Button

Similar to the swarm command buttons, the configuration button lacks an explicit type="button", which defaults its behavior to "submit" and can cause unexpected form submissions if placed inside a <form>.

Suggested change
class="rounded-xl border border-gray-200 bg-white p-2 text-gray-400 transition-all hover:border-[#175B37]/30 hover:text-[#175B37] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[#175B37]"
type="button"
class="rounded-xl border border-gray-200 bg-white p-2 text-gray-400 transition-all hover:border-[#175B37]/30 hover:text-[#175B37] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[#175B37]"

Comment on lines +378 to 390
<div class="mt-0.5 max-w-[145px]" onClick={(e) => e.preventDefault()}>
<select
class="max-w-full cursor-pointer appearance-none truncate border-none bg-transparent pr-2 text-right font-mono text-[9px] text-gray-500 outline-none hover:text-gray-900"
value={agent.model || "Auto"}
onChange={(e) => onModelChange?.(agent.id, (e.currentTarget as HTMLSelectElement).value)}
onChange={(e) =>
onModelChange?.(
agent.id,
(e.currentTarget as HTMLSelectElement).value,
)
}
title="Modifier le modèle"
>
<option value={agent.model}>{agent.model || "Modèle"}</option>
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

Accessibility: Missing Focus State and Accessible Label on Model Selector

The model selection <select> element has outline-none but lacks any focus-visible styles, making it invisible to keyboard navigators when focused. Additionally, it lacks an aria-label to provide programmatic context to screen readers.

Adding a focus ring and aria-label will ensure full accessibility compliance for this interactive control.

Suggested change
<div class="mt-0.5 max-w-[145px]" onClick={(e) => e.preventDefault()}>
<select
class="max-w-full cursor-pointer appearance-none truncate border-none bg-transparent pr-2 text-right font-mono text-[9px] text-gray-500 outline-none hover:text-gray-900"
value={agent.model || "Auto"}
onChange={(e) => onModelChange?.(agent.id, (e.currentTarget as HTMLSelectElement).value)}
onChange={(e) =>
onModelChange?.(
agent.id,
(e.currentTarget as HTMLSelectElement).value,
)
}
title="Modifier le modèle"
>
<option value={agent.model}>{agent.model || "Modèle"}</option>
<div class="mt-0.5 max-w-[145px]" onClick={(e) => e.preventDefault()}>
<select
class="max-w-full cursor-pointer appearance-none truncate border-none bg-transparent pr-2 text-right font-mono text-[9px] text-gray-500 outline-none hover:text-gray-900 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[#175B37] focus-visible:rounded"
value={agent.model || "Auto"}
onChange={(e) =>
onModelChange?.(
agent.id,
(e.currentTarget as HTMLSelectElement).value,
)
}
title="Modifier le modèle"
aria-label="Modifier le modèle"
>
<option value={agent.model}>{agent.model || "Modèle"}</option>

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