React accessibility linting across components
React components can look valid in isolation while producing an invalid heading outline, duplicate landmark, or repeated id after composition. Enable ZemDomu's cross-component analysis to check the structure assembled through imports instead of stopping at one JSX or TSX file.
Recognize composition pitfalls
A reusable component can be internally tidy and still break its caller. Hard-coded heading levels depend on what precedes the component, a component that owns main can create a second main landmark when nested in a page shell, and a fixed id can be duplicated when the component is rendered more than once.
Keep page-level landmarks in layout components, make repeated ids instance-specific, and choose headings from the composed page outline rather than a card's typography. CSS should control visual size; the heading element should communicate structure.
// ReportCard.tsx: this h3 assumes a heading that the caller may not render.
export function ReportCard() {
return (
<section>
<h3>Weekly report</h3>
<p>42 checks passed.</p>
</section>
);
}
// ReportsPage.tsx: the composed outline jumps from h1 to h3.
import { ReportCard } from "./ReportCard";
export function ReportsPage() {
return (
<main>
<h1>Reports</h1>
<ReportCard />
</main>
);
}Fix the heading across the import boundary
The caller establishes h1 as the page heading, so the imported section should begin with h2. This is easy to miss when ReportCard.tsx is reviewed alone because the preceding heading lives in ReportsPage.tsx.
If the component must work at several outline depths, prefer an API that makes the semantic heading explicit at each call site. Do not derive a heading level only from how large the text should look.
// ReportCard.tsx
export function ReportCard() {
return (
<section>
<h2>Weekly report</h2>
<p>42 checks passed.</p>
</section>
);
}
// ReportsPage.tsx now composes h1 -> h2.
import { ReportCard } from "./ReportCard";
export function ReportsPage() {
return (
<main>
<h1>Reports</h1>
<ReportCard />
</main>
);
}Enable the exact VS Code settings
Place these values in the workspace's .vscode/settings.json when the team wants checks on save and import-aware analysis. The rule and severity lines make the heading policy explicit; other ZemDomu rules keep their configured defaults.
After saving the settings, open the Command Palette and run ZemDomu: Scan Workspace for Semantic Accessibility Issues. Use the Problems panel to review the parent and imported component together.
{
"zemdomu.run": "onSave",
"zemdomu.crossComponentAnalysis": true,
"zemdomu.rules.enforceHeadingOrder": true,
"zemdomu.severity.enforceHeadingOrder": "warning"
}Run the same cross-component check from the CLI
Install ZemDomu in the project, then scan the React source with cross-component analysis enabled. A depth of 3 follows the page-to-section-to-child import chain; increase it deliberately if the project's component tree is deeper.
Save the same command as a package script so local development and CI use one checked-in entry point, for example "lint:a11y": "zemdomu \"src/**/*.{jsx,tsx}\" --cross --cross-depth 3".
- Run npm install --save-dev zemdomu once in the project.
- Run npx zemdomu "src/**/*.{jsx,tsx}" --cross --cross-depth 3 from the repository root.
- Correct each reported file and rule, then rerun the command until it exits without findings.
npm install --save-dev zemdomu
npx zemdomu "src/**/*.{jsx,tsx}" --cross --cross-depth 3Know what source analysis cannot prove
Static import analysis is strongest when component targets and markup are visible in source. Runtime-selected components, generated markup, portals, conditional states, and data-dependent ids may require inspection of the rendered DOM even when the source scan is clear.
Follow linting with keyboard, browser accessibility-tree, contrast, focus, and screen-reader testing. Those checks cover rendered behavior that a JSX or TSX source graph cannot prove.