npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

react-heading-manager

v0.3.0

Published

Accessible automatic heading hierarchy and ARIA region landmark manager for React

Readme

react-heading-manager

License: MIT npm version Build Status Test Coverage

Automatic heading hierarchy management and accessibility auditing for React applications.

react-heading-manager derives heading levels from your component and landmark structure, so you don't have to manually drill h1 through h6 through deeply nested component trees. It also provides a framework-agnostic audit engine and a Playwright matcher for validating heading hierarchy in tests.

Build your UI. Let the document structure determine the heading level. Verify the result automatically.


⚡ Quick Start

Get automatic heading hierarchy management running in a few lines.

1. Install

npm install react-heading-manager

2. Replace manually managed heading levels

Instead of manually deciding whether a component should render an <h1>, <h2>, <h3>, etc.:

import {
  Heading,
  HeadingFragment,
  Main,
  Section,
  Article,
} from "react-heading-manager";

export function Page() {
  return (
    <Main>
      <Heading>Page Title</Heading>

      <Section>
        <Heading>Features</Heading>

        <Article>
          <Heading>Release Notes</Heading>
        </Article>
      </Section>

      <HeadingFragment>
        <Heading>Related Content</Heading>
      </HeadingFragment>
    </Main>
  );
}

The heading level is derived from the surrounding document structure:

<main>
  <h1>Page Title</h1>

  <section>
    <h2>Features</h2>

    <article>
      <h3>Release Notes</h3>
    </article>
  </section>

  <h4>Related Content</h4>
</main>

No level prop. No h1/h2 prop drilling. No shared heading-level state between components.

3. Verify the hierarchy in Playwright

npm install -D @playwright/test
import { expect, test } from "@playwright/test";
import { registerPlaywright } from "react-heading-manager/testing/playwright";

registerPlaywright(expect);

test("page heading hierarchy is valid", async ({ page }) => {
  await page.goto("/");

  await expect(page).toHaveValidHeadingHierarchy();
});

The matcher reports structural violations with enough context to identify the offending region and heading.

4. Or audit the DOM directly

The core audit engine can be used independently of React and Playwright:

import {
  drawRegion,
  checkNormalizedHeadingReport,
} from "react-heading-manager/utils";

const regionTree = drawRegion(document.body);

const report = checkNormalizedHeadingReport({
  region: regionTree,
});

if (!report.isValid) {
  console.warn(report.errors);
}

This makes the audit engine useful for unit tests, custom tooling, accessibility audits, and other DOM-based workflows.

That's it. Use Heading components to manage hierarchy, then use the audit utilities or Playwright matcher to verify the resulting document structure.


Why react-heading-manager?

Heading levels are often difficult to manage in component-based applications.

A component may be rendered:

  • directly inside a page
  • inside several nested sections
  • inside reusable components
  • inside a dynamically composed layout
  • in a different location depending on application state

Manually passing heading levels through those component boundaries quickly becomes difficult to maintain:

<Heading level={2}>...</Heading>

With react-heading-manager, the heading level is derived from the document structure instead:

<Section>
  <Heading>Section title</Heading>

  <Article>
    <Heading>Article title</Heading>
  </Article>
</Section>

The same components can therefore be composed without manually coordinating heading levels across the component tree.


Key Features

  • Automatic heading level tracking without manual h1–h6 prop drilling
  • Semantic landmark awareness for main, section, article, and ARIA landmark regions
  • Framework-agnostic audit engine for DOM parsing and hierarchy analysis
  • Normalized heading analysis using deterministic numeric heading levels
  • Legacy-compatible heading analysis for existing RegionMapping data
  • Playwright integration for explicit E2E accessibility assertions
  • Detailed diagnostics including heading paths, levels, text, and DOM references
  • TypeScript support with typed public APIs and Playwright matcher augmentation
  • Zero-side-effect initialization
  • Tree-shakable module structure

Installation

npm

npm install react-heading-manager

yarn

yarn add react-heading-manager

pnpm

pnpm add react-heading-manager

bun

bun add react-heading-manager

Test utilities

If you use the testing integrations:

npm install -D @playwright/test happy-dom

Or:

pnpm add -D @playwright/test happy-dom

Usage

React Application

The React adapter provides the components and hooks used to build heading-aware document structures.

import {
  Heading,
  HeadingFragment,
  Main,
  Section,
  Article,
} from "react-heading-manager";

export function ProductPage() {
  return (
    <Main>
      <Heading>Products</Heading>

      <Section>
        <Heading>Featured Products</Heading>

        <Article>
          <Heading>Product Details</Heading>
        </Article>
      </Section>

      <HeadingFragment>
        <Heading>Related Products</Heading>
      </HeadingFragment>
    </Main>
  );
}

The resulting structure follows the surrounding heading hierarchy:

<main>
  <h1>Products</h1>

  <section>
    <h2>Featured Products</h2>

    <article>
      <h3>Product Details</h3>
    </article>
  </section>

  <h4>Related Products</h4>
</main>

When h6Clamp is disabled (default behavior), normalized heading levels can continue beyond the native HTML H1–H6 range. For example, a normalized H7 is represented as:

<h6 role="heading" aria-level="7">Deep Heading</h6>

Set h6Clamp to true when heading levels should remain within the native H1–H6 range:

<Main h6Clamp>
  <Heading>Page Title</Heading>
  {/* Heading levels are capped at H6 */}
</Main>

The h6Clamp setting is inherited by nested regions and can be overridden where needed.

Playwright E2E Testing

Register the matcher once in your Playwright setup:

import { expect } from "@playwright/test";
import { registerPlaywright } from "react-heading-manager/testing/playwright";

registerPlaywright(expect);

Then use the matcher in your tests:

import { expect, test } from "@playwright/test";

test("heading hierarchy is valid", async ({ page }) => {
  await page.goto("/");

  await expect(page).toHaveValidHeadingHierarchy();
});

You can also scope the check to a specific landmark:

test("main content has a valid heading hierarchy", async ({ page }) => {
  await page.goto("/");

  await expect(page.locator("main")).toHaveValidHeadingHierarchy();
});

Standalone Core Utilities

The audit engine does not depend on React.

import {
  drawRegion,
  checkNormalizedHeadingReport,
} from "react-heading-manager/utils";

const regionTree = drawRegion(document.body);

const report = checkNormalizedHeadingReport({
  region: regionTree,
});

if (!report.isValid) {
  console.warn(report.errors);
}

The audit pipeline is intentionally split into two stages:

DOM
 │
 ▼
drawRegion()
 │
 ▼
RegionMapping
 │
 ▼
checkNormalizedHeadingReport()
 │
 ▼
HeadingOrderReport

This allows DOM extraction and hierarchy validation to be tested and used independently.


Diagnostics

When a heading hierarchy violation is detected, the audit engine produces structured diagnostic information.

For example:

Heading level skipped at main[0] > section[0]:
context level is H1, expected maximum H2, but found H3.

A HeadingOrderError contains information such as:

  • region path
  • region tag name
  • heading level
  • normalized numeric level
  • heading text
  • DOM element reference
  • expected maximum level
  • actual level
  • human-readable error message

This allows integrations to provide useful diagnostics rather than simply returning true or false.

For example:

const report = checkNormalizedHeadingReport({
  region: regionTree,
});

for (const error of report.errors) {
  console.error({
    path: error.path,
    heading: error.heading,
    actualLevel: error.actualLevel,
    expectedMaxLevel: error.expectedMaxLevel,
    text: error.text,
  });
}

Heading Hierarchy Model

The normalized audit engine evaluates heading levels numerically.

A heading may:

  • remain at the same level
  • decrease to a lower level
  • increase by exactly one level

An increase of more than one level is reported as a hierarchy violation.

For example:

H1 → H2   ✓
H2 → H3   ✓
H3 → H3   ✓
H3 → H2   ✓
H2 → H4   ✗

The normalized engine uses HeadingDetail.numLevel as its canonical numeric representation.

This also means normalized levels greater than H6 can be represented and validated deterministically:

H6 → H7   ✓
H7 → H8   ✓
H6 → H8   ✗
H7 → H10  ✗

The normalized implementation intentionally does not reinterpret or clamp these values.

The sequential heading-level rule is an accessibility auditing heuristic. A reported heading-level jump should not be interpreted by itself as a definitive WCAG conformance determination.


Entry Points & Import Subpaths

The package exposes separate entry points for the React adapter, core utilities, and Playwright integration.

| Subpath | Target module | Usage | | ------------------------------------------ | -------------------- | ------------------------------------------------------------------------------------- | | react-heading-manager | React adapter | Render <Heading>, <Main>, <Section>, <Article>, hooks, and related components | | react-heading-manager/utils | Core + shared engine | DOM parsing, normalized heading audits, and hierarchy checks | | react-heading-manager/testing/playwright | Playwright adapter | Register and use the custom heading hierarchy matcher |

Playwright import

The supported Playwright entry point is:

import { registerPlaywright } from "react-heading-manager/testing/playwright";

⚠️ Avoid stale imports targeting the older path:

react - heading - manager / playwright;

Architecture & Module Isolation Policy

The package is organized into four layers with a strict dependency direction:

shared
   ↓
core
   ↓
adapters
   ↓
main

| Layer | Purpose | Can import | Must not import | | -------------- | ------------------------------------ | -------------------------- | ------------------------------------------ | | src/shared | Pure DOM primitives and shared types | Nothing higher-level | src/core, src/adapters, src/main | | src/core | Audit engine and DOM algorithms | src/shared | src/adapters, sibling src/core modules | | src/adapters | React and Playwright integrations | src/core, src/shared | Sibling adapters | | src/main | Public re-export routers | src/adapters, src/core | Business logic |

This isolation keeps the runtime surface clean and prevents framework-specific implementations from leaking into the framework-agnostic engine.


TypeScript Support

The package provides typed public APIs throughout the React, core, and testing layers.

Playwright matcher types are also augmented automatically.

import { expect } from "@playwright/test";
import { registerPlaywright } from "react-heading-manager/testing/playwright";

registerPlaywright(expect);

await expect(page).toHaveValidHeadingHierarchy();

No manual declaration merging is required.


Accessibility

react-heading-manager is designed to support accessibility auditing around document structure and heading relationships.

The core audit functionality considers:

  • native heading elements
  • ARIA heading roles
  • aria-level
  • HTML and ARIA landmark regions
  • heading relationships across nested regions

The package's audit rules are intended to help identify structural issues during development and testing.

They should be treated as auditing heuristics rather than a complete WCAG conformance engine.


Testing

The project maintains unit and integration coverage across:

  • DOM region extraction
  • heading normalization
  • legacy heading resolution
  • heading hierarchy processing
  • nested region traversal
  • normalized heading validation
  • Playwright matcher integration
  • edge cases involving ARIA headings and levels beyond H6

Run the test suite with:

npm test

Run linting with:

npm run lint

License

MIT © Vick Bake

See LICENSE for the full license text.