feat: add accessible hover tooltips to dashboard summary stat cards (Closes #484)#799
Conversation
…loses SolFoundry#484) - Added optional tooltip prop to SummaryCard component - Tooltips appear on hover AND keyboard focus (WCAG 2.1 SC 1.4.13) - Dismissible via Escape key - Added aria-describedby linking card to tooltip - Descriptive tooltips on all 4 dashboard stats - Pure Tailwind CSS, no external dependencies - Dark mode support
📝 WalkthroughWalkthroughThe Estimated code review effort🎯 2 (Simple) | ⏱️ ~10 minutes Suggested labels
Suggested reviewers
🚥 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)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@frontend/src/components/ContributorDashboard.tsx`:
- Line 249: The current tooltipId derived from label can collide; change to a
stable unique ID strategy (e.g., React's useId or a generated UUID) instead of
using label text. In ContributorDashboard.tsx, replace the label-based tooltipId
with a useId-derived or otherwise unique base (call it something like baseId
from useId()) and build tooltipId as `${baseId}-tooltip` when tooltip exists;
update any corresponding aria-describedby usages to reference this new tooltipId
so IDs remain unique across cards regardless of label text.
- Around line 252-261: The tooltip's described-by relationship is conditional on
showTooltip, which can cause screen readers to miss announcing it on first
focus; in the ContributorDashboard component ensure the aria-describedby
attribute and the tooltip element exist stably in the DOM regardless of
visibility: always set aria-describedby to tooltipId when tooltip text exists
(not only when showTooltip is true) and render the tooltip node even when hidden
(use aria-hidden or visually-hidden styles when showTooltip is false) so the ID
target is present for assistive tech; update references to tooltip, showTooltip
and tooltipId accordingly (e.g., the wrapper div's aria-describedby and the
tooltip element render logic around lines with tabIndex, onFocus/onBlur, and the
tooltip DOM block).
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro
Run ID: 42888b4f-dd8a-447b-b61b-21cedcc18b5d
📒 Files selected for processing (1)
frontend/src/components/ContributorDashboard.tsx
| function SummaryCard({ label, value, suffix, icon, trend, trendValue }: SummaryCardProps) { | ||
| function SummaryCard({ label, value, suffix, icon, trend, trendValue, tooltip }: SummaryCardProps) { | ||
| const [showTooltip, setShowTooltip] = useState(false); | ||
| const tooltipId = tooltip ? `tooltip-${label.toLowerCase().replace(/\s+/g, '-')}` : undefined; |
There was a problem hiding this comment.
🛠️ Refactor suggestion | 🟠 Major
Label-derived tooltip IDs are collision-prone
On Line 249, ID generation is derived from label. If two cards share the same label (now or in future), duplicate IDs can break aria-describedby mapping and produce invalid DOM semantics. This should use an ID strategy that guarantees uniqueness independent of display text.
As per coding guidelines, frontend/**: "React/TypeScript frontend. Check: Integration with existing components".
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@frontend/src/components/ContributorDashboard.tsx` at line 249, The current
tooltipId derived from label can collide; change to a stable unique ID strategy
(e.g., React's useId or a generated UUID) instead of using label text. In
ContributorDashboard.tsx, replace the label-based tooltipId with a useId-derived
or otherwise unique base (call it something like baseId from useId()) and build
tooltipId as `${baseId}-tooltip` when tooltip exists; update any corresponding
aria-describedby usages to reference this new tooltipId so IDs remain unique
across cards regardless of label text.
| <div | ||
| className="bg-white dark:bg-surface-100 rounded-xl p-5 border border-gray-200 hover:border-gray-300 dark:border-white/5 dark:hover:border-white/10 transition-colors shadow-sm dark:shadow-none relative" | ||
| onMouseEnter={() => tooltip && setShowTooltip(true)} | ||
| onMouseLeave={() => setShowTooltip(false)} | ||
| onFocus={() => tooltip && setShowTooltip(true)} | ||
| onBlur={() => setShowTooltip(false)} | ||
| onKeyDown={(e) => e.key === 'Escape' && setShowTooltip(false)} | ||
| tabIndex={tooltip ? 0 : undefined} | ||
| aria-describedby={showTooltip ? tooltipId : undefined} | ||
| > |
There was a problem hiding this comment.
State-dependent tooltip linkage can fail screen-reader announcement on first focus
On Line 260 and Lines 279-288, aria-describedby and the tooltip node are only present after focus/hover updates state. This can make keyboard focus land before assistive tech has a stable description target, so tooltip text may not be announced reliably, undermining the “accessible tooltip” objective.
As per coding guidelines, frontend/**: "React/TypeScript frontend. Check: Component structure and state management".
Also applies to: 279-288
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@frontend/src/components/ContributorDashboard.tsx` around lines 252 - 261, The
tooltip's described-by relationship is conditional on showTooltip, which can
cause screen readers to miss announcing it on first focus; in the
ContributorDashboard component ensure the aria-describedby attribute and the
tooltip element exist stably in the DOM regardless of visibility: always set
aria-describedby to tooltipId when tooltip text exists (not only when
showTooltip is true) and render the tooltip node even when hidden (use
aria-hidden or visually-hidden styles when showTooltip is false) so the ID
target is present for assistive tech; update references to tooltip, showTooltip
and tooltipId accordingly (e.g., the wrapper div's aria-describedby and the
tooltip element render logic around lines with tabIndex, onFocus/onBlur, and the
tooltip DOM block).
Summary
Add informative, accessible hover tooltips to all four dashboard summary stat cards.
Closes #484
Changes
frontend/src/components/ContributorDashboard.tsx— Added optionaltooltipprop toSummaryCardwith full keyboard accessibility.Implementation
aria-describedbylinking card to tooltiptabIndex={0}for keyboard focusabilityWallet
61FYMEPXMe73ypR53wMAR7PYAWHhZWKFJMNKnG9NwoW