claude-plugin-wordpress-manager 2.2.0 → 2.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (34) hide show
  1. package/.claude-plugin/plugin.json +2 -2
  2. package/CHANGELOG.md +26 -0
  3. package/agents/wp-content-strategist.md +25 -0
  4. package/agents/wp-ecommerce-manager.md +23 -0
  5. package/agents/wp-site-manager.md +26 -0
  6. package/docs/GUIDE.md +116 -28
  7. package/package.json +8 -3
  8. package/skills/wordpress-router/references/decision-tree.md +8 -2
  9. package/skills/wp-content/SKILL.md +1 -0
  10. package/skills/wp-content-attribution/SKILL.md +97 -0
  11. package/skills/wp-content-attribution/references/attribution-models.md +189 -0
  12. package/skills/wp-content-attribution/references/conversion-funnels.md +137 -0
  13. package/skills/wp-content-attribution/references/reporting-dashboards.md +199 -0
  14. package/skills/wp-content-attribution/references/roi-calculation.md +202 -0
  15. package/skills/wp-content-attribution/references/utm-tracking-setup.md +161 -0
  16. package/skills/wp-content-attribution/scripts/attribution_inspect.mjs +277 -0
  17. package/skills/wp-headless/SKILL.md +1 -0
  18. package/skills/wp-i18n/SKILL.md +1 -0
  19. package/skills/wp-multilang-network/SKILL.md +107 -0
  20. package/skills/wp-multilang-network/references/content-sync.md +182 -0
  21. package/skills/wp-multilang-network/references/hreflang-config.md +198 -0
  22. package/skills/wp-multilang-network/references/language-routing.md +234 -0
  23. package/skills/wp-multilang-network/references/network-architecture.md +119 -0
  24. package/skills/wp-multilang-network/references/seo-international.md +213 -0
  25. package/skills/wp-multilang-network/scripts/multilang_inspect.mjs +308 -0
  26. package/skills/wp-multisite/SKILL.md +1 -0
  27. package/skills/wp-programmatic-seo/SKILL.md +97 -0
  28. package/skills/wp-programmatic-seo/references/data-sources.md +200 -0
  29. package/skills/wp-programmatic-seo/references/location-seo.md +134 -0
  30. package/skills/wp-programmatic-seo/references/product-seo.md +147 -0
  31. package/skills/wp-programmatic-seo/references/technical-seo.md +197 -0
  32. package/skills/wp-programmatic-seo/references/template-architecture.md +125 -0
  33. package/skills/wp-programmatic-seo/scripts/programmatic_seo_inspect.mjs +264 -0
  34. package/skills/wp-woocommerce/SKILL.md +1 -0
@@ -0,0 +1,134 @@
1
+ # Location-Based SEO
2
+
3
+ Use this file when creating city/location pages at scale — service-area combinations, LocalBusiness schema, geo-targeting, and NAP consistency.
4
+
5
+ ## City/Location Page Patterns
6
+
7
+ Location pages combine a service or product with a geographic area:
8
+
9
+ | Pattern | Template | Scale |
10
+ |---------|----------|-------|
11
+ | Service + City | "Plumbing in {city}" | services × cities |
12
+ | Store + City | "{brand} Store in {city}" | stores × locations |
13
+ | Restaurant + Area | "Best {cuisine} in {neighborhood}" | cuisines × neighborhoods |
14
+ | Real estate + City | "Homes for Sale in {city}" | property types × cities |
15
+
16
+ **Content formula for each page:**
17
+ 1. H1: `{Service} in {City}`
18
+ 2. Intro paragraph (50–80 words) with local context
19
+ 3. Service details section (150–200 words)
20
+ 4. Local information (population, area facts, nearby landmarks)
21
+ 5. FAQ section (3–5 questions) with local keywords
22
+ 6. CTA with phone number and address
23
+
24
+ ## LocalBusiness Schema Markup
25
+
26
+ ```json
27
+ {
28
+ "@context": "https://schema.org",
29
+ "@type": "LocalBusiness",
30
+ "name": "{business_name} — {city}",
31
+ "description": "{service_description} in {city}",
32
+ "url": "https://example.com/{service}/{city}",
33
+ "telephone": "{phone}",
34
+ "address": {
35
+ "@type": "PostalAddress",
36
+ "streetAddress": "{street}",
37
+ "addressLocality": "{city}",
38
+ "addressRegion": "{state}",
39
+ "postalCode": "{zip}",
40
+ "addressCountry": "{country_code}"
41
+ },
42
+ "geo": {
43
+ "@type": "GeoCoordinates",
44
+ "latitude": "{lat}",
45
+ "longitude": "{lng}"
46
+ },
47
+ "openingHoursSpecification": [
48
+ {
49
+ "@type": "OpeningHoursSpecification",
50
+ "dayOfWeek": ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday"],
51
+ "opens": "09:00",
52
+ "closes": "17:00"
53
+ }
54
+ ],
55
+ "areaServed": {
56
+ "@type": "City",
57
+ "name": "{city}"
58
+ }
59
+ }
60
+ ```
61
+
62
+ **Schema variations by business type:**
63
+ - `@type: "Restaurant"` — add `servesCuisine`, `menu`, `priceRange`
64
+ - `@type: "MedicalBusiness"` — add `medicalSpecialty`
65
+ - `@type: "Store"` — add `paymentAccepted`, `currenciesAccepted`
66
+
67
+ ## Geo-Targeting Configuration
68
+
69
+ ### Google Search Console
70
+ 1. Set geographic target per property (if using country-specific domains)
71
+ 2. Submit location-specific sitemap: `/sitemap-locations.xml`
72
+ 3. Monitor "Search results" by country/city in Performance report
73
+
74
+ ### WordPress Implementation
75
+ - Use ACF or custom fields for lat/lng storage
76
+ - Store city, state, zip as separate meta fields (not combined string)
77
+ - Create a `location` taxonomy for hierarchical grouping (country → state → city)
78
+
79
+ ## NAP Consistency
80
+
81
+ **NAP = Name, Address, Phone** — must be identical across all location pages.
82
+
83
+ | Element | Rule | Example |
84
+ |---------|------|---------|
85
+ | Name | Exact legal business name | "Smith Plumbing LLC" (not "Smith's Plumbing") |
86
+ | Address | USPS-standardized format | "123 Main St, Ste 100" (not "123 Main Street Suite 100") |
87
+ | Phone | E.164 format in schema, formatted for display | `+15551234567` schema, `(555) 123-4567` display |
88
+
89
+ **Verification:** Cross-check NAP against Google Business Profile, Yelp, and BBB listings.
90
+
91
+ ## Local Keyword Research Methodology
92
+
93
+ 1. **Seed keywords:** core service terms (plumbing, dentist, attorney)
94
+ 2. **Location modifiers:** city names, neighborhoods, zip codes, "near me"
95
+ 3. **Intent modifiers:** "best", "affordable", "emergency", "24/7"
96
+ 4. **Combine:** `{intent} {service} in {location}` → "emergency plumber in Miami"
97
+ 5. **Validate:** Check search volume via keyword tools; prioritize cities with volume > 100/mo
98
+ 6. **Cluster:** Group by parent topic to avoid cannibalization
99
+
100
+ ## WordPress CPT for Locations
101
+
102
+ ```php
103
+ // fields: city, state, zip, lat, lng, population, local_description
104
+ register_post_type('service_location', [
105
+ 'public' => true,
106
+ 'show_in_rest' => true,
107
+ 'supports' => ['title', 'editor', 'custom-fields', 'thumbnail'],
108
+ 'taxonomies' => ['service_type', 'region'],
109
+ 'rewrite' => ['slug' => 'services'],
110
+ ]);
111
+
112
+ // Expose custom fields via REST API
113
+ register_rest_field('service_location', 'location_meta', [
114
+ 'get_callback' => function ($post) {
115
+ return [
116
+ 'city' => get_post_meta($post['id'], 'city', true),
117
+ 'state' => get_post_meta($post['id'], 'state', true),
118
+ 'zip' => get_post_meta($post['id'], 'zip', true),
119
+ 'lat' => get_post_meta($post['id'], 'lat', true),
120
+ 'lng' => get_post_meta($post['id'], 'lng', true),
121
+ 'population' => get_post_meta($post['id'], 'population', true),
122
+ ];
123
+ },
124
+ 'schema' => ['type' => 'object'],
125
+ ]);
126
+ ```
127
+
128
+ ## Decision Checklist
129
+
130
+ 1. Does the business serve multiple geographic areas? → Yes = location pages justified
131
+ 2. Is there search volume for `{service} in {city}` queries? → Validate before building
132
+ 3. Are NAP details consistent across all listings? → Audit before publishing
133
+ 4. Does each location page have 300+ words of unique content? → Avoid thin pages
134
+ 5. Is LocalBusiness schema valid per Google's Rich Results Test? → Test before deploy
@@ -0,0 +1,147 @@
1
+ # Product Programmatic SEO
2
+
3
+ Use this file when generating product variant pages, comparison pages, or category landing pages at scale using WooCommerce product data as the SEO content source.
4
+
5
+ ## Product Variant Pages
6
+
7
+ Generate pages for every meaningful combination of product attributes:
8
+
9
+ | Attribute Combination | URL Pattern | Example |
10
+ |----------------------|-------------|---------|
11
+ | Product + Size | `/{product}/{size}` | `/running-shoes/size-10` |
12
+ | Product + Color | `/{product}/{color}` | `/t-shirt/navy-blue` |
13
+ | Product + Size + Color | `/{product}/{size}-{color}` | `/dress/medium-red` |
14
+ | Brand + Category | `/{brand}/{category}` | `/nike/running-shoes` |
15
+ | Product + Material | `/{product}/{material}` | `/jacket/leather` |
16
+
17
+ **When to create variant pages (vs single page with selector):**
18
+ - Each variant has unique search volume (e.g., "red Nike Air Max")
19
+ - Variants differ significantly in content/images
20
+ - Variants target different buyer intents
21
+
22
+ **When NOT to create variant pages:**
23
+ - Variants are trivial (only size differs, no unique content)
24
+ - Low search volume for specific combinations
25
+ - Risk of thin content / doorway pages
26
+
27
+ ## WooCommerce Product Data as SEO Source
28
+
29
+ Pull product attributes via WooCommerce REST API:
30
+
31
+ ```bash
32
+ # List products with attributes — use wc_list_products MCP tool
33
+ wc_list_products(per_page=100, status="publish")
34
+
35
+ # Get product variations
36
+ wc_list_product_variations(product_id=123)
37
+ ```
38
+
39
+ **Useful WooCommerce fields for programmatic pages:**
40
+ - `name`, `description`, `short_description` → page content
41
+ - `attributes` → variant dimensions (color, size, material)
42
+ - `categories`, `tags` → taxonomy clustering
43
+ - `regular_price`, `sale_price` → pricing content
44
+ - `average_rating`, `rating_count` → social proof
45
+ - `images` → product visuals
46
+ - `stock_status` → availability signals
47
+
48
+ ## Comparison Pages
49
+
50
+ Generate "Product A vs Product B" pages from product pairs:
51
+
52
+ **Template structure:**
53
+ ```
54
+ Title: "{Product A} vs {Product B} — Which Is Better?"
55
+ H1: "{Product A} vs {Product B}"
56
+
57
+ Comparison table:
58
+ | Feature | Product A | Product B |
59
+ |---------------|---------------|---------------|
60
+ | Price | {price_a} | {price_b} |
61
+ | Rating | {rating_a}/5 | {rating_b}/5 |
62
+ | {attribute_1} | {value_a} | {value_b} |
63
+
64
+ Summary: "Choose {Product A} if... Choose {Product B} if..."
65
+ ```
66
+
67
+ **Scale calculation:** N products → N×(N-1)/2 comparison pages. 50 products = 1,225 pages.
68
+
69
+ **Quality gate:** Only generate comparisons where products share a category and have meaningful differences.
70
+
71
+ ## Category/Tag Landing Pages
72
+
73
+ Programmatic category pages aggregate products with editorial content:
74
+
75
+ ```
76
+ Title: "Best {Category} in {Year} — Top {count} Picks"
77
+ H1: "Best {Category}"
78
+
79
+ Content:
80
+ - Category introduction (100–150 words)
81
+ - Top products grid (from WooCommerce query)
82
+ - Buying guide section (200–300 words, template with dynamic fields)
83
+ - FAQ (3–5 questions from keyword research)
84
+ ```
85
+
86
+ **Implementation:**
87
+ 1. Query WooCommerce categories via `wc_list_products(category=ID)`
88
+ 2. Sort by `average_rating` or `total_sales` for "top picks"
89
+ 3. Generate buying guide from category attributes
90
+
91
+ ## Product Schema Markup
92
+
93
+ ```json
94
+ {
95
+ "@context": "https://schema.org",
96
+ "@type": "Product",
97
+ "name": "{product_name}",
98
+ "description": "{product_description}",
99
+ "image": "{image_url}",
100
+ "brand": {
101
+ "@type": "Brand",
102
+ "name": "{brand_name}"
103
+ },
104
+ "sku": "{sku}",
105
+ "offers": {
106
+ "@type": "Offer",
107
+ "url": "{page_url}",
108
+ "priceCurrency": "{currency}",
109
+ "price": "{price}",
110
+ "availability": "https://schema.org/{InStock|OutOfStock}",
111
+ "seller": {
112
+ "@type": "Organization",
113
+ "name": "{store_name}"
114
+ }
115
+ },
116
+ "aggregateRating": {
117
+ "@type": "AggregateRating",
118
+ "ratingValue": "{avg_rating}",
119
+ "reviewCount": "{review_count}"
120
+ }
121
+ }
122
+ ```
123
+
124
+ ## Canonical URL Strategy
125
+
126
+ Prevent duplicate content across variant and comparison pages:
127
+
128
+ | Scenario | Canonical Rule |
129
+ |----------|---------------|
130
+ | Color variants with same content | Canonical → parent product page |
131
+ | Size variants with unique content | Self-canonical (each variant is canonical) |
132
+ | Comparison A vs B and B vs A | Canonical → alphabetically first (A vs B) |
133
+ | Category + filtered view | Canonical → unfiltered category page |
134
+ | Paginated category pages | `rel="next"` / `rel="prev"` + canonical to page 1 |
135
+
136
+ **Implementation in headless frontend:**
137
+ ```html
138
+ <link rel="canonical" href="{computed_canonical_url}" />
139
+ ```
140
+
141
+ ## Decision Checklist
142
+
143
+ 1. Do product variants have unique search volume? → Yes = variant pages; No = single page
144
+ 2. Is comparison data meaningful (shared category, different attributes)? → Yes = comparison pages
145
+ 3. Does each generated page have 300+ words of unique content? → Verify template output
146
+ 4. Are canonical URLs set correctly to avoid duplicate indexing? → Test with Google URL Inspection
147
+ 5. Is Product schema valid? → Test with Rich Results Test
@@ -0,0 +1,197 @@
1
+ # Technical SEO for Programmatic Pages
2
+
3
+ Use this file when managing sitemaps, crawl budget, canonicals, internal linking, and Core Web Vitals for large-scale programmatic page generation.
4
+
5
+ ## XML Sitemap Generation
6
+
7
+ ### Yoast SEO Integration
8
+
9
+ Yoast auto-generates sitemaps for all public post types:
10
+ - Main sitemap index: `/sitemap_index.xml`
11
+ - Post type sitemaps: `/post_type-sitemap.xml` (paginated at 1000 URLs per file)
12
+
13
+ **Configure for programmatic CPTs:**
14
+ ```php
15
+ // Ensure CPT appears in Yoast sitemap
16
+ register_post_type('location', [
17
+ 'public' => true,
18
+ 'show_in_rest' => true,
19
+ // Yoast auto-includes public CPTs in sitemap
20
+ ]);
21
+ ```
22
+
23
+ ### Rank Math Integration
24
+
25
+ Rank Math uses similar auto-generation. Configure via:
26
+ - Settings → Sitemap → enable the CPT
27
+ - Pagination: 200 URLs per sitemap file (configurable)
28
+
29
+ ### Dynamic Sitemap Index for 1000s+ Pages
30
+
31
+ For very large sites, create a custom sitemap index:
32
+
33
+ ```php
34
+ // Custom sitemap for programmatic pages (mu-plugin)
35
+ add_filter('wp_sitemaps_post_types', function ($post_types) {
36
+ $post_types['location'] = get_post_type_object('location');
37
+ return $post_types;
38
+ });
39
+
40
+ // Custom max URLs per sitemap page
41
+ add_filter('wp_sitemaps_max_urls', function () {
42
+ return 2000; // Default is 2000, reduce if pages are slow to crawl
43
+ });
44
+ ```
45
+
46
+ **Sitemap submission:**
47
+ 1. Submit sitemap index URL in Google Search Console
48
+ 2. Reference sitemap in `robots.txt`: `Sitemap: https://example.com/sitemap_index.xml`
49
+ 3. Monitor "Sitemaps" report in GSC for errors
50
+
51
+ ## Crawl Budget Optimization
52
+
53
+ Crawl budget = how many pages Googlebot will crawl per session. Critical for 1000s+ pages.
54
+
55
+ ### Pages to Index (doindex)
56
+
57
+ - Programmatic pages with unique, valuable content (300+ words)
58
+ - Category landing pages with editorial content
59
+ - Comparison pages with meaningful differentiation
60
+
61
+ ### Pages to Noindex
62
+
63
+ | Page Type | Action | Reason |
64
+ |-----------|--------|--------|
65
+ | Thin variant pages (< 300 words) | `noindex, follow` | Low content value |
66
+ | Paginated archives (page 2+) | `noindex, follow` | Duplicate of page 1 |
67
+ | Faceted navigation results | `noindex` or block via `robots.txt` | Parameter-based duplicates |
68
+ | Internal search results | `noindex` | Dynamic, low SEO value |
69
+ | Tag pages with < 3 posts | `noindex, follow` | Thin taxonomy pages |
70
+
71
+ ### Implementation
72
+
73
+ ```html
74
+ <!-- Noindex via meta tag (headless frontend) -->
75
+ <meta name="robots" content="noindex, follow" />
76
+
77
+ <!-- Or via X-Robots-Tag HTTP header -->
78
+ X-Robots-Tag: noindex, follow
79
+ ```
80
+
81
+ ## Canonical URL Management
82
+
83
+ ### Headless Architecture Canonicals
84
+
85
+ In headless setups, the canonical must point to the frontend URL, not the WordPress admin URL:
86
+
87
+ ```html
88
+ <!-- Frontend page canonical -->
89
+ <link rel="canonical" href="https://www.example.com/services/miami" />
90
+
91
+ <!-- NOT the WordPress REST source -->
92
+ <!-- Wrong: https://wp.example.com/wp-json/wp/v2/location/123 -->
93
+ ```
94
+
95
+ **Configuration in headless frontend:**
96
+ ```javascript
97
+ // Next.js metadata
98
+ export function generateMetadata({ params }) {
99
+ return {
100
+ alternates: {
101
+ canonical: `https://www.example.com/${params.service}/${params.city}`,
102
+ },
103
+ };
104
+ }
105
+ ```
106
+
107
+ ### Canonical Rules for Programmatic Pages
108
+
109
+ | Scenario | Canonical Target |
110
+ |----------|-----------------|
111
+ | Standard programmatic page | Self-canonical |
112
+ | Variant with identical content to parent | Parent product page |
113
+ | Paginated series | Page 1 of the series |
114
+ | HTTP vs HTTPS | HTTPS version |
115
+ | www vs non-www | Preferred domain version |
116
+ | Trailing slash vs no trailing slash | Consistent chosen format |
117
+
118
+ ## robots.txt Configuration
119
+
120
+ ```
121
+ # robots.txt for headless WordPress
122
+ User-agent: *
123
+ Allow: /
124
+
125
+ # Block WordPress admin (if exposed)
126
+ Disallow: /wp-admin/
127
+ Allow: /wp-admin/admin-ajax.php
128
+
129
+ # Block faceted navigation / filter parameters
130
+ Disallow: /*?filter=
131
+ Disallow: /*?sort=
132
+ Disallow: /*?page=
133
+
134
+ # Sitemap reference
135
+ Sitemap: https://www.example.com/sitemap_index.xml
136
+ ```
137
+
138
+ ## Internal Linking Strategy
139
+
140
+ Programmatic pages need structured internal linking for crawlability and authority flow:
141
+
142
+ ### Hub-and-Spoke Model
143
+
144
+ ```
145
+ Category hub page (e.g., /plumbing/)
146
+ ├── /plumbing/miami
147
+ ├── /plumbing/orlando
148
+ ├── /plumbing/tampa
149
+ └── /plumbing/jacksonville
150
+ ```
151
+
152
+ **Implementation:**
153
+ - Hub page links to all spoke pages (or top N by relevance)
154
+ - Each spoke page links back to hub + 3–5 related spokes
155
+ - Breadcrumbs: Home → Service → City
156
+
157
+ ### Cross-Linking Between Clusters
158
+
159
+ ```
160
+ /plumbing/miami ←→ /electrical/miami (same city, different service)
161
+ /plumbing/miami ←→ /plumbing/fort-lauderdale (same service, nearby city)
162
+ ```
163
+
164
+ **Automated linking (template-based):**
165
+ ```html
166
+ <!-- Related services in {city} -->
167
+ <h2>Other Services in {city}</h2>
168
+ <ul>
169
+ {for service in other_services}
170
+ <li><a href="/{service.slug}/{city.slug}">{service.name} in {city.name}</a></li>
171
+ {/for}
172
+ </ul>
173
+ ```
174
+
175
+ ## Core Web Vitals for Template Pages
176
+
177
+ | Metric | Target | Programmatic Page Risk |
178
+ |--------|--------|----------------------|
179
+ | LCP | < 2.5s | Large hero images from WP media library |
180
+ | INP | < 200ms | Heavy JS for dynamic content |
181
+ | CLS | < 0.1 | Late-loading images without dimensions |
182
+
183
+ **Optimizations:**
184
+ 1. **LCP:** Preload hero image, use `next/image` or equivalent with width/height
185
+ 2. **INP:** Minimize client-side JS; SSG/ISR means most content is pre-rendered
186
+ 3. **CLS:** Set explicit `width` and `height` on all images; use CSS `aspect-ratio`
187
+ 4. **Font loading:** `font-display: swap` for web fonts
188
+ 5. **Third-party scripts:** Defer analytics/tracking below the fold
189
+
190
+ ## Decision Checklist
191
+
192
+ 1. Is the sitemap index submitted and error-free in GSC? → Monitor weekly
193
+ 2. Are thin pages noindexed to preserve crawl budget? → Audit monthly
194
+ 3. Do all programmatic pages have correct self-canonicals? → Verify in bulk
195
+ 4. Is robots.txt blocking faceted navigation and parameters? → Test with robots.txt tester
196
+ 5. Does every page link to its hub and 3–5 related pages? → Template includes these links
197
+ 6. Are Core Web Vitals passing for template pages? → Test with PageSpeed Insights on sample
@@ -0,0 +1,125 @@
1
+ # Template Architecture
2
+
3
+ Use this file when designing page templates for headless WordPress programmatic SEO — custom post types as data source, URL structure, ISR/SSG configuration, and bulk content creation.
4
+
5
+ ## Custom Post Types as Data Source
6
+
7
+ WordPress CPTs are the ideal data backbone for programmatic pages:
8
+
9
+ ```php
10
+ // Register a CPT for programmatic content (e.g., locations)
11
+ register_post_type('location', [
12
+ 'public' => true,
13
+ 'show_in_rest' => true, // Required for headless access
14
+ 'supports' => ['title', 'editor', 'custom-fields', 'thumbnail'],
15
+ 'rewrite' => ['slug' => 'locations'],
16
+ 'has_archive' => true,
17
+ ]);
18
+ ```
19
+
20
+ **Required CPT fields for programmatic SEO:**
21
+ - Title → H1 and `<title>` tag
22
+ - Custom fields → template variables (city, service, price, etc.)
23
+ - Taxonomy → category/tag for clustering
24
+ - Featured image → OG image
25
+
26
+ ## URL Structure Design
27
+
28
+ | Pattern | Example | Use Case |
29
+ |---------|---------|----------|
30
+ | `/{service}/{city}` | `/plumbing/miami` | Service-area pages |
31
+ | `/{product}/{variant}` | `/shoes/red-size-10` | Product variants |
32
+ | `/{category}/{item}` | `/restaurants/joes-pizza` | Directory listings |
33
+ | `/{brand}/{model}/{year}` | `/toyota/camry/2024` | Multi-attribute pages |
34
+ | `/{topic}/{subtopic}` | `/learn/react-hooks` | Educational content |
35
+
36
+ **URL rules:**
37
+ - Lowercase, hyphenated slugs
38
+ - Max 3 levels deep (SEO best practice)
39
+ - Include primary keyword in first path segment
40
+ - Avoid query parameters for indexable pages
41
+
42
+ ## ISR Configuration (Headless Frontend)
43
+
44
+ ### Next.js (App Router)
45
+
46
+ ```javascript
47
+ // app/[service]/[city]/page.js
48
+ export async function generateStaticParams() {
49
+ const res = await fetch(`${WP_API}/wp/v2/location?per_page=100`);
50
+ const locations = await res.json();
51
+ return locations.map((loc) => ({
52
+ service: loc.acf.service_slug,
53
+ city: loc.slug,
54
+ }));
55
+ }
56
+
57
+ export const revalidate = 3600; // ISR: revalidate every hour
58
+ ```
59
+
60
+ ### Nuxt
61
+
62
+ ```javascript
63
+ // nuxt.config.ts
64
+ export default defineNuxtConfig({
65
+ routeRules: {
66
+ '/services/**': { isr: 3600 },
67
+ },
68
+ });
69
+ ```
70
+
71
+ ### Astro
72
+
73
+ ```javascript
74
+ // astro.config.mjs — hybrid mode for ISR-like behavior
75
+ export default defineConfig({
76
+ output: 'hybrid',
77
+ adapter: node({ mode: 'standalone' }),
78
+ });
79
+ ```
80
+
81
+ **Revalidate intervals by content type:**
82
+
83
+ | Content Type | Interval | Reason |
84
+ |-------------|----------|--------|
85
+ | Location pages | 3600s (1h) | Rarely change |
86
+ | Product pages | 900s (15m) | Price/stock updates |
87
+ | Blog/guides | 86400s (24h) | Stable content |
88
+ | Comparison pages | 3600s (1h) | Data-driven, periodic |
89
+
90
+ ## Template Variables
91
+
92
+ A programmatic page template maps CPT fields to HTML:
93
+
94
+ ```
95
+ Title: "{service_name} in {city_name} — {brand}"
96
+ Meta desc: "Professional {service_name} in {city_name}. {unique_selling_point}."
97
+ H1: "{service_name} in {city_name}"
98
+ Body: Template paragraph using {city_population}, {service_details}, {local_info}
99
+ Schema: LocalBusiness JSON-LD with {name}, {address}, {phone}, {geo}
100
+ ```
101
+
102
+ **Anti-pattern:** Avoid thin content — each page must have enough unique value to justify indexing. Minimum 300 words of meaningful, varied content per page.
103
+
104
+ ## Bulk Content Creation Workflow
105
+
106
+ 1. **Prepare data:** CSV or JSON with one row per page (city, service, attributes)
107
+ 2. **Create CPT entries:** Loop via WordPress REST API:
108
+
109
+ ```bash
110
+ # Using wp-rest-bridge create_content tool
111
+ for each row in data:
112
+ create_content(type="location", title=row.title, content=row.body, meta=row.fields)
113
+ ```
114
+
115
+ 3. **Assign taxonomies:** Tag each entry with relevant terms for clustering
116
+ 4. **Generate frontend paths:** Run `generateStaticParams` or equivalent build step
117
+ 5. **Verify:** Spot-check 5–10 pages for content quality and schema validity
118
+
119
+ ## Decision Checklist
120
+
121
+ 1. Is the data structured and repeatable? → Yes = proceed with CPT
122
+ 2. Will each page have unique, valuable content? → Yes = proceed; No = consolidate
123
+ 3. Is the URL pattern SEO-friendly and max 3 levels? → Verify before building
124
+ 4. Is ISR/SSG configured with appropriate revalidation? → Match to content freshness
125
+ 5. Are sitemaps generated for all programmatic pages? → See `technical-seo.md`