react-headings 3.3.4 → 4.0.0-next.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -9,279 +9,132 @@
9
9
  <a aria-label="NPM version" href="https://www.npmjs.com/package/react-headings">
10
10
  <img alt="" src="https://img.shields.io/npm/v/react-headings?style=for-the-badge">
11
11
  </a>
12
- <a aria-label="Size" href="https://bundlephobia.com/package/react-headings">
13
- <img alt="" src="https://img.shields.io/bundlephobia/minzip/react-headings?style=for-the-badge">
14
- </a>
15
- <a aria-label="Monthly downloads" href="https://www.npmjs.com/package/react-headings">
16
- <img alt="" src="https://img.shields.io/npm/dm/react-headings?style=for-the-badge">
17
- </a>
18
12
  </p>
19
13
 
20
- > Never worry about using the wrong heading level (`h1`, `h2`, etc.) in complex React apps!
21
-
22
- React-headings maintains the proper hierarchy of headings for improved accessibility and SEO, no matter the component structure, while you keep full control of what's rendered.
23
-
24
- References:
25
-
26
- - [WCAG 2.0 technique H69](https://www.w3.org/TR/WCAG20-TECHS/H69.html)
27
- - [Lighthouse SEO heading order audit](https://web.dev/heading-order/)
28
-
29
- ## Table of contents
30
-
31
- - [Demos](#demos)
32
- - [Highlights](#highlights)
33
- - [Installation](#installation)
34
- - [Examples](#examples)
35
- - [API](#api)
36
- - [Changelog](#changelog)
37
- - [Contributing](#contributing)
14
+ > Never write `h1`–`h6` again.
38
15
 
39
- ## Demos
16
+ Search engines and screen readers rely on heading levels, but reusable components can't know how deep they'll be rendered. React Headings works out each level from your component tree, like the HTML [`headingoffset`](https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Global_attributes/headingoffset) proposal, but in any browser today.
40
17
 
41
- - [Minimal](https://codesandbox.io/s/react-headings-minimal-4temt?file=/src/Demo.js)
42
- - [Custom component](https://codesandbox.io/s/react-headings-custom-component-l4bjb?file=/src/Demo.js)
43
- - [Advanced structure](https://codesandbox.io/s/react-headings-advanced-structure-uxk4p?file=/src/Demo.js)
18
+ ## Features
44
19
 
45
- ## Highlights
46
-
47
- - Improves SEO and accessibility
48
20
  - Supports server-side rendering
49
- - Under 1 kB minified & gzipped
21
+ - 384 bytes minified & brotlied
50
22
  - Typed with TypeScript
51
- - Fully tested
52
- - Works with any CSS solutions (Tailwind, CSS-in-JS, etc.)
53
- - Plays nicely with component libraries (Material UI, etc.)
54
- - Follows [semantic versioning](https://semver.org/)
23
+ - Works with any CSS solution (Tailwind, CSS-in-JS, etc.)
24
+ - Plays nicely with component libraries
55
25
 
56
- ## Installation
26
+ ## Install
57
27
 
58
- ```bash
28
+ ```sh
59
29
  npm install react-headings
60
30
  ```
61
31
 
62
- ## Examples
63
-
64
- ### Basic usage
65
-
66
- ```jsx
67
- import React from "react";
68
- import { H, Section } from "react-headings";
69
-
70
- function App() {
71
- return (
72
- <Section component={<H>My hx</H>}>
73
- <div>...</div>
74
- <div>...</div>
75
- <div>...</div>
76
- <Section component={<H>My hx+1</H>}>
77
- <div>...</div>
78
- <div>...</div>
79
- <div>...</div>
80
- </Section>
81
- </Section>
82
- );
83
- }
84
- ```
85
-
86
- ### Advanced structure
32
+ ## Usage
87
33
 
88
- Child components inherit the current level of their parent:
34
+ Use `H` for every heading. Wrap the content that belongs under a heading in `HeadingLevel`: headings inside render one level deeper.
89
35
 
90
- ```jsx
91
- import React from "react";
92
- import { H, Section } from "react-headings";
36
+ ```tsx
37
+ import { H, HeadingLevel } from "react-headings";
93
38
 
94
- function ParentComponent() {
39
+ function Page() {
95
40
  return (
96
- <Section component={<H>My hx</H>}>
97
- <Section component={<H>My hx+1</H>}>
98
- <Section component={<H>My hx+2</H>}>
99
- <ChildComponent />
100
- </Section>
101
- </Section>
102
- </Section>
41
+ <>
42
+ <H>Star Wars</H> {/* h1 */}
43
+ <HeadingLevel>
44
+ <Card title="Characters"> {/* h2 */}
45
+ <Card title="Luke" /> {/* h3 */}
46
+ <Card title="Leia" /> {/* h3 */}
47
+ <Card title="Han Solo" /> {/* h3 */}
48
+ </Card>
49
+ </HeadingLevel>
50
+ </>
103
51
  );
104
52
  }
105
53
 
106
- function ChildComponent() {
107
- return (
108
- <Section component={<H>My hy</H>}>
109
- {/* The following heading would be a <h5> in the current context */}
110
- <Section component={<H>My hy+1</H>}>
111
- <p>...</p>
112
- </Section>
113
- </Section>
114
- );
115
- }
116
- ```
117
-
118
- ### Styling
119
-
120
- A heading can be styled like any ordinary `<hx>` element since it accepts all the same props:
121
-
122
- ```jsx
123
- import React from "react";
124
- import { H, Section } from "react-headings";
125
-
126
- function App() {
54
+ function Card({ title, children }) {
127
55
  return (
128
- <Section component={<H className="my-class">My hx</H>}>
129
- ...
130
- </Section>
56
+ <article>
57
+ <H>{title}</H>
58
+ <HeadingLevel>{children}</HeadingLevel>
59
+ </article>
131
60
  );
132
61
  }
133
62
  ```
134
63
 
135
- ### Custom heading
64
+ `Card` doesn't know its level: it renders `h2` for Characters, `h3` for each character, and `h1` on its own.
136
65
 
137
- A heading can be as complex as we want:
66
+ ### Dialogs
138
67
 
139
- ```jsx
140
- import React from "react";
141
- import { H, Section } from "react-headings";
142
- import MyIcon from "./MyIcon";
68
+ Portaled elements, like dialogs, aren't part of the page outline. Use `level` to start their headings at `h1`:
143
69
 
144
- function App() {
145
- return (
146
- <Section
147
- component={
148
- <div className="my-div">
149
- <MyIcon className="my-icon" />
150
- <H className="my-heading">My hx</H>
151
- </div>
152
- }
153
- >
154
- <div>...</div>
155
- <div>...</div>
156
- <div>...</div>
157
- </Section>
158
- );
159
- }
70
+ ```tsx
71
+ <Dialog>
72
+ <HeadingLevel level={1}>
73
+ <H>Delete file?</H> {/* h1 */}
74
+ </HeadingLevel>
75
+ </Dialog>
160
76
  ```
161
77
 
162
- ### Using component libraries
163
-
164
- Leveraging `Component` and `level` from the context allows the use of component libraries.
165
- Here's an example with [Material UI](https://material-ui.com/api/typography/):
166
-
167
- ```jsx
168
- import React from "react";
169
- import { useLevel } from "react-headings";
170
- import { Typography } from "@material-ui/core";
78
+ ### Styling
171
79
 
172
- function MyHeading(props) {
173
- const { Component } = useLevel();
80
+ React Headings only picks the tag. Style headings however you like, or use `render` for your own component:
174
81
 
175
- return <Typography component={Component} {...props} />;
176
- }
82
+ ```tsx
83
+ <H render={(props, { tag }) => <Text as={tag} size="lg" {...props} />}>
84
+ Title
85
+ </H>
177
86
  ```
178
87
 
179
88
  ## API
180
89
 
181
- ### `<H>` component
182
-
183
- Renders a `<h1>`, `<h2>`, `<h3>`, `<h4>`, `<h5>` or `<h6>` depending on the current level.
184
-
185
- #### Props
186
-
187
- | Name | Type | Required | Description |
188
- | ---------- | ---------- | -------- | --------------------------------------------------------------- |
189
- | `render` | `function` | No | Override with a custom heading. Has precedence over `children`. |
190
- | `children` | `node` | No | The content of the heading. Usually the title. |
191
-
192
- Any other props will be passed to the heading element.
193
-
194
- #### Example
195
-
196
- ```jsx
197
- import React from "react";
198
- import { H } from "react-headings";
199
-
200
- function Example1() {
201
- return <H>This is my title</H>;
202
- }
203
-
204
- function Example2() {
205
- return (
206
- <H render={({ level, Component }) => <Component>My h{level}</Component>} />
207
- );
208
- }
209
- ```
210
-
211
- ### `<Section>` component
90
+ ### `Heading` / `H`
212
91
 
213
- Creates a new section (a heading and its level).
92
+ Renders `h1`–`h6` based on the current level: `h1` by default, and never deeper than `h6`.
214
93
 
215
- #### Props
94
+ Accepts every `h1` prop and a `ref`, plus:
216
95
 
217
- | Name | Type | Required | Description |
218
- | ----------- | ------ | -------- | ------------------------------------------------------------------------------- |
219
- | `component` | `node` | Yes | The heading component. Can be anything but best used in combination with `<H>`. |
220
- | `children` | `node` | No | The content of the new level. |
96
+ | Prop | Type | Description |
97
+ | -------- | -------------------------------------------- | ----------------------------------------------------------------------------------------------------------- |
98
+ | `render` | `(props, { level, tag }) => ReactElement` | Renders your own element. Spread `props` on it (includes `children` and `ref`). |
221
99
 
222
- #### Example
100
+ `H` is the same component as `Heading`. Pick one name per project.
223
101
 
224
- ```jsx
225
- import React from "react";
226
- import { Section, H } from "react-headings";
227
-
228
- function Example1() {
229
- return (
230
- <Section component={<H>This is my title</H>}>
231
- This is my content
232
- </Section>
233
- );
234
- }
102
+ ### `HeadingLevel`
235
103
 
236
- function Example2() {
237
- return (
238
- <Section
239
- component={
240
- <div>
241
- <div>
242
- <H>This is my title</H>
243
- </div>
244
- </div>
245
- }
246
- >
247
- This is my content
248
- </Section>
249
- );
250
- }
251
- ```
104
+ | Prop | Type | Default | Description |
105
+ | -------- | -------- | ------- | ------------------------------------------------ |
106
+ | `offset` | `number` | `1` | Levels added to headings inside. |
107
+ | `level` | `number` | | Level of headings inside, ignoring parents. |
252
108
 
253
- ### `useLevel` hook
109
+ Pass `offset` or `level`, not both. Nested offsets add up. `offset={0}` adds nothing, which is useful for conditional nesting.
254
110
 
255
- Returns an object containing the current `level` and current `Component`.
111
+ Invalid values never throw: fractions round down, and anything else invalid counts as `offset={0}` or `level={1}`.
256
112
 
257
- #### Arguments
113
+ ### `useHeadingLevel()`
258
114
 
259
- None
115
+ Returns the `level` (`1`–`6`) and `tag` (`"h1"`–`"h6"`) a `Heading` would render here.
260
116
 
261
- #### Returns
117
+ ## Development warnings
262
118
 
263
- | Name | Type | Description |
264
- | ----------- | -------------------------------------------------------- | ------------------------------------- |
265
- | `level` | `1` \| `2` \| `3` \| `4` \| `5` \| `6` | The current level. |
266
- | `Component` | `"h1"` \| `"h2"` \| `"h3"` \| `"h4"` \| `"h5"` \| `"h6"` | The current component. Same as level. |
119
+ In development, React Headings warns in the console when:
267
120
 
268
- #### Example
121
+ - a level is skipped, like an `h3` with no `h2` above it,
122
+ - a level goes past 6,
123
+ - an `offset` or `level` is invalid.
269
124
 
270
- ```jsx
271
- import React from "react";
272
- import { useLevel } from "react-headings";
125
+ Warnings are removed from production builds. Vite and webpack show them automatically in dev mode. In plain Node or Jest, run with `--conditions=development`.
273
126
 
274
- function Example(props) {
275
- const { level, Component } = useLevel();
127
+ ## Server Components
276
128
 
277
- return <Component {...props}>This is a h{level}</Component>;
278
- }
279
- ```
129
+ Works in Server Components, including Server Components nested inside `HeadingLevel`. Only `useHeadingLevel()` needs a Client Component.
280
130
 
281
- ## Changelog
131
+ ## Differences from `headingoffset`
282
132
 
283
- For a list of changes and releases, see the [changelog](https://github.com/alexnault/react-headings/releases).
133
+ - Levels follow the React tree, not the DOM, so portaled content inherits its level.
134
+ - `Heading` always starts at level 1, while a native `h2` starts at 2.
135
+ - Levels stop at 6, while `headingoffset` goes up to 9 through `aria-level`.
136
+ - The output is a real `h1`–`h6` tag, so CSS like `h2 { … }` matches the level.
284
137
 
285
- ## Contributing
138
+ ## License
286
139
 
287
- Found a bug, have a question or looking to improve react-headings? Open an [issue](https://github.com/alexnault/react-headings/issues/new), start a [discussion](https://github.com/alexnault/react-headings/discussions/new) or submit a [PR](https://github.com/alexnault/react-headings/fork)!
140
+ MIT
package/dist/index.d.ts CHANGED
@@ -1,29 +1,40 @@
1
- import React from "react";
1
+ import type { ComponentPropsWithoutRef, ComponentPropsWithRef, ReactElement, ReactNode } from "react";
2
2
  type Level = 1 | 2 | 3 | 4 | 5 | 6;
3
- type Heading = `h${Level}`;
4
- type LevelContextValue = {
5
- level: Level;
6
- Component: Heading;
3
+ type Tag = `h${Level}`;
4
+ export type HeadingProps = ComponentPropsWithoutRef<"h1"> & {
5
+ render?: (props: ComponentPropsWithRef<"h1">, heading: {
6
+ level: Level;
7
+ tag: Tag;
8
+ }) => ReactElement;
7
9
  };
10
+ export type HeadingLevelProps = {
11
+ children?: ReactNode;
12
+ } & ({
13
+ offset?: number;
14
+ level?: never;
15
+ } | {
16
+ level: number;
17
+ offset?: never;
18
+ });
8
19
  /**
9
- * Returns the current heading and level.
20
+ * Renders an `h1`–`h6` according to the current level.
10
21
  */
11
- export declare function useLevel(): LevelContextValue;
12
- type HProps = React.DetailedHTMLProps<React.HTMLAttributes<HTMLHeadingElement>, HTMLHeadingElement> & {
13
- render?: (context: LevelContextValue) => React.ReactElement;
14
- };
22
+ export declare const Heading: import("react").ForwardRefExoticComponent<Omit<import("react").DetailedHTMLProps<import("react").HTMLAttributes<HTMLHeadingElement>, HTMLHeadingElement>, "ref"> & {
23
+ render?: (props: ComponentPropsWithRef<"h1">, heading: {
24
+ level: Level;
25
+ tag: Tag;
26
+ }) => ReactElement;
27
+ } & import("react").RefAttributes<HTMLHeadingElement>>;
28
+ export { Heading as H };
15
29
  /**
16
- * Renders a dynamic HTML heading (h1, h2, etc.) or custom component according to the current level.
30
+ * Renders headings inside `offset` levels deeper (default 1), or at `level`
31
+ * regardless of ancestors.
17
32
  */
18
- export declare const H: React.ForwardRefExoticComponent<Omit<HProps, "ref"> & React.RefAttributes<HTMLHeadingElement>>;
19
- type SectionProps = {
20
- component: React.ReactNode;
21
- children?: React.ReactNode;
22
- };
33
+ export declare function HeadingLevel({ offset, level, children, }: HeadingLevelProps): import("react").JSX.Element;
23
34
  /**
24
- * Renders `component` in the current level and `children` in the next level.
25
- * @param component A component containing a heading
26
- * @param children The children in the next level
35
+ * Returns the level and tag a `Heading` would render at this position.
27
36
  */
28
- export declare function Section({ component, children }: SectionProps): JSX.Element;
29
- export {};
37
+ export declare function useHeadingLevel(): {
38
+ level: Level;
39
+ tag: Tag;
40
+ };
@@ -0,0 +1,108 @@
1
+ "use client";
2
+
3
+ // src/index.tsx
4
+ import {
5
+ createContext,
6
+ forwardRef,
7
+ useContext,
8
+ useEffect,
9
+ useMemo
10
+ } from "react";
11
+ import { jsx } from "react/jsx-runtime";
12
+ var LevelContext = createContext(1);
13
+ function getNormalizedOffset(offset) {
14
+ return Number.isFinite(offset) && offset > 0 ? Math.floor(offset) : 0;
15
+ }
16
+ function getNormalizedLevel(level) {
17
+ return Number.isFinite(level) && level > 1 ? Math.floor(level) : 1;
18
+ }
19
+ var Heading = forwardRef(
20
+ function({ render, ...props }, ref) {
21
+ let { level, tag } = useHeadingLevel();
22
+ return useHeadingChecks(), render ? render({ ...props, ref }, { level, tag }) : /* @__PURE__ */ jsx(tag, { ref, ...props });
23
+ }
24
+ );
25
+ Heading.displayName = "Heading";
26
+ function HeadingLevel({
27
+ offset = 1,
28
+ level,
29
+ children
30
+ }) {
31
+ let parentLevel = useContext(LevelContext), isReset = level !== void 0, normalizedOffset = getNormalizedOffset(offset), innerLevel = isReset ? getNormalizedLevel(level) : parentLevel + normalizedOffset;
32
+ return useInvalidPropWarning(
33
+ isReset ? "level" : "offset",
34
+ isReset ? level : offset,
35
+ isReset ? innerLevel : normalizedOffset
36
+ ), children = /* @__PURE__ */ jsx(
37
+ SkippedLevelScope,
38
+ {
39
+ firstLevel: isReset ? innerLevel : parentLevel + 1,
40
+ isIsolated: isReset,
41
+ isPassthrough: !isReset && normalizedOffset === 0,
42
+ children
43
+ }
44
+ ), /* @__PURE__ */ jsx(LevelContext.Provider, { value: innerLevel, children });
45
+ }
46
+ function useHeadingLevel() {
47
+ let level = Math.min(useContext(LevelContext), 6);
48
+ return { level, tag: `h${level}` };
49
+ }
50
+ var ScopeContext = /* @__PURE__ */ createContext(null);
51
+ function createScopeRegistry(firstLevel, parentRegistry) {
52
+ let headingLevels = /* @__PURE__ */ new Map(), unregisterFromParent, isCheckScheduled = !1, hasWarned = !1;
53
+ function warnIfLevelSkipped() {
54
+ if (isCheckScheduled = !1, hasWarned || headingLevels.size === 0) return;
55
+ let presentLevels = new Set(headingLevels.values()), deepestLevel = Math.max(...presentLevels);
56
+ for (let level = firstLevel; level < deepestLevel; level++)
57
+ if (!presentLevels.has(level)) {
58
+ hasWarned = !0, console.warn(
59
+ `react-headings: Skipped heading level ${level}: found a level ${deepestLevel} heading but no level ${level} heading.`
60
+ );
61
+ return;
62
+ }
63
+ }
64
+ function scheduleCheck() {
65
+ isCheckScheduled || (isCheckScheduled = !0, queueMicrotask(warnIfLevelSkipped));
66
+ }
67
+ return {
68
+ register(level) {
69
+ let token = {};
70
+ return headingLevels.set(token, level), headingLevels.size === 1 && (unregisterFromParent = parentRegistry?.register(firstLevel)), scheduleCheck(), () => {
71
+ headingLevels.delete(token), headingLevels.size === 0 && (unregisterFromParent?.(), unregisterFromParent = void 0), scheduleCheck();
72
+ };
73
+ }
74
+ };
75
+ }
76
+ function SkippedLevelScope({
77
+ firstLevel,
78
+ isIsolated,
79
+ isPassthrough,
80
+ children
81
+ }) {
82
+ let parentRegistry = useContext(ScopeContext), registry = useMemo(
83
+ () => isPassthrough ? parentRegistry : createScopeRegistry(firstLevel, isIsolated ? null : parentRegistry),
84
+ [firstLevel, isIsolated, isPassthrough, parentRegistry]
85
+ );
86
+ return /* @__PURE__ */ jsx(ScopeContext.Provider, { value: registry, children });
87
+ }
88
+ function useHeadingChecks() {
89
+ let level = useContext(LevelContext), scopeRegistry = useContext(ScopeContext);
90
+ useEffect(() => {
91
+ level > 6 && console.warn(
92
+ `react-headings: Heading level ${level} exceeds 6 and was clamped to h6.`
93
+ );
94
+ }, [level]), useEffect(() => scopeRegistry?.register(level), [scopeRegistry, level]);
95
+ }
96
+ function useInvalidPropWarning(prop, value, normalizedValue) {
97
+ useEffect(() => {
98
+ value !== normalizedValue && console.warn(
99
+ `react-headings: HeadingLevel received invalid ${prop} ${value}, treated as ${normalizedValue}.`
100
+ );
101
+ }, [prop, value, normalizedValue]);
102
+ }
103
+ export {
104
+ Heading as H,
105
+ Heading,
106
+ HeadingLevel,
107
+ useHeadingLevel
108
+ };
package/dist/index.js CHANGED
@@ -1,70 +1,43 @@
1
- "use strict";
2
- var __assign = (this && this.__assign) || function () {
3
- __assign = Object.assign || function(t) {
4
- for (var s, i = 1, n = arguments.length; i < n; i++) {
5
- s = arguments[i];
6
- for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
7
- t[p] = s[p];
8
- }
9
- return t;
10
- };
11
- return __assign.apply(this, arguments);
12
- };
13
- var __rest = (this && this.__rest) || function (s, e) {
14
- var t = {};
15
- for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
16
- t[p] = s[p];
17
- if (s != null && typeof Object.getOwnPropertySymbols === "function")
18
- for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
19
- if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
20
- t[p[i]] = s[p[i]];
21
- }
22
- return t;
23
- };
24
- var __importDefault = (this && this.__importDefault) || function (mod) {
25
- return (mod && mod.__esModule) ? mod : { "default": mod };
26
- };
27
- Object.defineProperty(exports, "__esModule", { value: true });
28
- exports.Section = exports.H = exports.useLevel = void 0;
29
- var react_1 = __importDefault(require("react"));
30
- var LevelContext = react_1.default.createContext({
31
- level: 1,
32
- Component: "h1",
33
- });
34
- /**
35
- * Returns the current heading and level.
36
- */
37
- function useLevel() {
38
- return react_1.default.useContext(LevelContext);
1
+ "use client";
2
+
3
+ // src/index.tsx
4
+ import {
5
+ createContext,
6
+ forwardRef,
7
+ useContext,
8
+ useEffect,
9
+ useMemo
10
+ } from "react";
11
+ import { jsx } from "react/jsx-runtime";
12
+ var LevelContext = createContext(1);
13
+ function getNormalizedOffset(offset) {
14
+ return Number.isFinite(offset) && offset > 0 ? Math.floor(offset) : 0;
39
15
  }
40
- exports.useLevel = useLevel;
41
- function InternalH(_a, forwardedRef) {
42
- var render = _a.render, props = __rest(_a, ["render"]);
43
- var context = useLevel();
44
- if (render) {
45
- return render(context);
46
- }
47
- return react_1.default.createElement(context.Component, __assign({ ref: forwardedRef }, props));
16
+ function getNormalizedLevel(level) {
17
+ return Number.isFinite(level) && level > 1 ? Math.floor(level) : 1;
48
18
  }
49
- /**
50
- * Renders a dynamic HTML heading (h1, h2, etc.) or custom component according to the current level.
51
- */
52
- exports.H = react_1.default.forwardRef(InternalH);
53
- /**
54
- * Renders `component` in the current level and `children` in the next level.
55
- * @param component A component containing a heading
56
- * @param children The children in the next level
57
- */
58
- function Section(_a) {
59
- var component = _a.component, children = _a.children;
60
- var level = useLevel().level;
61
- var nextLevel = Math.min(level + 1, 6);
62
- var value = {
63
- level: nextLevel,
64
- Component: "h".concat(nextLevel),
65
- };
66
- return (react_1.default.createElement(react_1.default.Fragment, null,
67
- component,
68
- react_1.default.createElement(LevelContext.Provider, { value: value }, children)));
19
+ var Heading = forwardRef(
20
+ function({ render, ...props }, ref) {
21
+ let { level, tag } = useHeadingLevel();
22
+ return render ? render({ ...props, ref }, { level, tag }) : /* @__PURE__ */ jsx(tag, { ref, ...props });
23
+ }
24
+ );
25
+ Heading.displayName = "Heading";
26
+ function HeadingLevel({
27
+ offset = 1,
28
+ level,
29
+ children
30
+ }) {
31
+ let parentLevel = useContext(LevelContext), isReset = level !== void 0, normalizedOffset = getNormalizedOffset(offset), innerLevel = isReset ? getNormalizedLevel(level) : parentLevel + normalizedOffset;
32
+ return /* @__PURE__ */ jsx(LevelContext.Provider, { value: innerLevel, children });
69
33
  }
70
- exports.Section = Section;
34
+ function useHeadingLevel() {
35
+ let level = Math.min(useContext(LevelContext), 6);
36
+ return { level, tag: `h${level}` };
37
+ }
38
+ export {
39
+ Heading as H,
40
+ Heading,
41
+ HeadingLevel,
42
+ useHeadingLevel
43
+ };
package/package.json CHANGED
@@ -1,27 +1,37 @@
1
1
  {
2
2
  "name": "react-headings",
3
- "version": "3.3.4",
4
- "description": "HTML headings with auto-incrementing levels for WCAG compliance.",
5
- "main": "dist/index.js",
6
- "types": "dist/index.d.ts",
3
+ "version": "4.0.0-next.1",
4
+ "type": "module",
5
+ "description": "Dynamic heading levels for React. A polyfill of the HTML headingoffset proposal.",
7
6
  "author": "Alex Nault",
8
7
  "keywords": [
9
8
  "react",
10
9
  "heading",
11
- "auto",
10
+ "headings",
11
+ "headingoffset",
12
+ "headingreset",
12
13
  "level",
14
+ "outline",
13
15
  "accessibility",
14
16
  "a11y",
15
17
  "wcag",
16
- "h1",
17
- "header",
18
- "title"
18
+ "h1"
19
19
  ],
20
20
  "license": "MIT",
21
21
  "repository": "https://github.com/alexnault/react-headings",
22
22
  "homepage": "https://github.com/alexnault/react-headings#readme",
23
+ "exports": {
24
+ ".": {
25
+ "types": "./dist/index.d.ts",
26
+ "development": "./dist/index.development.js",
27
+ "production": "./dist/index.js",
28
+ "default": "./dist/index.js"
29
+ },
30
+ "./package.json": "./package.json"
31
+ },
23
32
  "scripts": {
24
- "build": "rm -rf ./dist && tsc",
33
+ "build": "rm -rf dist && node scripts/build.js && tsc -p tsconfig.build.json",
34
+ "typecheck": "tsc",
25
35
  "format": "prettier ./ --write",
26
36
  "lint": "eslint ./ --max-warnings=0",
27
37
  "size": "size-limit",
@@ -32,26 +42,28 @@
32
42
  ],
33
43
  "sideEffects": false,
34
44
  "peerDependencies": {
35
- "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
45
+ "react": "^18.0.0 || ^19.0.0"
36
46
  },
37
47
  "devDependencies": {
38
- "@size-limit/preset-small-lib": "^7.0.8",
39
- "@testing-library/react": "^13.4.0",
40
- "@types/react": "^18.3.28",
41
- "@types/react-dom": "^18.3.7",
42
- "@typescript-eslint/eslint-plugin": "^4.33.0",
43
- "@typescript-eslint/parser": "^4.33.0",
44
- "@vitest/coverage-c8": "^0.30.1",
45
- "conventional-changelog-conventionalcommits": "^8.0.0",
46
- "eslint": "^7.32.0",
47
- "eslint-config-prettier": "^8.10.2",
48
- "jsdom": "^21.1.2",
49
- "prettier": "^2.8.8",
50
- "react": "^18.3.1",
51
- "react-dom": "^18.3.1",
48
+ "@eslint/js": "^10.0.1",
49
+ "@size-limit/preset-small-lib": "^14.3.0",
50
+ "@testing-library/dom": "^10.4.2",
51
+ "@testing-library/react": "^16.3.3",
52
+ "@types/react": "^19.3.0",
53
+ "@types/react-dom": "^19.3.0",
54
+ "@vitest/coverage-v8": "^5.0.5",
55
+ "conventional-changelog-conventionalcommits": "^9.3.1",
56
+ "esbuild": "^0.28.2",
57
+ "eslint": "^10.14.0",
58
+ "eslint-plugin-react-hooks": "^7.1.1",
59
+ "jsdom": "^30.1.3",
60
+ "prettier": "^3.9.10",
61
+ "react": "^19.3.1",
62
+ "react-dom": "^19.3.1",
52
63
  "semantic-release": "^25.0.3",
53
- "size-limit": "^7.0.8",
54
- "typescript": "^4.9.5",
55
- "vitest": "^0.30.1"
64
+ "size-limit": "^14.3.0",
65
+ "typescript": "^5.9.3",
66
+ "typescript-eslint": "^8.72.1",
67
+ "vitest": "^5.0.5"
56
68
  }
57
69
  }