Skip to content

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

Description

@pirate-bot

Summary

When a page's title is hidden and a Custom Layout supplies its own H1, the page ends up with two H1 headings. The second is Neve's visually hidden title.

Expected: only one H1 on the page, the one from the custom layout.
Actual: Neve outputs an extra H1 with the screen-reader-text class in addition to the custom layout H1.
Impact: SEO and heading-checker tools flag multiple H1s on the page.

Customer context

  • Product / area: Neve page header with Neve Pro Custom Layouts
  • Version: Not provided
  • Environment: Page using a custom layout with an H1 heading; the page title is hidden (inferred)
  • Integration / third party: Neve Pro custom layouts
  • Reported error / symptom: Two H1 headings reported on a page that uses a custom layout
  • Impact: SEO concern; customer is using a heading checker

Reproduction notes

Not reproduced. Likely steps (inference): 1) Hide the page title through the global setting or the per-page setting. 2) Add a Neve Pro Custom Layout that renders an H1. 3) Inspect the frontend HTML for two H1 elements. Missing: Neve and Neve Pro versions, and the exact custom layout hook.

Diagnosis

Conclusion

When the title is hidden, views/page-header.php outputs a visually hidden H1 unless it finds an H1 in the post's raw post_content. Markup injected by a custom layout or hook is not in post_content, so the check does not see it and a second H1 can render. This is inferred from reading the code; no runtime reproduction was done.

Where this likely occurs

  • views/page-header.php approx. lines 15-27 and 46-56 (hide-title decision and the else branch): $content_has_h1 is computed from get_post_field('post_content') only. If false, it echoes the screen-reader-text H1.
  • inc/views/page_header.php — Page_Header::run() line 43 and render_page_header() lines 53-67 hook the view into neve_page_header at priority 9.
  • The page the customer linked is not necessarily the blog page. The staff reply described the case as the blog/posts page, via is_home. The archive hide-title default at lines 21-23 of the view may be involved.

Engineering notes

The H1 check looks only at post content. It does not look at output from other sources such as Neve Pro custom layouts hooked into the page header. Neve Pro was only grepped for neve_page_header usage; the custom layout rendering code was not inspected in depth. Custom layouts may also inject at other hooks.

Test coverage status

No relevant coverage was found during inspection (not exhaustive).

What to verify or explore next

  • Reproduce with a Custom Layout containing an H1 on the posts page and on a regular page, with the title hidden, and count the H1 elements.
  • Check whether archive and blog pages hide the title by default for new users via neve_archive_hide_title.

Unknowns / follow-up

  • Theme and Pro versions, and the hook where the customer's layout renders.
  • Whether an open issue already tracks this.

Confidence

Confidence: 70/100

The hidden-title branch in views/page-header.php only checks the post's raw content for an H1. A custom layout that outputs its own H1 is not detected, so a second H1 renders. No existing GitHub issue was searched, and I did not inspect customer-side layout markup.


Source: HelpScout #3479313350
Generated by bug-report-triage (ID: bug-report-triage_6acb421c5ba113.05905873)

Activity

  1. added theissue type on Oct 11, 2026
  2. added a commit that references this issue on Oct 15, 2026
    1f278cc
  3. milicavs commented on Oct 17, 2026

    @milicavs

    🧪 QA steps and test links

    When a page's title is hidden, Neve adds a visually hidden H1 with the page title so the page still has a main heading. On Neve 4.2.17 it only looks for another H1 in the page's own content, so an H1 that comes from somewhere else (a synced pattern, a Neve Pro Custom Layout, the blog page) is missed and the page ends up with two H1s, which SEO and heading checkers flag.

    Link Build Bot check
    Reproduce the bug Neve 4.2.17 (released) ✅ Reproduced: two H1s, h1.screen-reader-text "Hidden title page" plus the pattern's "Heading from a synced pattern"
    Verify the fix PR #4658 build 0fe4373 ✅ Fixed: one H1, the pattern's heading only

    In the links:

    • Steps 1 to 3 are already done, and the links open on the page from step 3 on the front end.
    • The front page shows the latest posts.
    • Tools → QA Logs shows debug.log.

    Reproduce on Neve 4.2.17:

    1. Patterns → Add New Pattern: name it "QA heading pattern", turn Synced on, add a Heading block set to H1 ("Heading from a synced pattern") and publish.
    2. Pages → Add New Page "Hidden title page": insert the synced pattern and a paragraph.
    3. In the editor's Neve options sidebar (Neve icon, top right), turn on Disable Title, then publish and view the page.
    4. In the browser console, run document.querySelectorAll('h1') (or use a heading checker extension).
    • Bug: 2 H1s: a hidden h1.screen-reader-text with the page title, plus the pattern's H1.
    • Fixed: 1 H1, the pattern's heading. No screen-reader-text H1.
    Edge cases
    • Hidden title, no H1 anywhere on the page: exactly one h1.screen-reader-text with the page title.
    • Hidden title, H1 Heading block typed straight into the page content: no screen-reader H1.
    • Hidden title, the page's only "H1" sits inside a <style>, <textarea> or <script> in a Custom HTML block: the screen-reader H1 stays.
    • Hidden title, the page's only "H1" is text inside an attribute (Custom HTML block <div data-tpl="<h1>x</h1>"><p>Text</p></div>, also with a custom tag like <my-widget>): the screen-reader H1 stays.
    • Hidden title, the page's only H1 sits inside an inline <svg> (for example in its <title> or <style>) in a Custom HTML block: the browser shows it as a real H1, so no screen-reader H1.
    • Settings → Reading with a static front page and a posts page, posts page title hidden: the posts page has exactly one H1 (with Neve Pro, add a Custom Layout with an H1 on the before-loop hook: still one).
    • With Neve Pro: a Custom Layout with an H1 on the before-content, after-header or before-footer hook, or an inside-content layout: no screen-reader H1.

    Fix: #4658 (open)

  4. rodica-andronache commented on Oct 22, 2026

    @rodica-andronache
    Contributor
  5. pirate-bot commented on Oct 23, 2026

    @pirate-bot
    CollaboratorAuthor

    QA agent — 🔴 Has problems · Custom Layout duplicates are fixed, but heading-parser edge cases still fail.

    With licensed Neve Pro fixtures, the reported Custom Layout, posts-page and synced-pattern duplicates are fixed. A 35-case rendered-heading matrix still found three missing-heading cases around SVG/MathML integration points and two duplicate-heading cases after unusual script/style end tags. The missing headings also occur on the base build; the end-tag duplicates are new. The PR’s 52 PHP tests and added synced-pattern browser test pass, but do not cover these failures.

    Problems found

    • ❌ A title-hidden page with no rendered H1 retains exactly one h1.screen-reader-text containing its page title, including when H1-like text occurs only in inert markup.
      Ordinary fallback and 21/24 inert-markup cases pass. Three title-hidden pages have zero actual DOM H1s and zero fallback: <title><style>

      Not heading

      </style></title>, <style>

      Not heading

      </style>, and

      Not heading

      . Chromium treats these strings as raw text or inert template content, so the req
      🐞 Existing bug: also fails on the base build. Not reported yet.

      A title-hidden page with no rendered H1 retains exactly one h1.screen-reader-tex

    • ❌ A title-hidden page with an H1 block directly in its content keeps the content heading as its only H1 and adds no screen-reader H1.
      The ordinary direct H1 and 9/11 real-heading boundary cases pass. Both extraneous-end-tag-attribute fixtures render two actual H1s on HEAD: the hidden page-title fallback and 'QA Matrix Real H1' from the core Heading block. Chromium closes script/style despite the extra attribute. Both fixtures render only the content H1 on qa-base; the same replay fails after restoring HEAD. This is a new duplica

      A title-hidden page with an H1 block directly in its content keeps the content h

    Worth a human look

    1. Repeat the customer’s exact layout hook and template once supplied; neither was included in the issue.
    Checks (4 passed)
    • ✅ A title-hidden regular page with a Neve Pro Custom Layout H1 renders exactly that one H1 and no screen-reader fallback, for before-content, after-header, before-footer and inside-content layouts.
    • ✅ With a static front page and a title-hidden posts page, a Neve Pro Custom Layout on neve_before_loop supplies the posts page's only H1.
    • ✅ A title-hidden page whose H1 comes from a synced pattern renders only the pattern heading and no screen-reader H1.
    • ✅ A page with its title left visible renders the normal title H1 and no fallback placeholder comment in the source.

    Full report, evidence and session trail

  6. pirate-bot commented on Oct 23, 2026

    @pirate-bot
    CollaboratorAuthor

    QA agent — Testing could not finish. No automatic attempts remain; human QA is required.

    Review the blocked observations and their requirement sources before treating them as product defects. Confirmed checks and saved evidence are included below.

    Checks (6 passed · 1 not tested)
    • ✅ Hiding a regular page title leaves exactly one H1 supplied by a Neve Pro Custom Layout, with no screen-reader H1, for before-content, after-header, before-footer and inside-content layouts.
    • ✅ A configured posts page with its title hidden and a Neve Pro Custom Layout H1 on neve_before_loop renders exactly one H1 from the layout.
    • ✅ A hidden-title page with no other rendered H1 retains exactly one h1.screen-reader-text containing its page title.
    • ✅ A hidden-title page with a direct H1 Heading block renders that heading once and no screen-reader H1.
    • ✅ A hidden-title page whose H1 comes from a synced pattern renders only the pattern heading and no screen-reader H1.
    • ✅ When a page title remains visible, its normal H1 renders and no fallback placeholder comment appears in the response source.
    • ⏸️ The hidden-title fallback counts real rendered H1s rather than H1-like text in comments, raw-text elements, attributes or inert templates, including large scripts and SVG/MathML integration points.

    Full report, evidence and session trail

  7. added a commit that references this issue on Oct 27, 2026
    40633ba
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions