SEO: pricing-rich service schema, AggregateRating, tighter meta

- Service pages now include AggregateOffer in JSON-LD (priceCurrency
  NZD, lowPrice/highPrice/offerCount derived from each service's
  pricing.plans). Unlocks price-rich SERP results.
- Homepage LocalBusiness schema now includes AggregateRating and
  per-testimonial Review entries (5 stars, n reviews). Eligible for
  star ratings in SERPs.
- Puppy Visits meta description rewritten — was 241 chars opening
  with "Puppy Visits Introducing Puppy Visits..." Now a tight 144
  chars with Auckland keyword.
- Removed the dead /about-us static-pages entry; the 301 redirect
  in [slug]/+page.server.ts already routes it to /about, so the
  duplicate metadata was unreachable. Pruned matching dead branches
  in [slug]/+page.svelte and RouteSkeleton.svelte for clarity.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
2026-05-05 14:06:31 +12:00
parent 65bdc8dc20
commit c95d2e8c25
4 changed files with 54 additions and 15 deletions
+30 -5
View File
@@ -30,6 +30,28 @@
return `${siteUrl}${value.startsWith('/') ? value : `/${value}`}`;
}
function aggregateOfferSchema(plans: { price: string }[]) {
const numericPrices = plans
.map((plan) => Number(plan.price.replace(/[^0-9.]/g, '')))
.filter((value) => Number.isFinite(value) && value > 0);
if (numericPrices.length === 0) {
return null;
}
const lowPrice = Math.min(...numericPrices);
const highPrice = Math.max(...numericPrices);
return {
'@type': 'AggregateOffer',
priceCurrency: 'NZD',
lowPrice: lowPrice.toFixed(2),
highPrice: highPrice.toFixed(2),
offerCount: numericPrices.length,
availability: 'https://schema.org/InStock'
};
}
function breadcrumbSchema(name: string, path: string) {
return {
'@context': 'https://schema.org',
@@ -89,7 +111,8 @@
},
areaServed: 'Auckland Central, New Zealand',
image: absoluteUrl(seoImage),
url: `${siteUrl}${data.page.canonicalPath}`
url: `${siteUrl}${data.page.canonicalPath}`,
offers: aggregateOfferSchema(packWalksContent.pricing.plans)
},
breadcrumbSchema('Pack Walks', data.page.canonicalPath)
];
@@ -111,7 +134,8 @@
},
areaServed: 'Auckland Central, New Zealand',
image: absoluteUrl(seoImage),
url: `${siteUrl}${data.page.canonicalPath}`
url: `${siteUrl}${data.page.canonicalPath}`,
offers: aggregateOfferSchema(dogWalkingContent.pricing.plans)
},
breadcrumbSchema('1:1 Walks', data.page.canonicalPath)
];
@@ -133,7 +157,8 @@
},
areaServed: 'Auckland Central, New Zealand',
image: absoluteUrl(seoImage),
url: `${siteUrl}${data.page.canonicalPath}`
url: `${siteUrl}${data.page.canonicalPath}`,
offers: aggregateOfferSchema(puppyVisitsContent.pricing.plans)
},
breadcrumbSchema('Puppy Visits', data.page.canonicalPath)
];
@@ -148,7 +173,7 @@
},
breadcrumbSchema('Our Pricing', data.page.canonicalPath)
];
} else if (data.slug === 'about' || data.slug === 'about-us') {
} else if (data.slug === 'about') {
seoImage = aboutPageContent.sections[0].imageUrl;
seoImageAlt = aboutPageContent.sections[0].imageAlt;
pageStructuredData = [
@@ -186,7 +211,7 @@
<ServiceLandingPage content={data.content} pageContent={puppyVisitsContent} currentPath={data.page.canonicalPath} />
{:else if data.slug === 'our-pricing'}
<PricingPage content={data.content} pageContent={ourPricingContent} />
{:else if data.slug === 'about' || data.slug === 'about-us'}
{:else if data.slug === 'about'}
<AboutPage content={data.content} pageContent={aboutPageContent} />
{:else if data.slug === 'terms-and-conditions'}
<LegalPage pageContent={termsAndConditionsContent} />