How to find cross-component accessibility issues

A component may be locally valid but create a semantic problem when its markup is combined with a parent or sibling. This reproducible example uses a repeated id: each TSX file contains one unique id, but the composed page contains two copies of page-title.

Create two files that pass an isolated id check

Create Page.tsx and SummaryPanel.tsx in src. A single-file uniqueIds check sees page-title only once in each file. When Page imports and renders SummaryPanel, the browser receives two elements with the same id and aria-labelledby can resolve ambiguously.

// src/Page.tsx
import { SummaryPanel } from "./SummaryPanel";

export function Page() {
  return (
    <main aria-labelledby="page-title">
      <h1 id="page-title">Dashboard</h1>
      <SummaryPanel />
    </main>
  );
}

// src/SummaryPanel.tsx
export function SummaryPanel() {
  return (
    <section aria-labelledby="page-title">
      <h2 id="page-title">Weekly summary</h2>
    </section>
  );
}

Correct the composed id relationship

Give the panel heading its own id and update the section's aria-labelledby reference. The final component tree now contains two distinct relationships, while the h1 to h2 outline remains sequential.

// src/SummaryPanel.tsx
export function SummaryPanel() {
  return (
    <section aria-labelledby="summary-title">
      <h2 id="summary-title">Weekly summary</h2>
    </section>
  );
}

Reproduce the check in VS Code

Enable cross-component analysis at workspace scope so the extension follows the import from Page.tsx to SummaryPanel.tsx. Saving one file may show local feedback; the workspace scan is the deliberate way to check the composed project graph.

  1. Add "zemdomu.crossComponentAnalysis": true to .vscode/settings.json.
  2. Open the repository in VS Code and run ZemDomu: Scan Workspace for Semantic Accessibility Issues.
  3. Confirm that the duplicate page-title is reported under uniqueIds, apply the corrected id, and rerun the scan.
{
  "zemdomu.run": "onSave",
  "zemdomu.crossComponentAnalysis": true,
  "zemdomu.rules.uniqueIds": true
}

Reproduce the same check from the CLI

Run the exact source pattern with cross-component analysis enabled. Checking either file without --cross cannot establish that both copies of page-title appear in the same composed tree.

For a shared command, add "lint:a11y": "zemdomu \"src/**/*.tsx\" --cross --cross-depth 3" to package.json and run npm run lint:a11y before committing.

npm install --save-dev zemdomu
npx zemdomu "src/**/*.tsx" --cross --cross-depth 3

Run the identical package script in CI

This complete workflow installs the locked project dependencies and calls the same lint:a11y script used locally. Commit the package script and package-lock.json before pushing the workflow.

name: Cross-component accessibility

on:
  pull_request:

permissions:
  contents: read

jobs:
  zemdomu:
    runs-on: ubuntu-latest
    timeout-minutes: 10
    steps:
      - name: Check out repository
        uses: actions/checkout@v4
      - name: Set up Node.js
        uses: actions/setup-node@v4
        with:
          node-version: 22
          cache: npm
      - name: Install dependencies
        run: npm ci
      - name: Check composed TSX structure
        run: npm run lint:a11y

Interpret a clean result carefully

Cross-component analysis follows source imports to build more context, but runtime-selected components, generated ids, portals, conditional branches, and server or client rendering differences can change the final DOM. A clear source scan does not prove that every runtime composition is valid.

Inspect the rendered DOM for duplicate ids and accessible-name relationships, then test focus and interaction behavior separately.

Related ZemDomu rules

Related evidence