Skip to content

feat: add accessible hover tooltips to dashboard summary stat cards (Closes #484)#799

Merged
chronoeth-creator merged 1 commit intoSolFoundry:mainfrom
orgy272:feat/tooltip-v3
Mar 24, 2026
Merged

feat: add accessible hover tooltips to dashboard summary stat cards (Closes #484)#799
chronoeth-creator merged 1 commit intoSolFoundry:mainfrom
orgy272:feat/tooltip-v3

Conversation

@orgy272
Copy link
Contributor

@orgy272 orgy272 commented Mar 23, 2026

Summary

Add informative, accessible hover tooltips to all four dashboard summary stat cards.

Closes #484

Changes

  • frontend/src/components/ContributorDashboard.tsx — Added optional tooltip prop to SummaryCard with full keyboard accessibility.

Implementation

  • Hover + focus tooltip display (mouse and keyboard)
  • Escape key dismissal (WCAG 2.1 SC 1.4.13)
  • aria-describedby linking card to tooltip
  • tabIndex={0} for keyboard focusability
  • Pure Tailwind CSS, zero external dependencies
  • Dark mode support
  • Minimal diff: 29 lines added in a single file

Wallet

61FYMEPXMe73ypR53wMAR7PYAWHhZWKFJMNKnG9NwoW

…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
@coderabbitai
Copy link

coderabbitai bot commented Mar 23, 2026

📝 Walkthrough

Walkthrough

The SummaryCard component in ContributorDashboard.tsx has been extended to support optional tooltips. An optional tooltip prop was added to the component, along with internal state (showTooltip) and a derived tooltipId. The root element now conditionally enables tooltip behavior through mouse enter/leave and focus/blur event handlers, keyboard escape handling, and conditional tabIndex and aria-describedby attributes. A tooltip element renders absolutely positioned above the card when both showTooltip and tooltip are truthy. Tooltip text has been added to four card usages: Total Earned, Active Bounties, Pending Payouts, and Reputation Rank.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Suggested labels

approved

Suggested reviewers

  • chronoeth-creator
🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately describes the main change: adding accessible hover tooltips to dashboard summary stat cards, with a reference to the issue being closed.
Description check ✅ Passed The description is clearly related to the changeset, providing summary, implementation details, and specific information about the tooltip feature added to the SummaryCard component.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

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

📥 Commits

Reviewing files that changed from the base of the PR and between 72d63b0 and 2297c28.

📒 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;
Copy link

Choose a reason for hiding this comment

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

🛠️ 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.

Comment on lines +252 to +261
<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}
>
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

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

@chronoeth-creator chronoeth-creator merged commit 4eac15d into SolFoundry:main Mar 24, 2026
9 of 13 checks passed
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