Skip to content

Give every list row a real link in its first cell - #6

Merged
sylvesterdamgaard merged 1 commit into
mainfrom
fix/a11y-row-anchors
Jul 27, 2026
Merged

Give every list row a real link in its first cell#6
sylvesterdamgaard merged 1 commit into
mainfrom
fix/a11y-row-anchors

Conversation

@sylvesterdamgaard

Copy link
Copy Markdown
Contributor

Completes the row work started in #5.

Removing role="link" restored table semantics and made the cells readable, but left rows as focusable <tr>s with no interactive role — readable and operable, yet with their purpose unannounced.

Each row's first cell now wraps its content in a genuine <a> pointing at the row's own destination. The anchor carries the accessible name and the keyboard focus, so rows drop tabindex and aria-label entirely.

Mouse behaviour is untouched: data-href and the single delegated listener stay, and that listener already ignores clicks originating inside an anchor.

How it was done

29 of 30 rows transformed with a quote-aware tag scanner, not a regex. Blade expressions contain ->, and a naive [^>]* truncates the tag at the first > — which is exactly why the earlier pass silently missed 7 rows. The 30th already had a proper anchor and only needed its redundant row-level attributes removed.

Verified beyond the suite

  • No nested anchors anywhere — invalid HTML, and it would break both click and AT behaviour.
  • Every anchor's href matches its own row's data-href — checked by pairing them in the source rather than trusting the transform.

The row contract test, third revision

It has now required role="link", then tabindex="0", and each requirement outlived its correctness. The comment says so. It now asserts the mechanism that is actually right.

Gate

pint · PHPStan level max (0 errors) · 1032 tests — green.

Completes the row work. Removing `role="link"` restored the table semantics and made
the cells readable, but left the rows as focusable <tr>s with no interactive role —
readable, operable, yet with their PURPOSE unannounced.

Each row's first cell now wraps its content in a genuine <a> pointing at the row's
own destination. That anchor carries the accessible name and the keyboard focus, so
the rows drop `tabindex` and `aria-label` entirely: a screen reader announces "…,
link", the remaining cells stay individually readable, and Tab reaches one focusable
element per row instead of a role-less one.

Mouse behaviour is untouched — `data-href` and the single delegated listener stay,
and that listener already ignores clicks originating inside an anchor, so a click on
the first cell simply follows the link to the same place.

29 of 30 rows transformed mechanically with a quote-aware tag scanner rather than a
regex: Blade expressions contain `->`, and a naive `[^>]*` truncates the tag at the
first `>` — which is exactly why the earlier pass silently missed 7 rows. The 30th
already had a proper anchor in its first cell and only needed its now-redundant
row-level tabindex/aria-label removed.

Verified beyond the suite: no nested anchors anywhere (invalid, and it would break
both click and AT behaviour), and every anchor's href matches its own row's
data-href — checked by pairing them in the source, not by assuming the transform.

Also updates the row contract test AGAIN, and the comment says so plainly: it has now
required `role="link"`, then `tabindex="0"`, and each requirement outlived its
correctness. It now asserts the mechanism that is actually right — data-href for
mouse, a real anchor for everything else.
@sylvesterdamgaard
sylvesterdamgaard merged commit 2a81c75 into main Jul 27, 2026
2 checks passed
@sylvesterdamgaard
sylvesterdamgaard deleted the fix/a11y-row-anchors branch July 27, 2026 12:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant