( Perceivable / WCAG 1.3.1 )
Table header scope value is invalid
What is this issue?
A <th> element has a scope attribute set, but the value isn’t col, row, colgroup, or rowgroup, the only four values HTML defines for it. A common version of this mistake is scope="columns", written by someone reasonably guessing at the attribute’s syntax rather than checking the specification, since “columns” reads as a perfectly natural English word for what the header applies to.
The header cell still exists, its text is still visible, and it still gets read by a screen reader as some kind of header, but the specific instruction about which cells it applies to, column-wise or row-wise, is malformed and not reliably interpreted the way the developer intended.
Why does this matter?
scope exists to remove any ambiguity about which cells a header applies to, which matters most in tables where position alone isn’t a fully reliable signal. An invalid value defeats that purpose without failing loudly: the browser doesn’t reject the attribute or show any visible error, so the table looks and often behaves almost correctly, which makes this a particularly easy mistake to ship unnoticed.
Depending on the browser and screen reader combination, an invalid scope value gets silently ignored (falling back to positional inference, which may or may not guess correctly for that specific table) or treated inconsistently, meaning the same table can announce differently across different assistive technology setups. A user testing with one screen reader might see no problem at all, while a user on a different combination hears headers applied incorrectly or not at all.
Who is affected?
- Screen reader users: may hear a header applied to the wrong cells, or not applied at all, depending on how their specific screen reader and browser combination handles the invalid value.
- Cognitive disabilities: users relying on consistent, predictable header announcements to track their position in a table encounter unreliable behavior that varies by tool, making the table harder to trust even when it happens to work.
What users experience
Elena uses JAWS on Windows to review a product spec table where every header cell was written with scope="columns", a plausible but invalid guess made during development. JAWS doesn’t recognize the invalid value and falls back to inferring header relationships from cell position, which happens to work correctly for this particular table’s simple layout, so Elena hears accurate column names as she moves through it. A colleague testing the same table with a different screen reader and browser combination gets inconsistent results on a more irregular table elsewhere on the site, where positional inference alone isn’t enough. This exposes that the “columns” typo was never actually doing the job scope="col" was supposed to do, even on the table where it happened to look fine.
How do I fix it?
Correct every invalid scope value to one of the four values HTML actually defines: col for a header that applies to a column, row for a header that applies to a row, colgroup for a header spanning multiple columns, or rowgroup for one spanning multiple rows. This works because these four values are the only ones browsers and screen readers are built to recognize; anything else falls outside the specification and is handled unpredictably.
For the overwhelming majority of tables, you only need col and row. Reach for colgroup or rowgroup specifically when a single header cell, using colspan or rowspan, genuinely spans and labels more than one column or row at once. Using them for a plain single-column header adds unnecessary complexity without changing the outcome.
Code Examples
<table>
<tr><th scope="columns">Product</th><th scope="columns">Price</th></tr>
<tr><td>Widget</td><td>$12</td></tr>
</table><table>
<tr><th scope="col">Product</th><th scope="col">Price</th></tr>
<tr><td>Widget</td><td>$12</td></tr>
</table>The only change is scope="columns" to scope="col" on each header cell: the visible text, the table’s layout, and every data cell stayed identical. That correction is what makes the association reliably recognized across every browser and screen reader, instead of depending on positional guesswork that only works for simple tables.
Common Mistakes
Mistake: “Columns is a more descriptive, readable value than the abbreviated col, so it should still work.” HTML attribute values aren’t free text: scope accepts exactly four specific strings, and anything else, however readable or grammatically sensible, is outside the specification and not guaranteed to be recognized. Readability to a developer reading the source code has no bearing on whether a browser or screen reader recognizes the value.
Mistake: “The table looks and sounds fine when I test it, so the scope value must be valid.” An invalid scope value often gets silently ignored with a fallback to positional inference, which can produce a correct-sounding result purely by chance on a simple table; that doesn’t mean the value is valid, only that this particular table’s layout happened to be simple enough for the fallback to guess right. Test the actual attribute value in your markup, not just the observed behavior on one table.
Mistake: “I’ll use scope=“rows” to be consistent with using a plural for multiple header cells in that row.“ scope describes what a single header cell applies to, not how many header cells exist in the table: the correct singular values (col, row) apply the same way whether there’s one header cell or twenty. Pluralizing the value based on a mental model of “there are several of these” is a common but incorrect intuition; check the specification’s exact four values instead of guessing from context.
How RedFlag Detects This
Guidance only: RedFlag documents this issue but does not currently flag it; verify manually. Checking every <th>’s scope value against the four valid options is a straightforward pattern to detect automatically, but this rule id is not wired into RedFlag’s automated detection engine, custom detector set, or coverage model today; it exists purely as documentation for a check RedFlag doesn’t currently perform.
False negative: every occurrence is effectively a false negative in the sense that nothing is ever automatically flagged: a table full of scope="columns" typos passes every automated RedFlag scan silently. False positive: not applicable, since this check never raises an automated violation to begin with. Manual step: inspect the scope attribute on every <th> in the page’s markup directly (browser dev tools, or view source) and confirm each value is exactly col, row, colgroup, or rowgroup; visual or even screen-reader testing alone may not reveal an invalid value that happens to fall back correctly on a simple table.
Manual Testing
- Open the page’s markup in browser dev tools (Elements panel) or view source.
- Find every
<th>element and check itsscopeattribute value directly against the four valid options:col,row,colgroup,rowgroup. - Separately, open the page in Chrome or Firefox with NVDA or JAWS running on Windows and navigate into the table using table-navigation commands.
- Confirm the announced header for each cell matches what the table’s visual layout implies. An invalid value that happens to fall back correctly on a simple table can still sound right even though the markup is wrong, so the markup check in steps 1–2 is the more reliable signal.
- Pay particular attention to any table with merged headers (
colspan/rowspan), since positional fallback is far less reliable there and an invalidscopevalue is more likely to produce an audibly wrong result.
Related WCAG Success Criteria
1.3.1 Info and Relationships: Information, structure, and relationships conveyed through presentation must also be available programmatically. An invalid scope value is a direct failure of this criterion: the developer’s intent to associate a header with a specific column or row exists in the markup, but isn’t expressed in a form assistive technology can reliably interpret.
Related Issues
Table header has no associated data cells is the direct downstream consequence this rule most often causes: an invalid scope value frequently results in a header that resolves to zero associated data cells.
Table cell headers attribute references a missing id covers the equivalent broken-association problem for complex tables using headers/id instead of scope.
Table is missing header cells is the more basic failure underneath this whole family: no <th> elements at all, a step before scope correctness is even relevant.
Complex data table is missing a caption is another header-family issue worth checking on the same table, since tables complex enough to need precise scope values are usually complex enough to need a caption too.
References
- W3C Understanding 1.3.1: Info and Relationships
- MDN: The scope attribute
- WebAIM: Creating Accessible Tables
Frequently asked questions
What are the only valid values for the scope attribute?
The HTML specification defines exactly four valid values: col, row, colgroup, and rowgroup. Any other value, including plausible-looking ones like column or columns, is invalid and is either ignored or treated inconsistently across browsers and screen readers.
What happens if I leave scope off a th entirely instead of using an invalid value?
Leaving scope off entirely is different from using an invalid value, though both fail to give an explicit association. Without scope, most screen readers fall back to inferring the header relationship from position, which works for simple tables but is less reliable than stating it explicitly.
When should I use colgroup or rowgroup instead of col or row?
Use colgroup when a single th spans and labels several columns at once, and rowgroup for the same situation across several rows. Use plain col or row for the much more common case of a header that applies to exactly one column or one row.
Is scope case-sensitive, so "Col" would count as invalid?
HTML attribute values are generally treated case-insensitively by browsers, so "Col" would likely still be recognized as valid in practice. It is still best practice to use the lowercase form shown in the specification for consistency and clarity.
Does an invalid scope value break the table visually as well as for screen readers?
No. An invalid scope value has zero effect on layout or appearance, since scope is purely a semantic attribute with no visual styling attached to it. A table with a completely broken scope value can look perfectly normal while still failing this check.