Don’t Turn a Table into an ARIA Grid Just for a Clickable Row

Again, title says it all. However, there is an equally bad opposite approach you might be tempted to use, so let me clarify:

Step 1

Use a checkbox for each row. It’s a clear visual cue most users understand. You can even put a checkbox above and/or below the table to toggle all rows as selected or not. The <label> element will do all the work for us.

If you are trying this technique with HTML buttons then the accessible name requires ARIA, but I outline how straightforward this is in my post Uniquely Labeling Fields in a Table.

Step 2

If you are insistent that the user can click anywhere on the row to check the checkbox, you can do that without JavaScript (and definitely without ARIA grid roles). I show how to do it in my post Block Links, Cards, Clickable Regions, Rows, Etc..

That post does not use a checkbox in each row, but instead uses links and buttons. The approach is still the same, except we rely on the <label> instead of ARIA.

Step 3

Peruse these to understand why and how I got to this place:

Example

I made an example. If the embed below is not working, visit it directly.

The important bits of the CSS:

th {
  position: relative;
}

th > label::after {
  content: "";
  display: block;
  position: absolute;
  inset: 0;  
}

tr:hover, tr:focus-within {
  outline: .5em solid rgba(0,0,255,.25);
}

th label:hover::after, th label:focus::after {
  background-color: rgba(255,255,0,.1);
}

/* Duplicated style until Firefox supports :has() */
td:has(input:focus) + th label::after {
  background-color: rgba(255,255,0,.1);
}

/* Width is here or else Safari honors nothing. */
th > label::after {
  width: 92vw;
}

Adjust your visual styles as appropriate.

Firefox will support :has() in 121, so you can delete that redundant style when your audience is current.

Without the width on the CSS generated content (label::after), Safari will not provide a box to allow the fake row click. I am unable to find any bugs related to this and none of the width keywords does the trick. So adjust that 92vw value as you see fit, potentially plugging it with one based on a known or calculated size (ugh, Safari).

Update: 3 November 2023

Michal Čaplygin dug up the bugs that informed some of what I am trying in this demo:

12 Comments

Reply

What if the main action is behind a more-menu button with additional actions for this row? Is it then okay to put a click handler on the row while keyboard/screen reader users still have an accessible way to trigger the action?

Sandro; . Permalink
In response to Sandro. Reply

So two interactive bits in one row? Still not ok to use a click handler on the row. Limit the width of the CSS generated box to ensure the other control is not covered. Ideally put that secondary control at the far end of the row.

In response to Sandro. Reply

Sandro, I just re-read your comment. You have a disclosure or menu trigger in a cell in your row. I would probably not make the entire row clickable in that context and I still would not put a click handler on the row.

In response to Adrian Roselli. Reply

OK, thanks

Sandro; . Permalink
Reply

Great article, however (and it’s a bit however and I do not lie), if you test the CodePen example with a screen reader the title column in the table is empty, except the first row (excluding the column header).
I haven’t looked at the code but there is something off there.

Birkir Gunnarsson; . Permalink
In response to Birkir Gunnarsson. Reply

Birkir, the embedded example is not CodePen, though that should not matter. I am not near a computer today (nor my Mac for a week or two). Can you tell me the screen reader and browser combo so I can queue it for testing later?

Reply

Jaws 2023 + Chrome (whatever the latest version is), no rush

Birkir Gunnarsson; . Permalink
In response to Birkir Gunnarsson. Reply

The JAWS/Chrome issue goes away when there is any other element in there (NVDA does not have this problem with Chrome, so I will have to do deeper debugging later to see if this is a JAWS bug or not). Because I am on the road, I made a quick CodePen to demonstrate a fix, where I add a <wbr> as the last node. It is a hack.

Reply

Cool! Yes, the example works now.

Another hack I tried out, and it works is to use aria-labelledby instead:

John Johnson
The checkbox reads “Select John Johnson” and the header cell reads as expected.

Birkir Gunnarsson; . Permalink
Reply

Sorry, HTML code does not come through, trying Markdown style comment
“`
id=”c11″ aria-label=”select”>
John Johnson
“`
If this doesn’t work, basically the idea is to use a theckbox input element with
id=”foo” aria-label=”select” and aria-labelledby=”foo hoo”
Then put id=”hoo” on the table cell element with the checkbox label.
If the text of that cell is “john” the checkbox screen reader label now reads “select John”

Birkir Gunnarsson; . Permalink
In response to Birkir Gunnarsson. Reply

Yeah, my comment form strips most HTML (it only allows the elements listed after the comment field) and it does not support markdown. Escaping HTML the brackets (&lt; & &gt;) is the best way to show HTML code snippets here.

That said, I appreciate you trying another method. Given my aversion to aria-label (primarily since it does not auto-translate but also because aria-label is wonky for header cells) and since adding it makes for a more verbose accName than I want, I will stick with the arbitrary non-rendering HTML element.

Leave a Comment or Response

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>