'Accessibility: how to handle buttons in a table

Given a table, which may have buttons within it, how should these buttons be marked up for accessibility?

<table>
  <tbody>
    <tr>
      <td>
        User 1
      </td>
      <td>
        Alice
      </td>
      <td>
        <button aria-label="Edit user Alice">Edit</button>
      </td>
    </tr>

    <tr>
      <td>
        User 2
      </td>
      <td>
        Bob
      </td>
      <td>
        <button aria-label="Edit user Bob">Edit</button>
      </td>
    </tr>

  </tbody>
</table>

For a sighted user it's clear what each edit button does, but I'm wondering how that works best for accessibility.

My intuition would say that the buttons ought to be given an aria-label that indicates what they really do, such as Edit user Alice, but is that correct?



Solution 1:[1]

That's exactly right. Nice job recognizing the need. The way the page is designed, there is an "affordance" that gives sighted users a clue as to the structural relationships between elements. That same relationship should be conveyed to all users.

One way you could do this is with column and row headers (<th scope="col"> and <th scope="row">). In your specific example, the row header would either be the "user 1" column or the "Alice" column. That would satisfy WCAG but is not necessarily the best user experience.

<table>
  <tbody>
    <tr>
      <th scope="col">user</th>
      <th scope="col">name</th>
      <th scope="col">edit profile</th>
    </tr>
    <tr>
      <td>User 1</td>
      <th scope="row">Alice</th>
      <td><button>Edit</button></td>
    </tr>
    <tr>
      <td>User 2</td>
      <th scope="row">Bob</th>
      <td><button>Edit</button></td>
    </tr>
  </tbody>
</table>

In this example, I chose the 2nd column to be the row header. The person's name. If a screen reader user were in the last column (the buttons) and they navigate vertically down that column (ctr+alt+downArrow for some screen readers), they'll hear "Alice" or "Bob" before they hear "edit button" so it gives them some context.

Note that some screen reader + browser combinations, the row header will not be announced for data cells "before" the row header column. So in this case, if I were in the first column (users) and I navigated down the column, I might not hear "Alice" or "Bob" for that row. NVDA with Firefox will not announce it but NVDA with Chrome will. JAWS on Chrome will not announce it.

While I strongly encourage row headers in tables (and column headers too but most people are good about specifying column headers so I often don't have to mention them), having additional context on the button itself would be the best solution (in addition to the table headers).

I would use aria-labelledby so that you can "reuse" the text that's already on the screen. There's no need to duplicate the text in an aria-label. All you need to do is specify an ID on the table cell you want to use as the additional context for the button and have an ID on the button itself. Then refer to those two IDs in aria-labelledby.

<table>
  <tbody>
    <tr>
      <th scope="col">user</th>
      <th scope="col">name</th>
      <th scope="col">edit profile</th>
    </tr>
    <tr>
      <td>User 1</td>
      <th scope="row" id="row1">Alice</th>
      <td><button id="button1" aria-labelledby="button1 row1">Edit</button></td>
    </tr>
    <tr>
      <td>User 2</td>
      <th scope="row" id="row2">Bob</th>
      <td><button id="button2" aria-labelledby="button2 row2">Edit</button></td>
    </tr>
  </tbody>
</table>

Now when the user navigates to the button, if they use tab instead of the table navigation keys, they'll hear "edit Alice button" or "edit Bob button".

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 slugolicious