Skip to content

feat(seo): add h1 + BreadcrumbList and enrich BlogPosting schema on blog posts / 博客文章增加 h1 与 BreadcrumbList 并丰富 BlogPosting 结构化数据#599

Merged
Oseltamivir merged 3 commits into
masterfrom
seo/blog-h1-schema
Jul 20, 2026
Merged

feat(seo): add h1 + BreadcrumbList and enrich BlogPosting schema on blog posts / 博客文章增加 h1 与 BreadcrumbList 并丰富 BlogPosting 结构化数据#599
Oseltamivir merged 3 commits into
masterfrom
seo/blog-h1-schema

Conversation

@Oseltamivir

@Oseltamivir Oseltamivir commented Jul 20, 2026

Copy link
Copy Markdown
Contributor

Summary

Fixes two on-page / structured-data SEO gaps on blog posts.

1. Blog posts had no <h1>. The post title rendered as <h2>, and the MDX renderer maps markdown #<h2> too — so posts shipped with no top-level heading carrying the primary keyword.

  • Post title <h2><h1> (identical styling), on both EN and /zh pages.
  • Exactly-one-<h1> verified: MDX mapper still maps #/##<h2> (bodies emit no <h1>); no other <h1> in the blog subcomponents, layouts, header/footer; the Cypress post test now asserts cy.get('h1') has length 1 and contains the title.

2. BlogPosting JSON-LD was missing recommended Article fields, and posts had no BreadcrumbList (compare/glossary pages have one; blog posts didn't).

  • BlogPosting gains image (per-post OG image URL), inLanguage (en-US / zh-CN), mainEntityOfPage, and publisher.logo (ImageObject, 1200×675).
  • New BreadcrumbList (Home → Blog → post) emitted as a separate JSON-LD block, matching the compare-page pattern. /zh translated (首页 / 博客).
  • Extracted pure builders buildBlogPostingJsonLd, buildBlogBreadcrumbJsonLd, buildBlogBreadcrumbJsonLdZh, blogOgImageUrl into blog.ts so they are unit-testable.

Testing

  • pnpm typecheck ✅ · pnpm lint ✅ · pnpm fmt
  • pnpm test:unit: +20 new passing tests for the JSON-LD builders, 0 regressions. (Same 13 pre-existing, unrelated clean-tree failures as noted elsewhere — not touched here.)
  • Single-<h1> guarantee covered by an updated Cypress e2e assertion (the blog page is an async RSC using compileMDX/fs/shiki, which the node-env vitest harness can't render).

Overlay support

Not applicable — blog-post on-page headings and structured data, no inference/evaluation chart-data path involved.

中文说明

修复博客文章的两处页面内 / 结构化数据 SEO 问题。

1. 博客文章此前没有 <h1> 文章标题以 <h2> 渲染,且 MDX 渲染器把 Markdown # 也映射为 <h2>,因此文章缺少承载主关键词的顶级标题。

  • 文章标题由 <h2> 改为 <h1>(样式不变),EN 与 /zh 两侧同步。
  • 已验证页面仅有一个 <h1>:MDX 映射器仍把 #/## 映射为 <h2>(正文不产生 <h1>),博客子组件、布局、页头/页脚均无 <h1>;Cypress 文章测试现在断言 cy.get('h1') 数量为 1 且包含标题。

2. BlogPosting JSON-LD 缺少推荐的 Article 字段,且文章没有 BreadcrumbList(compare/glossary 页面有,博客文章没有)。

  • BlogPosting 新增 image(每篇文章的 OG 图片 URL)、inLanguageen-US / zh-CN)、mainEntityOfPagepublisher.logo(ImageObject,1200×675)。
  • 新增 BreadcrumbList(首页 → 博客 → 文章),作为独立 JSON-LD 块输出,与 compare 页面模式一致;/zh 侧翻译为“首页 / 博客”。
  • 将纯函数构建器 buildBlogPostingJsonLdbuildBlogBreadcrumbJsonLdbuildBlogBreadcrumbJsonLdZhblogOgImageUrl 抽取到 blog.ts 以便单元测试。

测试:pnpm typecheckpnpm lintpnpm fmt 均通过;pnpm test:unit 新增 20 个通过用例,无回归。单一 <h1> 保证由更新后的 Cypress 端到端断言覆盖(博客页面是使用 compileMDX/fs/shiki 的异步 RSC,node 环境的 vitest 无法渲染)。叠加层(overlay)支持:不适用。


Note

Low Risk
SEO and markup-only changes on blog pages with no auth, data, or API behavior changes; risk is limited to heading semantics and structured-data shape.

Overview
Blog post pages now use a single <h1> for the post title (EN and /zh), replacing the previous <h2>, so the primary keyword sits in the page’s top heading while MDX body headings still render as <h2> and below. Cypress asserts exactly one h1 containing the title.

Structured data moves out of the page components into shared blog.ts helpers: BlogPosting JSON-LD gains image, inLanguage, mainEntityOfPage, and publisher.logo, and a separate BreadcrumbList block (Home → Blog → post, with a Chinese mirror) is emitted on both locales. 20 unit tests cover the builders.

Reviewed by Cursor Bugbot for commit 10f1b9a. Bugbot is set up for automated code reviews on this repo. Configure here.

…log posts

Render the post title as a real <h1> (was <h2>) so each post carries a single
top-level heading with its primary keyword; MDX body content keeps mapping
markdown headings to <h2>, guaranteeing exactly one <h1> per page.

Extract the BlogPosting JSON-LD into a pure, unit-testable builder and enrich it
with the recommended Article fields: image (post OG image URL), inLanguage,
mainEntityOfPage, and publisher.logo (ImageObject). Add a separate
BreadcrumbList JSON-LD (Home -> Blog -> post) to posts, mirroring the compare
pages. All applied to both the English and /zh post pages, with translated
breadcrumb labels and zh-CN language/URLs on the Chinese side.

中文:将博客文章标题渲染为真正的 <h1>(原为 <h2>),使每篇文章拥有唯一的顶级标题并承载主关键词;
MDX 正文标题仍映射为 <h2>,从而保证每页只有一个 <h1>。将 BlogPosting 结构化数据抽取为纯函数、
可单元测试的构建器,并补齐推荐的 Article 字段:image(文章 OG 图片 URL)、inLanguage、
mainEntityOfPage 与 publisher.logo(ImageObject)。同时为文章新增独立的 BreadcrumbList
结构化数据(首页 -> 博客 -> 文章),与 compare 页面保持一致。以上改动同时应用于英文与 /zh 文章页,
中文侧使用翻译后的面包屑标签及 zh-CN 语言标记与 URL。
@Oseltamivir
Oseltamivir requested a review from adibarra as a code owner July 20, 2026 08:55
@vercel

vercel Bot commented Jul 20, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
inferencemax-app Ready Ready Preview, Comment Jul 20, 2026 9:37am

Request Review

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 2c296e4. Configure here.

Comment thread packages/app/src/lib/blog.ts Outdated
Cursor Bugbot flagged the BlogPosting publisher.logo height (675) as
inconsistent with the codebase. The OG_IMAGE asset is genuinely 1200×675
(verified via sips), but the root layout declares it 1200×630 and its
Organization logo omits dimensions entirely. Rather than assert a number
that conflicts with the root layout, omit the optional width/height and
match the existing Organization-logo precedent in layout.tsx.

中文:修复博客 JSON-LD 中 publisher.logo 尺寸不一致的问题。Cursor Bugbot 指出
logo 声明的高度(675)与代码库其他处不一致:OG_IMAGE 实际为 1200×675(经 sips
验证),但根布局声明为 1200×630,且其 Organization logo 未声明尺寸。为避免与根布局
冲突,这里省略可选的 width/height,与 layout.tsx 中 Organization logo 的既有做法保持一致。
#598 (compare/blog metadata CTR) merged to master first. Reconciled the
overlapping blog-post changes:
- Kept #599's builder-based JSON-LD (buildBlogPostingJsonLd + BreadcrumbList)
  and the <h1>, alongside #598's title.absolute + blogDescription metadata.
- Routed buildBlogPostingJsonLd's description through blogDescription so
  #598's structured-data/SERP consistency fix survives the refactor.
- Unioned the imports and the blog.test.ts helper imports; updated the
  BlogPosting description assertion to track blogDescription.

中文:#598(compare/博客元数据点击率优化)先行合入 master,这里协调两者对博客文章
的重叠改动:保留 #599 基于构建器的 JSON-LD(buildBlogPostingJsonLd 与
BreadcrumbList)及 <h1>,同时保留 #598 的 title.absolute 与 blogDescription 元数据;
让 buildBlogPostingJsonLd 的 description 走 blogDescription,以在重构后延续 #598 的
结构化数据与搜索摘要一致性修复;合并相关 import 与 blog.test.ts 的辅助函数导入,并将
BlogPosting 的 description 断言改为跟随 blogDescription。
@Oseltamivir
Oseltamivir merged commit 667baac into master Jul 20, 2026
23 checks passed
@Oseltamivir
Oseltamivir deleted the seo/blog-h1-schema branch July 20, 2026 09:47
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