react-headings 4.0.0-next.1 → 4.0.0-next.2

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
@@ -13,12 +13,12 @@
13
13
 
14
14
  > Never write `h1`–`h6` again.
15
15
 
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.
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
- - 384 bytes minified & brotlied
21
+ - 370 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
@@ -108,7 +108,7 @@ Accepts every `h1` prop and a `ref`, plus:
108
108
 
109
109
  Pass `offset` or `level`, not both. Nested offsets add up. `offset={0}` adds nothing, which is useful for conditional nesting.
110
110
 
111
- Invalid values never throw: fractions round down, and anything else invalid counts as `offset={0}` or `level={1}`.
111
+ Invalid values never throw: they're ignored with a warning, as if not passed. `null` counts as not passed, without a warning.
112
112
 
113
113
  ### `useHeadingLevel()`
114
114
 
@@ -118,22 +118,11 @@ Returns the `level` (`1`–`6`) and `tag` (`"h1"`–`"h6"`) a `Heading` would re
118
118
 
119
119
  In development, React Headings warns in the console when:
120
120
 
121
- - a level is skipped, like an `h3` with no `h2` above it,
121
+ - a level is skipped,
122
122
  - a level goes past 6,
123
123
  - an `offset` or `level` is invalid.
124
124
 
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`.
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.
125
+ Warnings are removed from production builds. Vite and webpack show them automatically in dev mode. In plain Node, run with `--conditions=development`.
137
126
 
138
127
  ## License
139
128
 
@@ -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 getNormalizedOffset(offset) {
14
- return Number.isFinite(offset) && offset > 0 ? Math.floor(offset) : 0;
13
+ function isValidOffset(offset) {
14
+ return Number.isInteger(offset) && offset >= 0;
15
15
  }
16
- function getNormalizedLevel(level) {
17
- return Number.isFinite(level) && level > 1 ? Math.floor(level) : 1;
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 !== 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(
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 && normalizedOffset === 0,
37
+ isPassthrough: !isReset && offset === 0,
42
38
  children
43
39
  }
44
40
  ), /* @__PURE__ */ jsx(LevelContext.Provider, { value: innerLevel, children });
@@ -93,12 +89,12 @@ function useHeadingChecks() {
93
89
  );
94
90
  }, [level]), useEffect(() => scopeRegistry?.register(level), [scopeRegistry, level]);
95
91
  }
96
- function useInvalidPropWarning(prop, value, normalizedValue) {
92
+ function useInvalidPropWarning(prop, value, isValid) {
97
93
  useEffect(() => {
98
- value !== normalizedValue && console.warn(
99
- `react-headings: HeadingLevel received invalid ${prop} ${value}, treated as ${normalizedValue}.`
94
+ value != null && !isValid && console.warn(
95
+ `react-headings: HeadingLevel received invalid ${prop} ${value}, ignored.`
100
96
  );
101
- }, [prop, value, normalizedValue]);
97
+ }, [prop, value, isValid]);
102
98
  }
103
99
  export {
104
100
  Heading as H,
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 getNormalizedOffset(offset) {
14
- return Number.isFinite(offset) && offset > 0 ? Math.floor(offset) : 0;
13
+ function isValidOffset(offset) {
14
+ return Number.isInteger(offset) && offset >= 0;
15
15
  }
16
- function getNormalizedLevel(level) {
17
- return Number.isFinite(level) && level > 1 ? Math.floor(level) : 1;
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 !== void 0, normalizedOffset = getNormalizedOffset(offset), innerLevel = isReset ? getNormalizedLevel(level) : parentLevel + normalizedOffset;
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() {
package/package.json CHANGED
@@ -1,15 +1,13 @@
1
1
  {
2
2
  "name": "react-headings",
3
- "version": "4.0.0-next.1",
3
+ "version": "4.0.0-next.2",
4
4
  "type": "module",
5
- "description": "Dynamic heading levels for React. A polyfill of the HTML headingoffset proposal.",
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",