Skip to content

Commit a65df6f

Browse files
RivianTrackrclaude
andcommitted
Add individual tire pages with SEO (2.0 Pillar 2, Phase 2A)
Give every tire a server-rendered, indexable URL at /tires/{slug}/ so tires can rank on their own instead of being trapped inside the catalog shortcode. Content (specs, price, Roamer efficiency, owner reviews) is in the initial HTML — no JS required for crawlers. - Schema: add a `slug` column (migration 17), auto-generated from brand+model+size on insert/update with collision suffixing, and backfilled for existing rows. New get_tire_by_slug / sync_tire_slug / backfill_slugs helpers. slug appended to the frontend row arrays. - Routing: new RTG_Tire_Page mirrors the RTG_Tire_Review standalone pattern (rewrite rule, template_redirect render, security headers). Unknown slug -> real 404; legacy /tires/{tire_id}/ -> 301 canonical. - Structured data: factor the per-tire Product builder out of RTG_Schema into reusable build_product_item / build_single_product, so the page emits a dedicated Product node (not just the catalog ItemList) plus BreadcrumbList. build_description in RTG_Meta made public/static for reuse. - Template: frontend/templates/tire-page.php renders title/meta/ canonical/OG, breadcrumb, hero, full spec sheet (efficiency omitted per its discontinuation), Roamer block, and server-rendered reviews. - Cards: tire titles now link to /tires/{slug}/ (tirePageUrl localized, subdirectory-safe); .tire-card-model-link styling added. Build regenerated; 83/83 JS tests pass. Remaining SEO work (sitemap, ?tire= canonical consolidation, admin slug setting, on-page click tracking) tracked in ROADMAP-2.0.md. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1 parent e0958fc commit a65df6f

13 files changed

Lines changed: 803 additions & 144 deletions

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,11 @@ All notable changes to the Rivian Tire Guide plugin will be documented in this f
44

55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
66

7+
## [Unreleased]
8+
9+
### Added
10+
- **Individual, crawlable tire pages (2.0 Pillar 2, Phase 2A).** Every tire now has a server-rendered, indexable URL at `/tires/{slug}/` — tire content (specs, price, Roamer efficiency, owner reviews) is in the initial HTML, not injected by JS. Adds a `slug` column (migration 17, auto-generated from brand+model+size and backfilled), a new `RTG_Tire_Page` route mirroring the standalone review-page pattern, per-tire `<title>`/meta/canonical/OG tags, and dedicated `Product` + `BreadcrumbList` JSON-LD (the `RTG_Schema` product builder was factored out for reuse instead of only emitting a catalog-wide `ItemList`). Tire-card titles now link to these pages; legacy `/tires/{tire_id}/` URLs 301 to the canonical slug. Remaining SEO work (sitemap entries, `?tire=` canonical consolidation, admin slug setting, on-page click tracking) is tracked in ROADMAP-2.0.md.
11+
712
## [1.51.0] - 2026-07-02
813

914
### Changed

frontend/css/rivian-tires.css

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -886,6 +886,13 @@
886886
color: var(--rtg-text-heading);
887887
line-height: 1.2;
888888
}
889+
.tire-card-model-link {
890+
color: inherit;
891+
text-decoration: none;
892+
}
893+
.tire-card-model-link:hover {
894+
color: var(--rtg-accent);
895+
}
889896

890897
/* Tags row */
891898
.tire-card-tags {

frontend/css/rivian-tires.min.css

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

frontend/js/modules/cards.js

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -206,7 +206,7 @@ export function createSingleCard(row) {
206206
tireId, size, diameter, brand, model, category, price, warranty, weight, tpms,
207207
tread, loadIndex, maxLoad, loadRange, speed, psi, utqg, tags, link, image,
208208
/* efficiencyScore */ , /* efficiencyGrade */ , reviewLink, /* createdAt */ ,
209-
roamerEfficiency, roamerTotalKm, roamerVehicleCount, roamerVehicleBreakdown
209+
roamerEfficiency, roamerTotalKm, roamerVehicleCount, roamerVehicleBreakdown, slug
210210
] = row;
211211

212212
if (!VALIDATION_PATTERNS.tireId.test(tireId)) {
@@ -345,7 +345,18 @@ export function createSingleCard(row) {
345345

346346
const modelEl = document.createElement('div');
347347
modelEl.className = 'tire-card-model';
348-
modelEl.textContent = safeString(model);
348+
// Link the title to the individual tire page (crawlable, canonical URL).
349+
const tirePageBase = (typeof rtgData !== 'undefined' && rtgData.settings && rtgData.settings.tirePageUrl) ? rtgData.settings.tirePageUrl : '';
350+
const safeSlug = safeString(slug).trim();
351+
if (tirePageBase && safeSlug) {
352+
const modelLink = document.createElement('a');
353+
modelLink.className = 'tire-card-model-link';
354+
modelLink.href = tirePageBase + encodeURIComponent(safeSlug) + '/';
355+
modelLink.textContent = safeString(model);
356+
modelEl.appendChild(modelLink);
357+
} else {
358+
modelEl.textContent = safeString(model);
359+
}
349360
bodyEl.appendChild(modelEl);
350361

351362
const ratingDiv = document.createElement('div');

frontend/js/rivian-tires.min.js

Lines changed: 9 additions & 9 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)