From f9dbfaa84b46ff1e4aecce7a5872687ef006fec4 Mon Sep 17 00:00:00 2001 From: Brian Donovan Date: Fri, 14 Aug 2026 07:48:25 -0700 Subject: [PATCH] Fix invalid HTML nesting flagged by validateDOMNesting MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Ten product sites wrapped block-level content in a `

`: `

` is widely used as a spacing wrapper, and handing it a `SearchSelect`, `Callout`, `ul`, or another `

` produces invalid DOM. `P` now accepts `as="div"` (mirroring the existing `HeadingProps` `as`, as in `

`), 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 `

`; that surfaced sites React never reported, including the `

`-in-`

` 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. `` was byte-identical across all 99 captures, and the only edits anywhere were `

` to `

` 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 `

` 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 `

` 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 `

`'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) --- .../frontend/src/screens/settings_screen.tsx | 2 +- apps/design/frontend/src/districts_screen.tsx | 2 +- apps/design/frontend/src/proofing_status.tsx | 4 ++-- apps/mark-scan/frontend/src/pages/admin_screen.tsx | 2 +- apps/mark/frontend/src/pages/admin_screen.tsx | 2 +- apps/mark/frontend/src/pages/test_deck_screen.tsx | 4 ++-- .../screens/internal_connection_problem_screen.tsx | 2 +- .../components/poll_worker/ballot_style_select.tsx | 2 +- libs/ui/src/typography.tsx | 12 +++++++++++- 9 files changed, 21 insertions(+), 11 deletions(-) diff --git a/apps/central-scan/frontend/src/screens/settings_screen.tsx b/apps/central-scan/frontend/src/screens/settings_screen.tsx index 9dcc9040235..27684792b9b 100644 --- a/apps/central-scan/frontend/src/screens/settings_screen.tsx +++ b/apps/central-scan/frontend/src/screens/settings_screen.tsx @@ -85,7 +85,7 @@ export function SettingsScreen({ )}

Polling Place

-

+

) : ( -

+

Are you sure you want to delete the following districts?{' '} This will delete all contests associated with these diff --git a/apps/design/frontend/src/proofing_status.tsx b/apps/design/frontend/src/proofing_status.tsx index 9209e1234a4..f8fe58f3c3f 100644 --- a/apps/design/frontend/src/proofing_status.tsx +++ b/apps/design/frontend/src/proofing_status.tsx @@ -230,7 +230,7 @@ export function ProofingStatus(): React.ReactNode { Unfinalizing will clear exports and reenable editing. Enter a reason for unfinalizing.

-

+