solid-router 2.0.0-rc.0: `<Link>` drops a caller’s `role` on enabled links (react-router keeps it)
Nobody has claimed this yet.
- Dominant language
- TypeScript
- Stars
- 15.1k
- Forks
- 1.9k
- Avg merge
- 1d 20h
- Merged PRs (30d)
- 143
Description
Which project does this relate to?
Router
Describe the bug
role passed to <Link> never reaches the DOM unless the link is disabled. The React adapter keeps it, so the same markup behaves differently on the two adapters:
| element | solid-router 2.0.0-rc.0 | react-router 1.170.27 |
|---|---|---|
<Link to="/" role="button"> |
no role attribute |
role="button" |
<Link to="/" role="button" disabled> |
role="link" |
role="link" |
<a href="/" role="button"> |
role="button" |
role="button" |
Cause: useLinkProps copies the caller's props onto linkProps, then redefines role unconditionally, so the getter wins on enabled links —
role: () => local.disabled ? "link" : void 0,
The React adapter merges its { role: 'link', 'aria-disabled': true } only when the link IS disabled (...disabled && STATIC_DISABLED_PROPS), leaving a caller's role alone.
Complete minimal reproducer
https://github.com/TylerRick/tanstack-solid-link-role-repro (the react-control/ folder is the same markup on react-router, for comparison)
Steps to Reproduce the Bug or Issue
pnpm installpnpm dev- Open http://localhost:5598 and read the
roleattribute off#enabled,#disabledand#plain—#enabledhas none. - For the comparison:
cd react-control && pnpm install && pnpm dev, then read the same three (#enabledisrole="button"there).
Expected behavior
role behaves like any other pass-through attribute: the adapter's own value applies when the link is disabled, and a caller's value is respected otherwise. A fallback rather than an override would do it —
role: () => (local.disabled ? 'link' : propsSafeToSpread.role),
— or define the getter only when local.disabled is set. Happy to PR whichever you prefer.
Screenshots or Videos
No response
Platform
- Router / Start Version:
@tanstack/solid-router2.0.0-rc.0,@tanstack/router-core1.171.16 (control:@tanstack/react-router1.170.27) - solid-js /
@solidjs/web: 2.0.0-rc.0 - OS: Linux
- Browser: Chromium 141
- Bundler: vite 8.2.1
Additional context
role is an ARIA attribute, so dropping it silently changes what assistive technology announces for
the element — an author who writes role="button" on a <Link> gets a link announced as a link, with
no error and nothing in the DOM to explain why. Ours was a tab strip, where the roles are what make
the widget legible to screen readers; the only way to keep them was to stop using <Link> there and
hand-roll the click interception, which gives up its preloading and active-state handling.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start at the Solid router's useLinkProps entry point, where caller props are copied into linkProps and role is redefined. Compare the enabled and disabled cases with the minimal reproducer and its react-control comparison. Done means enabled links preserve a caller's role, while disabled links retain the adapter's role="link" behavior.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- typescript
- Domain
- accessibility, frontend, web-dev
- Issue type
- Bug
- Difficulty
- 2/5
- Estimated time
- 1-3 hours
- Activity status
- Quiet
- Clarity
- Clearly specified
- Newbie friendliness
- 76/100