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.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

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