Skip to content

[19635] fix: Add scope="col" to th elements for td-has-header a11y compliance - #19636

Open
fkwq34 wants to merge 4 commits into
primefaces:masterfrom
fkwq34:tables-accessibility-scopes
Open

[19635] fix: Add scope="col" to th elements for td-has-header a11y compliance#19636
fkwq34 wants to merge 4 commits into
primefaces:masterfrom
fkwq34:tables-accessibility-scopes

Conversation

@fkwq34

@fkwq34 fkwq34 commented Jun 23, 2026

Copy link
Copy Markdown

fixes: #19635

Description
Adds scope="col" attribute to elements in Table and TreeTable components to fix the td-has-header accessibility violation.

Changes

  • Added updateThScope() method that sets scope="col" on all elements
  • Method is called on view init, when data loads, and when columns change
  • Ensures dynamically loaded content and toggled columns maintain accessibility compliance

Why
Without the scope attribute, screen reader users cannot understand data context when navigating table cells. The scope="col" attribute creates a programmatic relationship between data cells and their column headers.

Testing

  1. Run axe DevTools or Lighthouse accessibility audit on Table/TreeTable demos
  2. Verify td-has-header rule passes
  3. Inspect elements and confirm scope="col" is present

References

  • axe rule: td-has-header
  • WCAG: 1.3.1 Info and Relationships (Level A)
@rosenthalj

rosenthalj commented Jun 24, 2026

Copy link
Copy Markdown
Contributor

I only quickly reviewed the changes you made to table.ts. Wouldn't it be more efficient to be proactive vs reactive by adding the missing attribute when the "th" is created? How does querying/setTimeout effect performance when a table has a large number of columns or a large number of rows?

What would happen if a table (HTML and/or PrimeNG) was implemented within a PrimeNG table? PrimeNG (and your change) should not be modifying any embedded non-PrimeNG tables.

@fkwq34

fkwq34 commented Jun 24, 2026

Copy link
Copy Markdown
Author

@rosenthalj Good points. I have reworked the approach:

  • Switched to a MutationObserver on the instead of running querySelectorAll on every change — it now only fires when th elements are added
  • Scoped the selector to :scope > tr > th so we don't accidentally change any nested tables embed

Let me know if you see any other issues.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

2 participants