Skip to content

Release - #4676

Open
pirate-bot wants to merge 4 commits into
masterfrom
development
Open

Release#4676
pirate-bot wants to merge 4 commits into
masterfrom
development

Conversation

@pirate-bot

@pirate-bot pirate-bot commented Oct 27, 2026 •

Copy link
Copy Markdown
Collaborator

Linked issues

This release will close the following issues once merged:

Public changelog

  • Fixed duplicate H1 headings on pages with hidden titles.
  • Updated dependencies

pirate-bot and others added 3 commits October 21, 2026 22:57
…ewhere (#4658)

* fix: avoid a duplicate H1 when a hidden title's page provides one elsewhere

The screen-reader H1 fallback only scanned post_content, so an H1 from a
synced pattern, a page builder or a Neve Pro custom layout produced a
second H1. The main area is now buffered and the fallback is kept only
when no other H1 rendered.

Fixes #4652

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* fix: buffer the whole body for the hidden-title H1 decision

Start at wp_body_open and resolve at wp_footer so header and footer
custom layouts count, ignore H1s inside HTML comments, and strip
scripts before comments so a lone "<!--" in a script cannot swallow
real markup. Tests now cover header/footer hooks, patterns, comments,
and each fails on the base tree.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* fix: span the hidden-title H1 buffer across the theme body hooks

Open on neve_body_start_after (wp_body_open as fallback) and resolve on
neve_body_end_before so custom layouts on the body start and end hooks
count. Tests: one data-provider case per body hook, a stray open
buffer, the wp_body_open fallback, and a placement test that fails on
the base tree.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* test: make the hidden-title H1 tests order-independent and tighter

The bootstrap assigns user 1 directly, so wp_set_current_user() left
kses on and stripped the noscript fixture when the class ran alone.
Assert the exact buffer level in the stray-buffer case and reuse
render_page() for the wp_body_open-only render.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* fix: scan the body for an H1 without a regex that can exhaust the backtrack limit

The lazy regex that stripped script, template, noscript and comment
blocks gave up on an inline script larger than pcre.backtrack_limit
(1 MB by default). preg_replace returned null, the unstripped body was
scanned instead, and a literal "<h1>" inside the script counted as a
real heading, so a hidden-title page lost its only H1. Found by QA on
issue #4652; the same scan miscounted on the base tree.

Replace it with a single left-to-right pass over the markup that skips
comments and the raw-text elements by string search, which is linear
and has no limit. Tests: the 1.2 MB script fixture plus a data provider
for the comment and raw-text edge cases.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* fix: drop the ctype dependency from the closing-tag search

ctype_space() needs ext-ctype, which WordPress only recommends. Use
strspn() over the HTML whitespace set instead.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* fix: treat template contents as nested markup in the H1 scan

Browsers parse <template> contents as markup, so a nested template, or
a comment or script holding "</template>", must not close the outer one.
The scan now tracks template depth in its main loop and ignores H1s
inside it; find_closing_tag() stays a raw-text search for script and
noscript. Found by QA on issue #4652.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* test: drop two template scan rows that pass without the fix

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* fix: skip attribute values in the H1 scan

Literal tag text inside a quoted attribute was read as markup, so a
"<template>" in a data attribute hid the real H1 and an "<h1>" in one
dropped the fallback. The scan now skips to the end of each tag,
honouring quoted values, before looking for the next tag. Found by QA
on issue #4652.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* fix: recognise custom element tags in the H1 scan

The tag-name match only allowed letters and digits, so a hyphenated
custom element was not seen as a tag and its attribute text was read
as markup. Match the name up to whitespace, slash or ">" as the HTML
tokenizer does. Found by QA on issue #4652.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* fix: ignore an H1 tag cut off by the end of input

Browsers drop a tag the input ends inside, so count an H1 only once
its tag end is found.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* fix: skip every raw-text element in the H1 scan

Browsers never parse tags inside style, textarea, title, xmp, iframe,
noembed or noframes either, so H1 text there dropped the fallback.
Found by QA on issue #4652.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* fix: apply foreign-content rules inside svg and math in the H1 scan

Inside <svg> and <math>, title and style are ordinary elements, tags
can self-close and an <h1> breaks out as HTML, so a self-closing SVG
<title/> must not start a raw-text skip. <foreignObject> restores the
HTML rules.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* test: cover each branch of the foreign-content tracking in the H1 scan

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* fix: honour end-tag attributes and all integration points in the H1 scan

Browsers close script and style on an end tag that carries attributes
or a stray slash, so the closer search now accepts those forms. Inside
svg and math, title, desc and the MathML text elements restore the
HTML rules like foreignObject does; a context stack replaces the two
counters so an HTML <title> under an integration point stays raw text.
Found by QA on issue #4652.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* fix: close integration points in the H1 scan's context stack

A closing title, desc, foreignObject or MathML text tag never left the
stack, so HTML rules outlived the element and an <h1> that breaks out
of the following foreign content was skipped.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
@pirate-bot

pirate-bot commented Oct 27, 2026 •

Copy link
Copy Markdown
Collaborator Author

Plugin build for 4193ab0 is ready 🛎️!

Bumps [codeinwp/themeisle-sdk](https://github.com/Codeinwp/themeisle-sdk) from 3.3.67 to 3.3.68.
- [Release notes](https://github.com/Codeinwp/themeisle-sdk/releases)
- [Commits](Codeinwp/themeisle-sdk@v3.3.67...v3.3.68)

---
updated-dependencies:
- dependency-name: codeinwp/themeisle-sdk
  dependency-version: 3.3.68
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

This branch was successfully deployed

1 active deployment
staging — 4193ab0e Deployed Oct 27, 2026 by pirate-bot
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.

Duplicate H1: hidden screen-reader title H1 rendered alongside custom layout H1 when page title is hidden

2 participants