Reproducer: https://github.com/TylerRick/tanstack-solid-link-role-repro (pnpm install && pnpm dev; react-control/ is the same markup on react-router)
A role passed to <Link> never reaches the DOM unless the link is disabled:
| 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" |
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), which is why the same markup keeps role="button" there.
A fix could be to fall back instead of override — role: () => (local.disabled ? 'link' : propsSafeToSpread.role) — or to define the getter only when local.disabled is set. Happy to PR whichever you prefer.
Impact for us: an accessible tab strip built on <Link role="button"> loses the role, so getByRole('button') no longer finds the tabs.
Versions: @tanstack/solid-router 2.0.0-rc.0, @tanstack/router-core 1.171.16, solid-js / @solidjs/web 2.0.0-rc.0, vite 8.2.1.
Reproducer: https://github.com/TylerRick/tanstack-solid-link-role-repro (
pnpm install && pnpm dev;react-control/is the same markup on react-router)A
rolepassed to<Link>never reaches the DOM unless the link is disabled:<Link to="/" role="button">roleattributerole="button"<Link to="/" role="button" disabled>role="link"role="link"<a href="/" role="button">role="button"role="button"useLinkPropscopies the caller's props ontolinkProps, then redefinesroleunconditionally, so the getter wins on enabled links:The React adapter merges its
{ role: 'link', 'aria-disabled': true }only when the link IS disabled (...disabled && STATIC_DISABLED_PROPS), which is why the same markup keepsrole="button"there.A fix could be to fall back instead of override —
role: () => (local.disabled ? 'link' : propsSafeToSpread.role)— or to define the getter only whenlocal.disabledis set. Happy to PR whichever you prefer.Impact for us: an accessible tab strip built on
<Link role="button">loses the role, sogetByRole('button')no longer finds the tabs.Versions:
@tanstack/solid-router2.0.0-rc.0,@tanstack/router-core1.171.16, solid-js /@solidjs/web2.0.0-rc.0, vite 8.2.1.