Uniquely Labeling Fields in a Table

Many of my clients over the years have relied on fields in tables. Sometimes a checkbox to select a row, sometimes text inputs to update information, sometimes buttons select something. Rarely are they interested in a block of label text above the field, and I cannot disagree with them.

The challenge here is to create a unique name for each field without repeating text all over your screen. Experience has taught me that when clients try to visually hide <label> text, it can fall out of sync with the column or row headers, making it even more confusing.

The good news here is that if you are creating tables correctly, there isn’t much extra work you need to do. Your column and row headers, paired with some ARIA, can do the job for you. I made a demo to show it in action, embedded below or directly at Codepen.

See the Pen Demo: Uniquely Labeling Fields in Table by Adrian Roselli (@aardrian) on CodePen.

There are two sets of thing happening here:

  1. Each column needs a <th> and each row needs a <th scope="row">, each with a unique id attribute;
  2. Each field needs an aria-labelledby pointing to the ids of the column and row header (separated by a space).

The order of the id values in the aria-labelledby matters. Note that the aria-labelledby code for the <button> flips the order of the ids referenced by aria-labelledby in order to provide a more natural announcement to screen reader users (“Remove 1” versus “1 Remove”). The <button> references its own id attribute instead of the column header, though this is a decision you should make based on if the button text is different from the column header or likely to change based on errors or user input.

The fields are verbose for screen reader users who use table navigation, but that is a trade-off for users who tab through the fields. While this approach can make it a pain for voice users to select fields, hidden labels will always cause that challenge. An explanation of the naming convention can mitigate that.

Note that all the fields on this form are nonsense, the table is not responsive, and errors are not shown. These are strictly accessible, but maybe using row headers of strictly numbers is not the most usable option depending on your table. In no way would this example warrant an ARIA grid role.

Captured using Firefox 67.0b16 and NVDA 2018.3.2. The first row I navigate with table commands, and then I switch to Tab. As you can see, maybe row headers of just a number can get confusing.

Update: 22 June 2019

John made a great point in the comments. Maybe some of the labels would make more sense in my example if they pointed at the author name instead of an assigned number. After all, the row number announced with the assigned number feels like a permanent off-by-one error and may have muddied the point I was trying to make.

So I removed the first column, made the row header the second column, and used that as the assigned name. Note that in some browser and screen reader combinations, row headers that are not the first column are not announced for any cells in columns prior to the row header.

See the Pen Demo: Uniquely Labeling Fields in Table v2 by Adrian Roselli (@aardrian) on CodePen.

I also made a video.

Captured using Safari and VoiceOver on macOS 10.14.3. I alternate between navigating with table commands and Tab.



I looked at this quickly because I had to get back to work but wouldn’t it be better connect the items to the author’s name instead of the row number, that way it would read “Remove Mary Shelley” instead of “Remove 2”?

In response to John F Croston III. Reply

Yes, John, yes it would.

Granted, each table would be different and, as you have pointed out, not every bit of labeling is best leaning on a row header when there is another cell that may make more sense in that context for those users.

For those following along, to make that work the <td> will also need a unique id, which will then be referenced in the aria-labelledby.


The fields are verbose for screen reader users who use table navigation, but that is a trade-off for users who tab through the fields.

Noting that as of NVDA 2023.3.3 + Chrome 122, tabbing is also quite verbose. From the first example, the following is announced when the first input is tabbed to:

“1 row 2 Claimed by column 7 1 Claimed by edit blank”

In response to Weston Thayer. Reply

Yeah, I am working on a proposal that might make accNames for fields in tables come from column and row headers (in the absence of another method already in place). If I can come up with a viable model, and then browser support, hopefully stuff like you cite could be mooted.

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>