Introduction to React
Learn the basics of React...
Components
Building blocks of React apps...
State Management
Managing data in your app...
Test heading structure
Information, structure, and relationships conveyed through presentation must be programmatically determinable. Headings must reflect the visual hierarchy.
Heading hierarchy example
Learn the basics of React...
Building blocks of React apps...
Managing data in your app...
Master CSS Grid layout...
JavaScript, CSS, React, Node.js
Screen reader navigation
Screen reader navigation:
(no h1 - div used instead!) ├── h4: Latest Articles ⚠️ skipped h2,h3 │ ├── h2: Introduction to React ⚠️ wrong order │ │ ├── h6: Components ⚠️ skipped h3,h4,h5 │ │ └── h6: State Management │ └── h5: CSS Grid Tutorial ⚠️ inconsistent └── (no heading - span used!)
Incorrect vs correct markup
<!-- Using div for page title --> <div class="title">Company Blog</div> <!-- Skipping from nothing to h4 --> <h4>Latest Articles</h4> <!-- Wrong level inside section --> <article> <h2>Article Title</h2> <!-- Should be h3 --> <!-- Random h6 usage --> <h6>Subsection</h6> <!-- Should be h4 --> </article> <!-- Using styled span --> <span class="heading">Categories</span>
<!-- Single h1 for page title --> <h1>Company Blog</h1> <!-- Sequential heading levels --> <h2>Latest Articles</h2> <article> <h3>Article Title</h3> <!-- Proper nesting --> <h4>Subsection</h4> </article> <!-- Use headings for structure --> <h2>Categories</h2> <!-- Style with CSS, not tag choice --> <h2 class="text-xl">Small Heading</h2> <h3 class="text-2xl">Large Subheading</h3>
Testing hints
import AxeBuilder from '@axe-core/playwright';
// Check heading order
const results = await new AxeBuilder({ page })
.withRules([
'heading-order', // Sequential levels
'page-has-heading-one', // Has h1
'empty-heading' // No empty headings
])
.analyze();
// Get document outline using $$eval
const headings = await page.$$eval(
'h1, h2, h3, h4, h5, h6',
els => els.map(el => ({
level: parseInt(el.tagName[1]),
text: el.textContent?.trim()
}))
);
// Verify no skipped levels
let prevLevel = 0;
for (const h of headings) {
if (h.level > prevLevel + 1) {
console.log('Skipped: h' + prevLevel + ' to h' + h.level);
}
prevLevel = h.level;
}// Browser extension for manual testing:
// "HeadingsMap" shows document outline
// Programmatic outline generation:
const getOutline = async (page) => {
return page.$$eval(
'h1, h2, h3, h4, h5, h6',
els => els.map(h => ({
tag: h.tagName,
text: h.textContent?.trim(),
// Check if heading is visible
visible: h.offsetParent !== null
}))
);
};
// Verify single h1
const h1Count = await page.locator('h1').count();
expect(h1Count).toBe(1);Automation hints
heading-order, page-has-heading-onepage.locator('h1, h2, h3, h4, h5, h6')expect(page.locator('h1')).toHaveCount(1)