1. Metadata Structure
What every page must do
// 每个 page.tsx 顶部
import type { Metadata } from 'next'
import { getSiteConfig } from '@/lib/site'
import { generateSiteMetadata } from '@/lib/seo' // 来自共享包
const site = getSiteConfig()
export const metadata: Metadata = generateSiteMetadata(site, { pageTitle: 'Page Title — Brand Name (2026)', pageDescription: 'Description text 150-155 characters, containing the target keyword.', canonicalPath: '/page-path', robots: { index: true, follow: true }, // Append keywords (not required, but helps with long-tail) keywords: [...(site.keywords || []), 'Additional long-tail keyword 1', 'Additional long-tail keyword 2'], })
### Metadata 规则
| 项 | 规则 |
|---|---|
| **pageTitle** | 含品牌名 `MECCHA CHAMELEON`,不拼错。格式:`主题 — 子主题` (em dash,前后空格) |
| **pageDescription** | **150-155 字符**。一段完整的描述,含 2-3 个目标关键词。不要截断、不要清单 |
| **canonicalPath** | 必填。从 `/` 开始,如 `/platforms`。空字符串 `''` 表示首页 |
| **robots** | 内容页 `{ index: true, follow: true }`,法律页 `{ index: false, follow: false }` |
| **keywords** | 只在 `/platforms`、`/buy`、`/requirements` 等需要大量长尾词覆盖的页面追加 |
### 首页(根 page.tsx)
首页也需要单独的 `metadata` 导出——不要只依赖 layout.tsx 默认值。
```tsx
import { generateSiteMetadata } from '@/lib/seo'
const site = getSiteConfig()
export const metadata: Metadata = generateSiteMetadata(site, {
pageTitle: 'Paint Yourself Invisible | Hide-and-Seek Party Game (2026)',
pageDescription: 'MECCHA CHAMELEON is a creative hide-and-seek party game on Steam (PC). Paint to blend in. $5.99, Very Positive, 2-10 players. Tips, requirements, and guide.',
canonicalPath: '',
robots: { index: true, follow: true },
})
Legal pages (privacy, tos, disclaimer)
Must use generateSiteMetadata, do not hand-write a raw Metadata object. Otherwise it will be missing the canonical URL, OG, and Twitter Card.
import { getSiteConfig } from '@/lib/site'
import { generateSiteMetadata } from '@/lib/seo'
const site = getSiteConfig()
export const metadata: Metadata = generateSiteMetadata(site, {
pageTitle: 'Privacy Policy',
pageDescription: 'Privacy policy for MECCHA CHAMELEON guide. Data collection, cookies, and your rights.',
canonicalPath: '/privacy',
robots: { index: false, follow: false },
})
layout.tsx root configuration
export const metadata: Metadata = generateSiteMetadata(site, {
pageTitle: 'Paint Yourself Invisible | Hide-and-Seek Party Game (2026)',
})
2. Title & Description Quality Check
Title Format
✅ MECCHA CHAMELEON Platforms — PS5, Mac, Mobile & PC Support (2026)
❌ MECCA CHAMELEON Platforms —PS5, Mac, Mobile & PC Support (2026)
❌ MECCHA CHAMELEON Platforms —PS5... // em dash 后缺空格
Rules:
- Brand name spelled correctly (MECCHA not MECCA)
- em dash(
—) with one space before and after - Append
(2026)year - Length 50-65 characters
Description Length
| Status | Character count |
|---|---|
| ✅ Ideal | 150-155 |
| ⚠️ Acceptable | 145-160 |
| ❌ Too long | >165 (Google will truncate) |
| ❌ Too short | <100 (wastes exposure opportunity) |
Trimming techniques:
原本: "MECCHA CHAMELEON tips and tricks —best camouflage techniques ranked S to B, Seeker search strategies, common beginner mistakes, and how to find hidden players. Master both roles with pro strategies." (168 chars)
裁剪: "MECCHA CHAMELEON tips and tricks — ranked camouflage techniques S to B, Seeker strategies, beginner mistakes, and how to find hidden players. Master both roles." (152 chars)
Key: delete redundant adverbs, merge similar items, remove unnecessary modifiers.
3. JSON-LD Structured Data
Page -> Schema Mapping Table
| Page type | JSON-LD Schema | Required fields |
|---|---|---|
| Homepage | VideoGame + WebSite |
aggregateRating, offers, operatingSystem |
| Reviews | Review + VideoGame |
reviewRating, itemReviewed, aggregateRating |
| FAQ | FAQPage(multiple Question/Answer entries) |
At least 5 Q&A pairs |
| Buying guide | Product + Offer |
offers.price, offers.availability |
| Platform/need/mode | VideoGame |
aggregateRating, offers, operatingSystem |
| All content pages | BreadcrumbList |
via generateBreadcrumbJsonLd auto-generated |
VideoGame JSON-LD Complete Version
Don't only write name + url + description。aggregateRating and offers are the most important—they trigger star ratings and price tags in search results.
<script
type="application/ld+json"
dangerouslySetInnerHTML={{
__html: JSON.stringify({
'@context': 'https://schema.org',
'@type': 'VideoGame',
name: data.name,
url: 'https://meccha-chameleon.info/classes',
description: '页面描述',
operatingSystem: 'Windows 10/11 64-bit',
applicationCategory: 'GameApplication',
aggregateRating: {
'@type': 'AggregateRating',
ratingValue: data.rating, // 4.3
bestRating: data.ratingMax, // 5
ratingCount: data.ratingCount, // 16348
},
offers: {
'@type': 'Offer',
price: '5.99',
priceCurrency: 'USD',
availability: 'https://schema.org/InStock',
url: data.steamUrl,
},
}),
}}
/>
FAQPage JSON-LD
Content pages are accordion <details>, JSON-LD needsall Q&A pairs:
<script
type="application/ld+json"
dangerouslySetInnerHTML={{
__html: JSON.stringify({
'@context': 'https://schema.org',
'@type': 'FAQPage',
mainEntity: data.faqs.map((faq) => ({
'@type': 'Question',
name: faq.q,
acceptedAnswer: {
'@type': 'Answer',
text: faq.a, // 纯文本,不要含 HTML 标签
},
})),
}),
}}
/>
Note: acceptedAnswer.text do not include <a> tags or HTML. Schema parsers prefer plain text.
4. Sitemap & Robots
sitemap.ts
export default function sitemap(): MetadataRoute.Sitemap {
const site = getSiteConfig()
const baseUrl = `https://${site.domain}`
const mainPages = ['', '/guide', '/review', '/faq', '/classes', '/tier', '/items', '/platforms', '/buy', '/requirements']
const legalPages = ['/privacy', '/tos', '/disclaimer']
const entries: MetadataRoute.Sitemap = [
...mainPages.map(path => ({
url: `${baseUrl}${path}`,
changeFrequency: (path === '' ? 'weekly' : 'monthly') as 'weekly' | 'monthly',
priority: path === '' ? 1.0 : 0.8,
})),
...legalPages.map(path => ({
url: `${baseUrl}${path}`,
changeFrequency: 'monthly' as const,
priority: 0.3,
})),
]
return entries
}
Principles:
- Homepage priority 1.0, content pages 0.8, legal pages 0.3
/jump(redirect intermediate pages) are not added to the sitemap- When adding new pages, update in sync
mainPagesarray
robots.ts
export default function robots(): MetadataRoute.Robots {
const site = getSiteConfig()
return {
rules: {
userAgent: '*',
allow: '/',
disallow: ['/jump', '/api/'],
},
sitemap: `https://${site.domain}/sitemap.xml`,
}
}
5. Internal Link Network
Link Density Requirements
| Page type | Minimum number of outbound links (pointing to other pages within the site) |
|---|---|
| Homepage | At least 5 |
| Guide-type pages | At least 3 (pointing to other guide pages) |
| Buying/platform/need pages | At least 3 |
| FAQ | At least 6 (covering all content pages) |
| Legal pages | 1-2 is enough |
Guide page cross-linking pattern
Each guide page (guide/classes/items/tier) adds a navigation bar before the bottom CTA:
<section className="section" style={{ padding: '2rem 0' }}>
<div className="container">
<div style={{ maxWidth: '800px', margin: '0 auto' }} className="reveal">
<p style={{ textAlign: 'center' }}>
<strong>Related guides:</strong>{' '}
<a href="/guide">Beginner's Guide</a> —
<a href="/classes">Game Modes</a> —
<a href="/items">Camouflage Guide</a>
</p>
</div>
</div>
</section>
FAQ internal links
In the FAQ's a answer text, embed links pointing to the corresponding detailed pages:
"MECCHA CHAMELEON is currently available exclusively on PC via Steam. See our
<a href="/platforms">Platform Availability Guide</a>for full breakdown."
Six, Keyword-driven page planning
Find content gaps from search data
From CSV exported from Google Search Console / keyword tools, extract:
- High search volume + 0 coverage → need to create new pages
- High growth (↑%) + low coverage → need to strengthen existing pages
Example priority matrix:
| Keyword | Search volume | Growth | Action |
|---|---|---|---|
| ps5, mac, mobile | 20+ | 20%+ | Create /platforms |
| steam key | 5+ | 30%+ | Create /buy |
| cloud save error | 1+ | 160%+ | Strengthen /requirements |
| tips, how to play | Consistent | Stable | Already covered ✅ |
Seven, Low-priority but worthwhile tasks
| Item | Description |
|---|---|
| OG image | Each page uses a separate OG image (at least a different title overlay). Currently there is only one capsule.jpg |
| favicon | At least favicon.svg + apple-touch-icon.png. Consider the PWA's icon-192x192.png |
| Number of fonts | At most 3 Google Font families. More than 5 affects LCP |
| hreflang | If multiple languages are supported, in alternates add hreflang tags |
| WebP/AVIF | Use WebP format for images instead of JPEG to reduce transfer size |