Skip to content

Fix invalid HTML nesting flagged by validateDOMNesting - #9156

Draft
eventualbuddha wants to merge 1 commit into
mainfrom
test-noise
Draft

Fix invalid HTML nesting flagged by validateDOMNesting#9156
eventualbuddha wants to merge 1 commit into
mainfrom
test-noise

Conversation

@eventualbuddha

@eventualbuddha eventualbuddha commented Aug 14, 2026

Copy link
Copy Markdown
Contributor

Overview

Ten product sites wrapped block-level content in a <p>, which React flags as validateDOMNesting noise on every test run. libs/ui's P now accepts as="div" — mirroring the existing <H2 as="h1"> idiom — and the offending sites use it. Because as reuses the same styled component, the generated class and emitted CSS are unchanged.

See the commit for the audit method, validation, and alternatives considered.

Demo Video or Screenshot

No visual change — that's the point. Before/after DOM and Chromium rendering are identical; details in the commit message.

Testing Plan

  • All 11 jsdom suites pass with identical test counts (2922 passed, 2 skipped) and zero validateDOMNesting output. Note that vitest.config.shared.mts sets reporters: [] outside CI, which suppresses console output — reproducing this locally needs --reporter=default.
  • A temporary DOM audit (patching appendChild/insertBefore to record any flow-content element inserted under a <p>) records zero hits across all jsdom packages after the fix, and found sites React's deduped warning never reported.
  • Full-document captures (including the injected styled-components stylesheet) from the affected tests, before vs after: <head> byte-identical across 99 captures; the only edits anywhere are <p …><div …> with identical attributes.
  • 61 of those pages replayed in Chromium with the before-DOM rebuilt via DOM APIs: zero of 5228 elements changed geometry or any of 35 computed CSS properties, and residual pixel deltas exactly equal a provably no-op div-to-div re-creation control.
  • lint (which type-checks) clean on all 13 packages that use P.

Checklist

  • I have prefixed my PR title with "VxDesign: ", "VxPollBook: ", or "HWTA: " if my change is specific to one of those products.
  • I have added logging where appropriate for any new user actions.
  • I have added the "user-facing-change" label to this PR, if relevant, to automate an announcement in #machine-product-updates.

🤖 Generated with Claude Code

Ten product sites wrapped block-level content in a `<p>`: `<P>` is widely used as a spacing wrapper, and handing it a `SearchSelect`, `Callout`, `ul`, or another `<P>` produces invalid DOM. `P` now accepts `as="div"` (mirroring the existing `HeadingProps` `as`, as in `<H2 as="h1">`), and the offending sites use it. `StyledP` overrides every UA `p` style, and `as` reuses the same styled component, so the generated class and emitted CSS are unchanged.

React dedupes this warning per test file by `ancestorTag|childTag`, so the 13 reported warnings were really 10 distinct sites. To find them all I temporarily patched `Node.prototype.appendChild`/`insertBefore` in every jsdom package's setup to record any flow-content element inserted under a `<p>`; that surfaced sites React never reported, including the `<P>`-in-`<P>` in `internal_connection_problem_screen`. Post-fix the audit records zero.

Validation, beyond all 11 jsdom suites staying green with identical test counts and zero `validateDOMNesting` output: captured the full document (including the injected styled-components stylesheet) from the affected tests before and after. `<head>` was byte-identical across all 99 captures, and the only edits anywhere were `<p …>` to `<div …>` with identical attributes plus the matching close tags. Then replayed 61 of those pages in Chromium. The before-DOM can't be screenshotted from a file — the HTML parser auto-closes the invalid `<p>` and hoists the block out, which React's client-side DOM construction never does — so each page loads the valid after-DOM and rebuilds the before-DOM in place via `createElement`/`appendChild`/`replaceChild`, asserting the reconstruction re-serializes to exactly the captured before-DOM (61/61 exact). Result: zero of 5228 elements changed geometry or any of 35 computed CSS properties, and the residual pixel deltas exactly equal a div-to-div re-creation control, i.e. rasterization noise rather than an effect of the tag change.

Alternatives considered: hoisting the block content out of the `<P>` as a sibling is semantically cleaner but changes spacing at nearly every site (flex gap in `ballot_style_select`, UA `ul` margins in `districts_screen`, the inner `<P>`'s margin in the two nested cases). A new div-based libs/ui component duplicates `StyledP`'s styles and emits a second class for no behavioral gain. Making `StyledP` render a `div` outright is a one-liner but drops the `paragraph` ARIA role from every paragraph in the repo, including voter-facing screens, and would need an audit of the existing `p` type selectors.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant