react-headings 4.0.0-next.1 → 4.0.0-next.3
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 +23 -35
- package/dist/index.d.ts +1 -1
- package/dist/index.development.js +11 -16
- package/dist/index.js +5 -6
- package/package.json +3 -4
package/README.md
CHANGED
|
@@ -13,12 +13,12 @@
|
|
|
13
13
|
|
|
14
14
|
> Never write `h1`–`h6` again.
|
|
15
15
|
|
|
16
|
-
|
|
16
|
+
Hardcoded heading levels break accessibility and SEO when components are reused. React Headings fixes this by computing each level from your component tree.
|
|
17
17
|
|
|
18
18
|
## Features
|
|
19
19
|
|
|
20
20
|
- Supports server-side rendering
|
|
21
|
-
-
|
|
21
|
+
- Only 369 bytes minified & brotlied
|
|
22
22
|
- Typed with TypeScript
|
|
23
23
|
- Works with any CSS solution (Tailwind, CSS-in-JS, etc.)
|
|
24
24
|
- Plays nicely with component libraries
|
|
@@ -31,37 +31,38 @@ npm install react-headings
|
|
|
31
31
|
|
|
32
32
|
## Usage
|
|
33
33
|
|
|
34
|
-
Use `
|
|
34
|
+
Use `Heading` for every heading. Wrap the content that belongs under a heading in `HeadingLevel`: headings inside render one level deeper.
|
|
35
35
|
|
|
36
36
|
```tsx
|
|
37
|
-
import {
|
|
37
|
+
import { Heading, HeadingLevel } from "react-headings";
|
|
38
38
|
|
|
39
39
|
function Page() {
|
|
40
40
|
return (
|
|
41
41
|
<>
|
|
42
|
-
<
|
|
42
|
+
<Heading>Star Wars</Heading> {/* h1 */}
|
|
43
43
|
<HeadingLevel>
|
|
44
|
-
<
|
|
45
|
-
|
|
46
|
-
<
|
|
47
|
-
<
|
|
48
|
-
|
|
44
|
+
<Heading>Characters</Heading> {/* h2 */}
|
|
45
|
+
<HeadingLevel>
|
|
46
|
+
<Section title="Luke" /> {/* h3 */}
|
|
47
|
+
<Section title="Leia" /> {/* h3 */}
|
|
48
|
+
<Section title="Han Solo" /> {/* h3 */}
|
|
49
|
+
</HeadingLevel>
|
|
49
50
|
</HeadingLevel>
|
|
50
51
|
</>
|
|
51
52
|
);
|
|
52
53
|
}
|
|
53
54
|
|
|
54
|
-
function
|
|
55
|
+
function Section({ title, children }) {
|
|
55
56
|
return (
|
|
56
|
-
<
|
|
57
|
-
<
|
|
57
|
+
<section>
|
|
58
|
+
<Heading>{title}</Heading>
|
|
58
59
|
<HeadingLevel>{children}</HeadingLevel>
|
|
59
|
-
</
|
|
60
|
+
</section>
|
|
60
61
|
);
|
|
61
62
|
}
|
|
62
63
|
```
|
|
63
64
|
|
|
64
|
-
`
|
|
65
|
+
`Section` doesn't know its level: it renders `h3` here.
|
|
65
66
|
|
|
66
67
|
### Dialogs
|
|
67
68
|
|
|
@@ -70,7 +71,7 @@ Portaled elements, like dialogs, aren't part of the page outline. Use `level` to
|
|
|
70
71
|
```tsx
|
|
71
72
|
<Dialog>
|
|
72
73
|
<HeadingLevel level={1}>
|
|
73
|
-
<
|
|
74
|
+
<Heading>Delete file?</Heading> {/* h1 */}
|
|
74
75
|
</HeadingLevel>
|
|
75
76
|
</Dialog>
|
|
76
77
|
```
|
|
@@ -80,14 +81,14 @@ Portaled elements, like dialogs, aren't part of the page outline. Use `level` to
|
|
|
80
81
|
React Headings only picks the tag. Style headings however you like, or use `render` for your own component:
|
|
81
82
|
|
|
82
83
|
```tsx
|
|
83
|
-
<
|
|
84
|
+
<Heading render={(props, { tag }) => <Text as={tag} size="lg" {...props} />}>
|
|
84
85
|
Title
|
|
85
|
-
</
|
|
86
|
+
</Heading>
|
|
86
87
|
```
|
|
87
88
|
|
|
88
89
|
## API
|
|
89
90
|
|
|
90
|
-
### `Heading`
|
|
91
|
+
### `Heading`
|
|
91
92
|
|
|
92
93
|
Renders `h1`–`h6` based on the current level: `h1` by default, and never deeper than `h6`.
|
|
93
94
|
|
|
@@ -97,8 +98,6 @@ Accepts every `h1` prop and a `ref`, plus:
|
|
|
97
98
|
| -------- | -------------------------------------------- | ----------------------------------------------------------------------------------------------------------- |
|
|
98
99
|
| `render` | `(props, { level, tag }) => ReactElement` | Renders your own element. Spread `props` on it (includes `children` and `ref`). |
|
|
99
100
|
|
|
100
|
-
`H` is the same component as `Heading`. Pick one name per project.
|
|
101
|
-
|
|
102
101
|
### `HeadingLevel`
|
|
103
102
|
|
|
104
103
|
| Prop | Type | Default | Description |
|
|
@@ -108,7 +107,7 @@ Accepts every `h1` prop and a `ref`, plus:
|
|
|
108
107
|
|
|
109
108
|
Pass `offset` or `level`, not both. Nested offsets add up. `offset={0}` adds nothing, which is useful for conditional nesting.
|
|
110
109
|
|
|
111
|
-
Invalid values never throw:
|
|
110
|
+
Invalid values never throw: they're ignored with a warning, as if not passed. `null` counts as not passed, without a warning.
|
|
112
111
|
|
|
113
112
|
### `useHeadingLevel()`
|
|
114
113
|
|
|
@@ -118,22 +117,11 @@ Returns the `level` (`1`–`6`) and `tag` (`"h1"`–`"h6"`) a `Heading` would re
|
|
|
118
117
|
|
|
119
118
|
In development, React Headings warns in the console when:
|
|
120
119
|
|
|
121
|
-
- a level is skipped,
|
|
120
|
+
- a level is skipped,
|
|
122
121
|
- a level goes past 6,
|
|
123
122
|
- an `offset` or `level` is invalid.
|
|
124
123
|
|
|
125
|
-
Warnings are removed from production builds. Vite and webpack show them automatically in dev mode. In plain Node
|
|
126
|
-
|
|
127
|
-
## Server Components
|
|
128
|
-
|
|
129
|
-
Works in Server Components, including Server Components nested inside `HeadingLevel`. Only `useHeadingLevel()` needs a Client Component.
|
|
130
|
-
|
|
131
|
-
## Differences from `headingoffset`
|
|
132
|
-
|
|
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.
|
|
124
|
+
Warnings are removed from production builds. Vite and webpack show them automatically in dev mode. In plain Node, run with `--conditions=development`.
|
|
137
125
|
|
|
138
126
|
## License
|
|
139
127
|
|
package/dist/index.d.ts
CHANGED
|
@@ -25,7 +25,6 @@ export declare const Heading: import("react").ForwardRefExoticComponent<Omit<imp
|
|
|
25
25
|
tag: Tag;
|
|
26
26
|
}) => ReactElement;
|
|
27
27
|
} & import("react").RefAttributes<HTMLHeadingElement>>;
|
|
28
|
-
export { Heading as H };
|
|
29
28
|
/**
|
|
30
29
|
* Renders headings inside `offset` levels deeper (default 1), or at `level`
|
|
31
30
|
* regardless of ancestors.
|
|
@@ -38,3 +37,4 @@ export declare function useHeadingLevel(): {
|
|
|
38
37
|
level: Level;
|
|
39
38
|
tag: Tag;
|
|
40
39
|
};
|
|
40
|
+
export {};
|
|
@@ -10,11 +10,11 @@ import {
|
|
|
10
10
|
} from "react";
|
|
11
11
|
import { jsx } from "react/jsx-runtime";
|
|
12
12
|
var LevelContext = createContext(1);
|
|
13
|
-
function
|
|
14
|
-
return Number.
|
|
13
|
+
function isValidOffset(offset) {
|
|
14
|
+
return Number.isInteger(offset) && offset >= 0;
|
|
15
15
|
}
|
|
16
|
-
function
|
|
17
|
-
return Number.
|
|
16
|
+
function isValidLevel(level) {
|
|
17
|
+
return Number.isInteger(level) && level >= 1;
|
|
18
18
|
}
|
|
19
19
|
var Heading = forwardRef(
|
|
20
20
|
function({ render, ...props }, ref) {
|
|
@@ -28,17 +28,13 @@ function HeadingLevel({
|
|
|
28
28
|
level,
|
|
29
29
|
children
|
|
30
30
|
}) {
|
|
31
|
-
let parentLevel = useContext(LevelContext), isReset = level
|
|
32
|
-
return useInvalidPropWarning(
|
|
33
|
-
isReset ? "level" : "offset",
|
|
34
|
-
isReset ? level : offset,
|
|
35
|
-
isReset ? innerLevel : normalizedOffset
|
|
36
|
-
), children = /* @__PURE__ */ jsx(
|
|
31
|
+
let parentLevel = useContext(LevelContext), isReset = isValidLevel(level), isOffsetValid = isValidOffset(offset), innerLevel = isReset ? level : parentLevel + (isOffsetValid ? offset : 1);
|
|
32
|
+
return useInvalidPropWarning("offset", offset, isOffsetValid), useInvalidPropWarning("level", level, isReset), children = /* @__PURE__ */ jsx(
|
|
37
33
|
SkippedLevelScope,
|
|
38
34
|
{
|
|
39
35
|
firstLevel: isReset ? innerLevel : parentLevel + 1,
|
|
40
36
|
isIsolated: isReset,
|
|
41
|
-
isPassthrough: !isReset &&
|
|
37
|
+
isPassthrough: !isReset && offset === 0,
|
|
42
38
|
children
|
|
43
39
|
}
|
|
44
40
|
), /* @__PURE__ */ jsx(LevelContext.Provider, { value: innerLevel, children });
|
|
@@ -93,15 +89,14 @@ function useHeadingChecks() {
|
|
|
93
89
|
);
|
|
94
90
|
}, [level]), useEffect(() => scopeRegistry?.register(level), [scopeRegistry, level]);
|
|
95
91
|
}
|
|
96
|
-
function useInvalidPropWarning(prop, value,
|
|
92
|
+
function useInvalidPropWarning(prop, value, isValid) {
|
|
97
93
|
useEffect(() => {
|
|
98
|
-
value
|
|
99
|
-
`react-headings: HeadingLevel received invalid ${prop} ${value},
|
|
94
|
+
value != null && !isValid && console.warn(
|
|
95
|
+
`react-headings: HeadingLevel received invalid ${prop} ${value}, ignored.`
|
|
100
96
|
);
|
|
101
|
-
}, [prop, value,
|
|
97
|
+
}, [prop, value, isValid]);
|
|
102
98
|
}
|
|
103
99
|
export {
|
|
104
|
-
Heading as H,
|
|
105
100
|
Heading,
|
|
106
101
|
HeadingLevel,
|
|
107
102
|
useHeadingLevel
|
package/dist/index.js
CHANGED
|
@@ -10,11 +10,11 @@ import {
|
|
|
10
10
|
} from "react";
|
|
11
11
|
import { jsx } from "react/jsx-runtime";
|
|
12
12
|
var LevelContext = createContext(1);
|
|
13
|
-
function
|
|
14
|
-
return Number.
|
|
13
|
+
function isValidOffset(offset) {
|
|
14
|
+
return Number.isInteger(offset) && offset >= 0;
|
|
15
15
|
}
|
|
16
|
-
function
|
|
17
|
-
return Number.
|
|
16
|
+
function isValidLevel(level) {
|
|
17
|
+
return Number.isInteger(level) && level >= 1;
|
|
18
18
|
}
|
|
19
19
|
var Heading = forwardRef(
|
|
20
20
|
function({ render, ...props }, ref) {
|
|
@@ -28,7 +28,7 @@ function HeadingLevel({
|
|
|
28
28
|
level,
|
|
29
29
|
children
|
|
30
30
|
}) {
|
|
31
|
-
let parentLevel = useContext(LevelContext), isReset = level
|
|
31
|
+
let parentLevel = useContext(LevelContext), isReset = isValidLevel(level), isOffsetValid = isValidOffset(offset), innerLevel = isReset ? level : parentLevel + (isOffsetValid ? offset : 1);
|
|
32
32
|
return /* @__PURE__ */ jsx(LevelContext.Provider, { value: innerLevel, children });
|
|
33
33
|
}
|
|
34
34
|
function useHeadingLevel() {
|
|
@@ -36,7 +36,6 @@ function useHeadingLevel() {
|
|
|
36
36
|
return { level, tag: `h${level}` };
|
|
37
37
|
}
|
|
38
38
|
export {
|
|
39
|
-
Heading as H,
|
|
40
39
|
Heading,
|
|
41
40
|
HeadingLevel,
|
|
42
41
|
useHeadingLevel
|
package/package.json
CHANGED
|
@@ -1,15 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-headings",
|
|
3
|
-
"version": "4.0.0-next.
|
|
3
|
+
"version": "4.0.0-next.3",
|
|
4
4
|
"type": "module",
|
|
5
|
-
"description": "Dynamic heading levels for React.
|
|
5
|
+
"description": "Dynamic heading levels for React.",
|
|
6
6
|
"author": "Alex Nault",
|
|
7
7
|
"keywords": [
|
|
8
8
|
"react",
|
|
9
9
|
"heading",
|
|
10
10
|
"headings",
|
|
11
|
-
"headingoffset",
|
|
12
|
-
"headingreset",
|
|
13
11
|
"level",
|
|
14
12
|
"outline",
|
|
15
13
|
"accessibility",
|
|
@@ -20,6 +18,7 @@
|
|
|
20
18
|
"license": "MIT",
|
|
21
19
|
"repository": "https://github.com/alexnault/react-headings",
|
|
22
20
|
"homepage": "https://github.com/alexnault/react-headings#readme",
|
|
21
|
+
"types": "./dist/index.d.ts",
|
|
23
22
|
"exports": {
|
|
24
23
|
".": {
|
|
25
24
|
"types": "./dist/index.d.ts",
|