Skip to content

Comments

fix: render CardLink as native button when no href is provided#3476

Merged
lorgonal merged 1 commit intomainfrom
fix/card-link-a11y-button-fallback
Feb 23, 2026
Merged

fix: render CardLink as native button when no href is provided#3476
lorgonal merged 1 commit intomainfrom
fix/card-link-a11y-button-fallback

Conversation

@lorgonal
Copy link
Contributor

@lorgonal lorgonal commented Feb 19, 2026

Summary

  • CardLink now renders a native <button> when no href is provided (onClick-only usage), instead of going through the Link component which marks it as aria-disabled="true"
  • Fixes an accessibility issue where screen readers announce the element as disabled and keyboard users cannot activate it, despite the button being fully functional
  • Adds tests and stories for the new button fallback behavior

Problem

When CardLink is used without an href (e.g. in Factorial's BalanceCounter widget which passes onClick but no url), the underlying Link component in linkHandler.tsx evaluates !props.href as truthy and sets isDisabled = true. This renders a <span aria-disabled="true" disabled=""> with role="button" — semantically misleading and inaccessible:

  • Screen readers announce it as disabled/unavailable
  • No native keyboard support (Enter/Space don't activate <span>)
  • role="button" + aria-disabled="true" contradicts the element's actual behavior

Solution

CardLink now branches on href:

  • No href → renders a native <button type="button"> with proper a11y semantics
  • Has href → unchanged behavior via Link

Anchor-specific props (target, rel, download) are stripped when rendering as <button>.

Alternatives Considered

Fix in Link/linkHandler.tsx: Change the isDisabled = !props.href || disabled logic so Link doesn't mark itself disabled when href is absent but onClick is present. This would fix it globally for all Link consumers.

We opted against this because:

  • Changing Link's core disabled logic could have unintended side effects across the design system
  • A <button> is the semantically correct element for a click-only action — it's not a link without a destination, it's an action trigger
  • Localizing the fix to CardLink has zero risk to other Link consumers

Testing

  • 3 new unit tests for Card.test.tsx (button rendering, click handler, link fallback)
  • New stories: WithClickableLink, WithHrefLink in Card stories
  • New story WithOnClickOnlyLink in Widget stories
  • All 18 tests passing

@lorgonal lorgonal requested a review from a team as a code owner February 19, 2026 08:23
@github-actions github-actions bot added fix react Changes affect packages/react labels Feb 19, 2026
@github-actions
Copy link
Contributor

github-actions bot commented Feb 19, 2026

✅ No New Circular Dependencies

No new circular dependencies detected. Current count: 0

@github-actions
Copy link
Contributor

github-actions bot commented Feb 19, 2026

📦 Alpha Package Version Published

Use pnpm i github:factorialco/f0#npm/alpha-pr-3476 to install the package

Use pnpm i github:factorialco/f0#6586bdc69834ff749a348b014ef7368ef35f02b5 to install this specific commit

@github-actions
Copy link
Contributor

github-actions bot commented Feb 19, 2026

🔍 Visual review for your branch is published 🔍

Here are the links to:

@github-actions
Copy link
Contributor

github-actions bot commented Feb 19, 2026

Coverage Report for packages/react

Status Category Percentage Covered / Total
🔵 Lines 38.77% 7101 / 18314
🔵 Statements 38.16% 7259 / 19020
🔵 Functions 29.28% 1514 / 5169
🔵 Branches 28.56% 4123 / 14436
File Coverage
File Stmts Branches Functions Lines Uncovered Lines
Changed Files
packages/react/src/ui/Card/Card.tsx 97.5% 82.35% 100% 100% 45
Generated in workflow #11120 for commit 1eb1b75 by the Vitest Coverage Report Action

@lorgonal lorgonal force-pushed the fix/card-link-a11y-button-fallback branch from 3ceaa3b to 0a08f14 Compare February 19, 2026 08:36
When CardLink is used without an href (onClick-only), the underlying
Link component marks it as disabled with aria-disabled="true", making
it inaccessible to keyboard and screen reader users despite being
functionally clickable. Render a native <button> instead to get proper
keyboard support and correct a11y semantics.

Co-authored-by: Claude <noreply@anthropic.com>
@lorgonal lorgonal force-pushed the fix/card-link-a11y-button-fallback branch from 0a08f14 to 1eb1b75 Compare February 23, 2026 10:03
@lorgonal lorgonal merged commit 69e634b into main Feb 23, 2026
19 checks passed
@lorgonal lorgonal deleted the fix/card-link-a11y-button-fallback branch February 23, 2026 13:53
@eliseo-juan eliseo-juan mentioned this pull request Feb 23, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

fix react Changes affect packages/react

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants