Fix typedoc signature heading levels: uniform sibling sections for signature parts#318
Conversation
|
|
|
Someone is attempting to deploy a commit to the universal-ember Team on Vercel. A member of the Team first needs to authorize it. |
- Revert the theme-toggle heading bump: State Attributes / Styling are back to h3, under API Reference. The real cause of the heading-order violation (kolay rendering the signature's "Element" heading as an h1) is fixed upstream in universal-ember/kolay#318, which makes all signature headings render one level below the preceding document heading (h3 under an "API Reference" h2). - Update the comment above the (now disabled) typescript@rc scenario: "allowed to fail via continue-on-error" no longer applies since the scenario was commented out; still tracked in issue NullVoxPopuli#98. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
NullVoxPopuli
left a comment
There was a problem hiding this comment.
Don't use hard-coded level numbers. Use the sectioning rules alone, and the automatic heading will be figured out.
|
Done in 3bf9acb — all level numbers (HeadingScope / SectionHeading / One observation from verifying, for your radar rather than this PR: through non-boundary wrapper layers (like the repl demo divs), the detection's fallback subtree traversal resolves subsequent sibling headings one level below the first (timeline renders Verification: kolay suite green (browser 29/29 incl. a new regression test — |
| </section> | ||
| {{! | ||
| No <section> around this (or any sibling) heading: | ||
| a nested heading-less <section> is its own sectioning boundary, which |
There was a problem hiding this comment.
that sounds like a bug, rather than something to avoid
There was a problem hiding this comment.
Agreed — fixed at the source: universal-ember/ember-primitives#776. Two bugs in getSectionHeadingLevel, both contradicting its own docstring ("stopping if we encounter other boundary elements"):
findHeadingIn's fallback child-traversal recursed into nested sectioning content, so headings inside a sibling/nested<section>leaked out as context — that's what made sibling headings resolve one-deeper and union variants cascade h3→h4→h5. The fallback now skipssection/article/aside/nav, consistent with the previous-sibling scan. (header/footer/mainare still traversed — a<header>contains its section's heading; a blanket boundary skip fails 4 of the existing "wrapped headings" rendering tests.)levelOfreturned 1 when the walk reached a boundary that had no heading of its own — that's what pinned headings inside heading-less wrapper sections to<h1>. Such boundaries are now transparent: the walk keeps searching outward.
With that fix, this PR needs no code change — the headings here just sit in <Load>'s section and all resolve to the same level, one below whatever heading precedes the block. I reworded these comments in cc92e97 so they state intent rather than describe the (now-fixed) quirk.
Verified the full chain: ember-primitives suite green with the fix (388 pass, incl. all existing wrapped-headings/extraneous-sections/shadow cases + 2 new unit tests); kolay suite green with the fixed package injected (regression test unchanged — it never encoded the buggy levels); nvp.ui floating-deps repro with both packed fixes: 83/83 under axe-core 4.12.1, timeline now renders h3 Timeline → h4 Element / h4 Arguments / h4 Blocks with union variants at equal levels, theme-toggle h2 API Reference → h3 Element → h3 State Attributes.
Note: until ember-primitives#776 lands and a release flows through (ember-primitives pins which-heading-do-i-need exactly, currently 0.2.6), published kolay + this PR yields h3/h4/h4-style siblings through the repl wrappers — still heading-order-legal everywhere, so nothing is blocked on the ordering of the two merges.
|
The Browser Tests failures on this PR are an unrelated CI race, not the heading change: @embroider/vite 1.5.2's jump-dir race guard only catches |
…race
CI's Browser Tests job intermittently fails with
Error: ENOTEMPTY: directory not empty,
rename '/tmp/embroider-vite-jump-tmp-XXXXXX' -> '/tmp/embroider-vite-jump-<hash>'
whenever multiple test apps build in parallel (turbo) and resolve the
same workspace package: the jump dir is content-addressed by
pkg.root + pkg.name, so concurrent builds race to rename onto the same
target. @embroider/vite 1.5.2 only caught EEXIST from that race, but on
Linux renaming onto an existing non-empty directory throws ENOTEMPTY,
which escaped the guard and failed the build. This bites every PR that
changes kolay's dist (all three test apps rebuild in parallel; it
killed 3 consecutive CI runs on #318).
@embroider/vite 1.7.7+ handles ENOTEMPTY in the race guard, so bump the
test apps' pins to 1.7.8 (and @embroider/core to 4.6.2 to satisfy its
peer range). All test-app suites and node tests pass locally.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ent heading
Previously, the Element / Arguments / Blocks / Return headings in
rendered signatures used automatic per-heading level detection
(ember-primitives' <Heading> via which-heading-do-i-need). That
algorithm treats every <section> as a nesting boundary and only looks
for context between the two nearest boundaries, which breaks down for
this markup in two ways:
- <Load>'s heading-less <section> wrapper isolates the headings from
the surrounding document, so the first heading of a signature
renders as an <h1> in the middle of a docs page.
- sibling headings/sections influence each other via the fallback
child-traversal, so union signature variants cascade one level
deeper per variant (h4, h5, h6, ...) and "Arguments" would key off
whatever heading the traversal happened to find first.
Both produce axe `heading-order` violations ("Heading levels should
only increase by one") in consuming docs pages, e.g. nvp.ui's
/Docs/3-components/theme-toggle, whose signature has only an Element:
the page's "### State Attributes" <h3> followed the rendered <h1>
directly (flagged by axe-core >= 4.12; picked up by no-lockfile CI
installs).
Instead, compute the level once per rendered signature: one level
below the nearest heading that precedes the api-docs block in document
order (crossing shadow roots). All member headings are siblings at
that same level, matching how the docs around them are authored:
## API Reference <- h2 authored in markdown
(Element h3) (Arguments h3) (Blocks h3)
### State Attributes <- h3 authored in markdown
- new HeadingScope component: anchors a text node before the signature
content and yields the computed level to everything within
- new SectionHeading component: renders an h1-h6 at the given @Level
(adds ember-element-helper, already in the dependency graph via
ember-primitives)
- ComponentSignature / ModifierSignature / HelperSignature thread the
level through Element / Args / Blocks / Return
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…e levels Drops HeadingScope / SectionHeading / explicit @Level threading (and the ember-element-helper dependency) in favor of fixing the markup that was confusing automatic heading-level detection: - Element and Blocks no longer wrap their <Heading> in an inner <section>: a nested heading-less section is its own sectioning boundary, which cuts detection off from the surrounding document and pins the first heading of every signature to <h1>. The section these headings belong to is the one rendered by <Load>. - Return keeps its styling hook but as a <div class='typedoc__helper__return'> instead of a <section>, for the same reason. With the headings living directly in <Load>'s section, detection resolves them against the heading that precedes the api-docs block (e.g. an "API Reference" <h2> yields an <h3> Element), and axe's heading-order audit passes on consuming docs pages. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The behaviors the previous comments described working around are bugs in which-heading-do-i-need, now fixed in universal-ember/ember-primitives#776 (fallback traversal no longer treats nested sectioning content as heading context; heading-less boundaries are transparent). The comments now just state the intent: these headings are siblings within <Load>'s <section>. With that fix, all signature headings resolve to the same level, one below the heading that precedes the api-docs block (verified against nvp.ui with both fixes packed: timeline renders h3 Timeline -> h4 Element / h4 Arguments / h4 Blocks, union variants at equal levels, 83/83 with axe-core 4.12.1). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
4341ce5 to
44f3fb4
Compare
…xt; empty boundaries are transparent
Fixes two ways getSectionHeadingLevel disagreed with its own documented
algorithm ("We'll need to check the subtrees between these elements,
stopping if we encounter other boundary elements"):
1. findHeadingIn's fallback child-traversal recursed into every child,
including sectioning content. Headings inside a nested/sibling
<section> belong to that section and are not context for ours, but
the fallback would find them anyway. This made sibling headings
resolve one level deeper than the first through non-boundary
wrappers, and made sibling <section>s cascade one level per section
(h3, h4, h5, ...) instead of staying at equal levels.
The fallback now skips sectioning content (section, article, aside,
nav) like the previous-sibling traversal already did. header,
footer, and main are still traversed: a <header> in particular
typically contains its section's heading (covered by the existing
"wrapped headings" rendering tests).
2. levelOf returned 1 as soon as the walk reached the next boundary,
even if that boundary had no heading of its own. A heading-less
wrapper <section> therefore isolated everything inside it, pinning
headings to <h1> mid-document. Such a boundary has no heading level
to nest under, so it is now transparent: the walk keeps searching
outward for context.
Both fixes come out of universal-ember/kolay#318, where api-docs
headings rendered inside a heading-less <section> came out as <h1>
(failing axe's heading-order audit in consuming docs pages), and
union-signature variants cascaded one level per variant.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
0.60.1 ships which-heading-do-i-need 0.4.1, which contains the traversal fixes from universal-ember/ember-primitives#776 (fallback does not treat nested sectioning content as heading context; heading-less boundaries are transparent). Together with the markup change in this PR, all api-docs signature headings now resolve to the same level, one below the heading that precedes the block (e.g. "## API Reference" -> h3 Element / h3 Arguments / h3 Blocks). Bumped in the addon package, docs-app, and both test apps that pin it. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
0093f54 bumps Re-verified with the real 0.60.1 from npm (not a packed build): browser suites 6/6 tasks — docs-app 29/29 including the heading regression test, which asserts the flattened behavior (an Downstream effect once this releases: nvp.ui's floating-deps a11y jobs go green with its markdown untouched ( |
|
|
||
| const Blocks: TOC<{ Args: { info: any } }> = <template> | ||
| {{#if @info}} | ||
| <section> |
There was a problem hiding this comment.
do these still need to be moved out of the sections?
There was a problem hiding this comment.
it's possible that declarations within here could have headers as well
There was a problem hiding this comment.
No — they're back in 57ffca5, and you're right on both counts. With the ember-primitives@0.60.1 bump this PR now carries (which-heading-do-i-need 0.4.1), a heading-less boundary is transparent instead of pinning h1, so headings inside these sections resolve correctly again.
One catch, found empirically: restoring the sections exactly as they were renders Element h3 / Arguments h3 / Blocks h4 — the regression test fails with "h3.typedoc__heading exists twice". Arguments never had a <section>, so its heading sits directly in <Load>'s section, and per the sectioning rules a <section> following a sibling h3 is a subsection of it — the detection's previous-sibling scan finds the Arguments h3 and nests Blocks under it. So 57ffca5 makes the structure uniform instead: every part (Element, Arguments, Blocks, Return) is a sibling <section> containing its heading and its content, and uniform sibling sections all resolve to the same level, one below the preceding document heading.
And yes — declarations in here can carry headings, since comments are compiled markdown. That's the strongest reason to keep the sections: content contained in a part's <section> can't become level-context for the parts that follow (both the sibling scan and the fallback traversal skip sectioning content). Element's comment moved inside its section for the same reason — it used to render after </section>, directly in <Load>'s section, where a comment-authored heading would push Arguments/Blocks down a level. Covered by a new sample + test: SignatureWithCommentHeadings has #### Element notes / #### Block usage in its comments; the test asserts the three signature headings stay h3, the comment h4s render contained in their sections, and no h5 appears (no drift).
Verification on 57ffca5: browser suites 6/6 turbo tasks (docs-app 30 pass / 5 skip / 0 fail, incl. both heading tests), test:node 75/75, pnpm lint 17/17 tasks. The rendered result is unchanged from the flattened markup: h2 API Reference → h3 Element / h3 Arguments / h3 Blocks.
…orm sibling <section> Rather than moving the headings out of the <section>s, restore them and make the structure uniform: each signature part (Element, Arguments, Blocks, Return) is a <section> containing its heading and its content. - Arguments gains the <section> it never had: a heading placed directly in <Load>'s section is level-context for the sibling <section>s after it, which would nest Blocks' heading one level deeper (h4 next to h3 siblings). As uniform sibling sections, all parts resolve to the same level (verified: pre-PR markup under ember-primitives 0.60.1 renders Element h3 / Arguments h3 / Blocks h4). - Element's comment moves inside its <section>, so headings authored in a declaration's markdown comment stay contained instead of becoming level-context for the parts that follow. - New sample (SignatureWithCommentHeadings) + rendering test cover exactly that: markdown headings in Element/Blocks comments render (contained in their sections) without shifting the three signature headings off h3. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Problem
The Element / Arguments / Blocks / Return headings in rendered signatures could violate axe's
heading-orderaudit ("Heading levels should only increase by one") on consuming docs pages — concretely, nvp.ui's/Docs/3-components/theme-togglefails with axe-core >= 4.12 (pulled in by no-lockfile CI installs): its signature has only anElement, whose heading rendered as an<h1>mid-page, so the markdown's### State Attributesthat follows was a 2-level jump. See NullVoxPopuli/nvp.ui#99 and its review discussion.Root cause (two layers)
<section>as a dead-end boundary and pinned any heading inside it to<h1>, and its fallback traversal leaked headings out of nested sectioning content. Both fixed in which-heading-do-i-need: nested sections are not heading context; heading-less boundaries are transparent ember-primitives#776, released as ember-primitives 0.60.1 (which-heading-do-i-need 0.4.1) — this PR bumps to^0.60.1.Element,Blocks, andReturneach wrapped in a<section>, butArgumentswas not. A heading placed directly in<Load>'s section is level-context for the sibling<section>s after it: per the sectioning rules, a<section>following a sibling<h3>is a subsection of it, so even with fixed detection the parts renderElement h3 / Arguments h3 / Blocks h4.Fix
No level numbers; sectioning rules only. Every signature part (Element, Arguments, Blocks, Return) is now a uniform sibling
<section>containing its heading and its content. Sibling sections resolve to equal levels — one below whatever heading precedes the api-docs block:Because each part's content lives inside its section, headings authored in a declaration's markdown comments stay contained instead of becoming level-context for the parts that follow (
Element's comment moved inside its section for that reason).Verification
<h2>before aComponentSignatureyields threeh3.typedoc__headings (Element, Arguments, Blocks) and no h1/h2/h4#### …) renders them contained in their sections — the three signature headings stayh3, and noh5appears (no drift)pnpm turbo test:browser(6/6 tasks, docs-app 30 pass / 5 skip / 0 fail),pnpm turbo test:node(75/75),pnpm lint(17/17 tasks: js, types, hbs, prettier, publint)kolaythere, restored its original### State Attributesmarkdown — full suite 83/83 (previously failingheading-orderon theme-toggle)🤖 Generated with Claude Code