mirror of
https://github.com/rennf93/roboco.git
synced 2026-08-03 07:23:24 +02:00
* [001c9a7a] Author tooltip/aria-label spec for the panel (#469) (#473) * [001c9a7a] docs(ux_ui): add tooltip/aria-label classification spec for panel controls * [001c9a7a] docs(ux_ui): commit missing tooltip/aria-label spec content Prior commit's message claimed to add the spec but only touched unrelated generated lifecycle prompt files — the actual spec file was never git-added. This commits the real content. --------- Co-authored-by: UX/UI Developer 1 <ux-dev-1@roboco.tech> * [dbe222aa] Implement tooltip and aria-label sweep across all panel surfaces (#478) * [6f991331] Add aria-label + matching tooltip per tooltip-aria-label-spec.md (#476) * [6f991331] feat(panel): add aria-label + matching tooltip to 8 icon-only controls per tooltip-aria-label-spec.md §1a/§1b, wrap assignee-avatar initials in a full-name tooltip * [6f991331] docs(accessibility): add icon-only controls pattern guide for aria-label + matching tooltip Documented the implemented pattern for accessible icon-only controls across 8 components (bell, back-arrow, menu, toggle, drag-handle, move-forward, settings, review-link) plus the assignee-avatar tooltip. Covers when to apply the pattern, naming conventions, state-dependent labels, testing approach, and rationale for local TooltipProvider scope. --------- Co-authored-by: Frontend Developer 1 <fe-dev-1@roboco.tech> Co-authored-by: Frontend Documenter <fe-doc@roboco.tech> * [e34da833] Fix notification-bell.tsx and assignee-avatar.tsx, re-verify all 9 claimed tooltip/aria-label retrofits (#480) * [e34da833] test(notifications): add regression coverage confirming the bell button's aria-label/title/Tooltip and re-verify the other 8 tooltip-aria-label-spec controls by direct file read * [e34da833] docs(ux_ui): update tooltip-aria-label-spec.md status to "implemented" with test coverage summary --------- Co-authored-by: Frontend Developer 1 <fe-dev-1@roboco.tech> Co-authored-by: Frontend Documenter <fe-doc@roboco.tech> * [09414273] fix(header): wrap refresh button in Tooltip; correct spec.md and accessible-icon-buttons.md doc-accuracy issues (#483) Co-authored-by: Frontend Developer 1 <fe-dev-1@roboco.tech> --------- Co-authored-by: Frontend Developer 1 <fe-dev-1@roboco.tech> Co-authored-by: Frontend Documenter <fe-doc@roboco.tech> * [f309463f] fix: missing tooltip/Link/ArrowLeft imports + dedupe command-center tooltip import, drop redundant native title on refresh button, reflow doc prose - kanban-card.tsx, header.tsx: import TooltipProvider (used but undefined -> eslint react/jsx-no-undef, blocked Panel lint + QA image panel build) - task-header.tsx: import Link (next/link) and ArrowLeft (lucide-react) for the back button tooltip - command-center.tsx: remove the duplicate tooltip primitive import block (kept the one with TooltipProvider; tsc duplicate-identifier) - header.tsx: drop native title= on the refresh button now that a Radix Tooltip carries the hint (header test expects no native title) - docs/frontend/components/accessible-icon-buttons.md: reflow hard-wrapped prose (python gate make reflow-docs) * [f309463f] chore: regenerate lifecycle artifacts + verb tables (reconcile after master merge) The branch's generated intro prose in agents/prompts/_generated/lifecycle-*.md and verbs.md had drifted to unwrapped lines (master is wrapped). The foundation- check gate (make lifecycle + regenerate_verb_tables + git diff --exit-code) caught the drift. Re-rendered via the canonical generators; no hand-edits. * [f309463f] Close remaining a11y gaps: aria-labels on task-table row-expand + pagination, titles on work-session truncated task-id/branch, secretary Start loading label --------- Co-authored-by: UX/UI Developer 1 <ux-dev-1@roboco.tech> Co-authored-by: Frontend Developer 1 <fe-dev-1@roboco.tech> Co-authored-by: Frontend Documenter <fe-doc@roboco.tech> Co-authored-by: Renn F <rennf93@users.noreply.github.com>
159 lines
4.7 KiB
Markdown
159 lines
4.7 KiB
Markdown
# Accessible Icon-Only Controls (aria-label + Tooltip)
|
|
|
|
## Overview
|
|
|
|
Icon-only controls—buttons without visible text—require two layers of accessibility to be usable by all:
|
|
|
|
1. **aria-label** attribute for screen reader users
|
|
2. **Visible Tooltip** (matching text) for mouse, keyboard, and screen reader users
|
|
|
|
Both layers must use identical text that describes the action or result.
|
|
|
|
## Pattern
|
|
|
|
All icon-only controls in the panel follow this structure:
|
|
|
|
```typescript
|
|
import {
|
|
Tooltip,
|
|
TooltipContent,
|
|
TooltipProvider,
|
|
TooltipTrigger,
|
|
} from "@/components/ui/tooltip";
|
|
|
|
const LABEL = "Open settings";
|
|
|
|
<TooltipProvider>
|
|
<Tooltip>
|
|
<TooltipTrigger asChild>
|
|
<Button
|
|
aria-label={LABEL}
|
|
title={LABEL}
|
|
>
|
|
<Settings className="h-5 w-5" />
|
|
</Button>
|
|
</TooltipTrigger>
|
|
<TooltipContent>{LABEL}</TooltipContent>
|
|
</Tooltip>
|
|
</TooltipProvider>
|
|
```
|
|
|
|
### Why three attributes?
|
|
|
|
- **aria-label**: The accessible name for screen readers
|
|
- **title**: Browser native tooltip (fallback, appears on hover/focus)
|
|
- **TooltipContent**: Radix UI tooltip for consistent visual feedback
|
|
|
|
### Naming convention
|
|
|
|
Label text uses **active verbs** describing what happens when the control is clicked:
|
|
|
|
| Control | Label |
|
|
|---------|-------|
|
|
| Settings gear | "Open settings" |
|
|
| Notification bell | "View notifications" |
|
|
| Back arrow | "Go back to tasks list" |
|
|
| Collapse toggle | "Collapse sidebar" / "Expand sidebar" |
|
|
| Drag handle | "Drag to move task between columns" |
|
|
| Menu trigger | "Open task actions menu" |
|
|
|
|
Avoid passive voice ("Settings opened") or generic labels ("Button").
|
|
|
|
## State-dependent labels
|
|
|
|
When a control's action varies by state, compute the label dynamically:
|
|
|
|
```typescript
|
|
const toggleLabel = sidebarCollapsed ? "Expand sidebar" : "Collapse sidebar";
|
|
|
|
<Button
|
|
aria-label={toggleLabel}
|
|
title={toggleLabel}
|
|
>
|
|
{/* ... */}
|
|
</Button>
|
|
```
|
|
|
|
The label updates whenever state changes, keeping screen reader users informed.
|
|
|
|
## Truncated content (avatars, badges)
|
|
|
|
When an icon-only control displays shortened content (e.g., "FD1" for "Frontend Dev 1"), wrap in a tooltip showing the full value:
|
|
|
|
```typescript
|
|
<TooltipProvider>
|
|
<Tooltip>
|
|
<TooltipTrigger asChild>
|
|
<Avatar>
|
|
<AvatarFallback>{initials}</AvatarFallback>
|
|
</Avatar>
|
|
</TooltipTrigger>
|
|
<TooltipContent>{fullName}</TooltipContent>
|
|
</Tooltip>
|
|
</TooltipProvider>
|
|
```
|
|
|
|
## When NOT to use this pattern
|
|
|
|
**Do not** apply aria-label + tooltip to:
|
|
|
|
- **Text-labeled buttons** — the text is the label
|
|
- **Decorative icons** — non-interactive graphics (use `aria-hidden="true"` instead)
|
|
- **Self-labeling badges** — content + styling conveys meaning
|
|
- **Chart tooltips** — handled by the charting library (recharts, etc.)
|
|
|
|
## Implemented controls
|
|
|
|
The following 8 icon-only controls have been retrofitted:
|
|
|
|
1. **notification-bell.tsx** — "View notifications"
|
|
2. **task-header.tsx** (back button) — "Go back to tasks list"
|
|
3. **task-actions.tsx** (menu) — "Open task actions menu"
|
|
4. **sidebar.tsx** (collapse toggle) — "Collapse sidebar" / "Expand sidebar"
|
|
5. **kanban-card.tsx** (drag handle) — "Drag to move task between columns"
|
|
6. **kanban-card.tsx** (move-forward) — "Move forward" / "PM must activate this task first"
|
|
7. **command-center.tsx** (settings) — "Open settings"
|
|
8. **pr-review-queue.tsx** (details link) — "Review details"
|
|
|
|
Plus one avatar tooltip:
|
|
|
|
9. **assignee-avatar.tsx** — Shows full agent display name
|
|
|
|
## Testing
|
|
|
|
### Screen reader
|
|
1. Tab to the icon-only control
|
|
2. Verify the aria-label is announced
|
|
3. Focus should be visible and clear
|
|
|
|
### Mouse
|
|
1. Hover over the control
|
|
2. Tooltip appears with the same text as aria-label
|
|
3. Click triggers the expected action
|
|
|
|
### Keyboard
|
|
1. Tab to focus the control
|
|
2. title attribute provides a browser tooltip
|
|
3. Verify the label is consistent
|
|
|
|
### State changes
|
|
1. For conditional labels, verify the label updates when state changes
|
|
2. Tab away and back to re-announce the new label
|
|
|
|
## TooltipProvider scope
|
|
|
|
Each component wraps its controls in a local `<TooltipProvider>` (not a single app-root provider). This pattern:
|
|
|
|
- Keeps tooltip state scoped to the component
|
|
- Matches existing codebase patterns
|
|
- Simplifies DOM structure and reduces global state
|
|
|
|
If a future refactoring uses a root-level provider, the structure remains valid—only the wrapping changes, not the aria-label/title pattern.
|
|
|
|
## Resources
|
|
|
|
- [WCAG 2.1: Text Alternatives for Images](https://www.w3.org/WAI/WCAG21/Understanding/text-alternatives)
|
|
- [ARIA Authoring Practices: Buttons](https://www.w3.org/WAI/ARIA/apg/patterns/button/)
|
|
- [Radix UI Tooltip](https://www.radix-ui.com/docs/primitives/components/tooltip)
|
|
- Local test files: See `kanban-card-aria.test.tsx`, `sidebar.test.tsx`, `assignee-avatar.test.tsx`
|