Skip to content

Make Button a correct polymorphic, accessible primitive (link vs button semantics) #55

Description

@chonilius

Problem

src/components/ui/Button.tsx is the shared button primitive used everywhere: primary claim/fund/deposit CTAs, wallet connect, GitHub OAuth redirect, tab/filter controls, and presumably comes in several visual variants given the design system's slate/emerald palette. As the single most reused interactive primitive in the codebase, any gap in it multiplies across every feature. Specifically worth scrutinizing given everything else in this batch: does Button.tsx correctly render a disabled and a loading/pending state as genuinely distinct (a disabled-because-invalid-input button versus a disabled-because-request-in-flight button convey different information and should look/announce differently to prevent a user assuming a stuck loading state is just "the button doesn't work")? Does it forward a ref and spread ARIA attributes correctly when used as a submit trigger inside the various claim/fund/deposit flows, or does it swallow/override props in a way that breaks accessibility attributes callers try to pass? Is it correctly implemented as a polymorphic component (rendering as an <a> for navigation-style CTAs like "Connect GitHub" which is fundamentally a redirect/link action, versus a <button> for in-page actions like "Claim bounty") — using a <button onClick={() => location.href = ...}> pattern for what is semantically navigation breaks middle-click-to-open-in-new-tab, right-click-to-copy-link, and screen reader link/button role expectations, all real usability regressions for something as foundational as "how do I start the OAuth flow."

Why this is hard

  • Requires reading Button.tsx's full implementation and then grepping every call site across src/app/** and src/components/** to classify each usage as "genuinely an in-page action" (correct as <button>) versus "actually navigation/redirect" (should be an <a>/Next.js Link under the hood) — this is a full-codebase audit against a real semantic-HTML standard, not a local fix, and the GitHub OAuth connect CTA specifically is a near-certain candidate for being wrongly implemented as a <button> with an imperative redirect given it's described as a "redirect to the backend's OAuth endpoint."
  • Loading vs. disabled needs a real accessibility treatment: aria-busy/aria-disabled (not native disabled, which removes the element from the accessibility tree and tab order, arguably wrong for a "this is processing, please wait" state where the user might want to understand why nothing's happening) versus native disabled (correct for "this action isn't currently valid") are different HTML/ARIA patterns with different keyboard/screen-reader implications, and the current component needs to be audited for which (if either) it actually implements correctly today.
  • Making Button.tsx genuinely polymorphic (as="a"/as={Link} support, or a discriminated prop union) while preserving every existing visual variant, size, and the loading/disabled states designed above is a non-trivial component API redesign with real blast radius across every call site in the app — this needs careful prop-typing (likely a generic/polymorphic TypeScript component pattern) to avoid a sprawling, unsafe any-typed escape hatch.
  • Must verify ref forwarding and full ARIA attribute pass-through work correctly for every variant after the redesign, since this is exactly the kind of regression that's invisible in casual visual QA but breaks real assistive-technology usage.

Scope

  • Audit Button.tsx's current implementation and every call site, classifying each as in-page-action vs. navigation.
  • Redesign Button.tsx to be genuinely polymorphic (correct <a>/Link rendering for navigation CTAs, correct <button> for in-page actions), preserving all current visual variants/sizes.
  • Implement a correct, distinct aria-busy-based loading state separate from native disabled, and audit/fix every call site using the wrong one today.
  • Fix the GitHub OAuth "Connect" CTA and any other misclassified navigation CTA to use proper link semantics instead of an imperative onClick redirect.
  • Verify ref forwarding and ARIA attribute pass-through across all variants.

Acceptance criteria

  • The GitHub OAuth connect CTA (and any other navigation-classified CTA found in the audit) is a real anchor/Link supporting middle-click-open-in-new-tab and right-click-copy-link, verified manually.
  • Loading and disabled states are implemented with correct, distinct ARIA semantics, verified with a screen reader.
  • All existing visual variants and sizes render unchanged after the polymorphic refactor.

Metadata

Metadata

Assignees

Labels

GrantFox OSSIssue tracked in GrantFox OSSMaybe RewardedIssue may be eligible for a GrantFox rewardOfficial Campaign | FWC26Campaign: Official Campaign | FWC26accessibilityAccessibility (a11y) issueenhancementNew feature or requestvery hardVery difficult task, expert-level effort required

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions