HTML Guides for grid
Learn how to identify and fix common HTML validation errors flagged by the W3C Validator — so your pages are standards-compliant and render correctly across every browser. Also check our Accessibility Guides.
An element with role="row" must sit inside an element whose role is table, grid, treegrid, or rowgroup, or be claimed by one through aria-owns. A row on its own has no structure to belong to, so assistive technologies cannot tell which table or grid it is part of.
The row role describes one row of a composite structure. Those four allowed owners fall into two groups: table and rowgroup for static tabular data, and grid and treegrid for interactive widgets that support keyboard navigation. In every case the container supplies the context a screen reader needs to announce the row's position and its cells. Placed outside such a container, the row is orphaned and the whole table semantics break down.
The most reliable fix is to nest the role="row" element inside its container in the DOM. When your layout prevents that, keep the row where it is and point aria-owns from the container at the row's id.
Invalid example
The role="row" is a sibling of the table, so nothing contains or owns it:
<div role="table" aria-label="Prices"></div>
<div role="row">
<span role="cell">Basic</span>
<span role="cell">$9</span>
</div>
Valid example
Nest the row inside the element with role="table":
<div role="table" aria-label="Prices">
<div role="row">
<span role="cell">Basic</span>
<span role="cell">$9</span>
</div>
</div>
If the DOM nesting is not possible, leave the row in place and add aria-owns="price-row" to the container so assistive technologies still treat it as the owner.
The validator rejects justify-self values it doesn't recognize: usually a typo or a keyword from another alignment property, sometimes the valid but newer anchor-center.
justify-self aligns one box along the inline axis, which is horizontal in left-to-right and right-to-left text, and overrides the container's justify-items for that box. It works on grid items, inside their grid area, and on absolutely positioned boxes. Flexbox ignores it, because a flex container places its items along the main axis with justify-content and auto margins. The validator accepts auto, normal, stretch, center, start, end, self-start, self-end, flex-start, flex-end, left, right and the baseline keywords (baseline, first baseline, last baseline). A position can take a safe or unsafe prefix, as in safe center.
Wrong values often come from neighbouring properties. middle belongs to vertical-align; box alignment calls it center. top and bottom are vertical, and the vertical (block) axis belongs to align-self, which spells them start and end. space-between and space-around spread space across several items and belong to justify-content. legacy only exists on justify-items, so justify-self: legacy right fails while justify-self: right passes. Browsers drop a declaration with an unknown value, and the box falls back to whatever alignment justify-items gives it.
anchor-center is valid CSS that the validator hasn't caught up with. CSS Anchor Positioning adds it to justify-self and align-self so an absolutely positioned element can center itself on its anchor along that axis, for example a tooltip lined up with the middle of its button. The validator reports it even inside @supports (justify-self: anchor-center). When the box isn't absolutely positioned or has no default anchor, the spec says anchor-center behaves as center, so on a grid item you can write center and get the same layout.
If the element really is anchor positioned, keep anchor-center and mute this message for that value.
Invalid example
<div style="display: grid; grid-template-columns: repeat(3, 1fr);">
<span style="justify-self: middle;">Item</span>
<span style="justify-self: legacy right;">Qty</span>
<span style="justify-self: anchor-center;">Price</span>
</div>
Valid example
<div style="display: grid; grid-template-columns: repeat(3, 1fr);">
<span style="justify-self: center;">Item</span>
<span style="justify-self: right;">Qty</span>
<span style="justify-self: center;">Price</span>
</div>
A <tr> element already has an implicit ARIA role of row, so adding role="row" is redundant when the parent <table> uses its default semantics or has a role of table, grid, or treegrid.
HTML tables come with built-in accessibility semantics. The <table> element implicitly has role="table", and <tr> implicitly has role="row". Browsers and assistive technologies already understand this structure, so explicitly adding these roles is unnecessary and flagged by the W3C validator.
The only time you'd need to add a role to a <tr> is when the table's native semantics have been overridden — for example, if the <table> has been repurposed with a non-table role like role="presentation" or role="none". In that case, you'd need explicit ARIA roles to restore row semantics.
Incorrect Example
<table>
<tr role="row">
<th>Name</th>
<th>Email</th>
</tr>
<tr role="row">
<td>Alice</td>
<td>alice@example.com</td>
</tr>
</table>
Fixed Example
Simply remove the redundant role="row" from the <tr> elements:
<table>
<tr>
<th>Name</th>
<th>Email</th>
</tr>
<tr>
<td>Alice</td>
<td>alice@example.com</td>
</tr>
</table>
The same fix applies if your <table> explicitly has role="table", role="grid", or role="treegrid" — the <tr> elements still don't need an explicit role="row" because the browser infers it automatically.
Validate at scale.
Ship accessible websites, faster.
Automated HTML & accessibility validation for large sites. Check thousands of pages against WCAG guidelines and W3C standards in minutes, not days.
1K Weekly
1,000 monthly checks. Cancel anytime.
Get 1K Weekly →Join teams across 40+ countries