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 install
pnpm dev
- Open http://localhost:5598 and read the
role attribute off #enabled, #disabled and #plain — #enabled has none.
- For the comparison:
cd react-control && pnpm install && pnpm dev, then read the same three (#enabled is role="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-router 2.0.0-rc.0, @tanstack/router-core 1.171.16 (control: @tanstack/react-router 1.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.
Which project does this relate to?
Router
Describe the bug
rolepassed 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:<Link to="/" role="button">roleattributerole="button"<Link to="/" role="button" disabled>role="link"role="link"<a href="/" role="button">role="button"role="button"Cause:
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), leaving a caller'srolealone.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 devroleattribute off#enabled,#disabledand#plain—#enabledhas none.cd react-control && pnpm install && pnpm dev, then read the same three (#enabledisrole="button"there).Expected behavior
rolebehaves 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 —— or define the getter only when
local.disabledis set. Happy to PR whichever you prefer.Screenshots or Videos
No response
Platform
@tanstack/solid-router2.0.0-rc.0,@tanstack/router-core1.171.16 (control:@tanstack/react-router1.170.27)@solidjs/web: 2.0.0-rc.0Additional context
roleis an ARIA attribute, so dropping it silently changes what assistive technology announces forthe element — an author who writes
role="button"on a<Link>gets a link announced as a link, withno 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 andhand-roll the click interception, which gives up its preloading and active-state handling.