From 7469480dcc662a94b6155836ff5e474271f4cbd1 Mon Sep 17 00:00:00 2001 From: Kevin Cantrell Date: Tue, 7 Jul 2026 16:39:28 +0900 Subject: [PATCH] feat(io): add Organization/WebSite/Breadcrumb/Product JSON-LD for rich results Adds a small shared JSON-LD toolkit (src/lib/seo/{site,schema}.ts, src/lib/components/JsonLd.svelte) and wires the still-eligible Google structured -data enhancements: - Organization + WebSite once site-wide (root layout) - BreadcrumbList: Breadcrumbs.svelte now delegates to the shared breadcrumbSchema - Product: one per sensor module on /replacement-sensors + one on /replacement-case (bare Products - no fabricated ratings) FAQ/HowTo intentionally omitted (Google deprecated both rich-result types). schema.ts + JsonLd.svelte are byte-identical to the Japan-website branch; only site.ts differs. Verified in the prerendered build: exactly one Org+WebSite per page, valid JSON, correct escaping and @id cross-refs. TODO(confirm): .io Organization sameAs URLs. Co-Authored-By: Claude Opus 4.8 (1M context) --- src/lib/components/Breadcrumbs.svelte | 33 ++-- src/lib/components/JsonLd.svelte | 15 ++ src/lib/seo/schema.ts | 176 ++++++++++++++++++++ src/lib/seo/site.ts | 67 ++++++++ src/routes/+layout.svelte | 5 + src/routes/replacement-case/+page.svelte | 15 ++ src/routes/replacement-sensors/+page.svelte | 16 ++ 7 files changed, 307 insertions(+), 20 deletions(-) create mode 100644 src/lib/components/JsonLd.svelte create mode 100644 src/lib/seo/schema.ts create mode 100644 src/lib/seo/site.ts diff --git a/src/lib/components/Breadcrumbs.svelte b/src/lib/components/Breadcrumbs.svelte index f31f02a..640c80a 100644 --- a/src/lib/components/Breadcrumbs.svelte +++ b/src/lib/components/Breadcrumbs.svelte @@ -4,8 +4,9 @@ * * Pass an ordered list of crumbs from the site root to the current page. The * final crumb is rendered as the current page - no link, `aria-current="page"`. - * The component also emits a matching schema.org `BreadcrumbList` so the trail - * is eligible for breadcrumb rich results in search. + * The component also emits a matching schema.org `BreadcrumbList` (via the + * shared breadcrumbSchema builder + ) so the trail is eligible for + * breadcrumb rich results in search. * * Usage: * */ + import JsonLd from './JsonLd.svelte'; + import { breadcrumbSchema } from '$lib/seo/schema'; + export interface Crumb { label: string; /** Absolute or root-relative path. Omit on the final (current) crumb. */ href?: string; } - let { items, origin = 'https://cropwatch.io' }: { items: Crumb[]; origin?: string } = - $props(); + let { items }: { items: Crumb[] } = $props(); const lastIndex = $derived(items.length - 1); - // BreadcrumbList JSON-LD: absolute URLs from each href; the current page omits - // `item`. `<` is escaped so a label can never break out of the script tag. - const jsonLd = $derived( - JSON.stringify({ - '@context': 'https://schema.org', - '@type': 'BreadcrumbList', - itemListElement: items.map((crumb, i) => ({ - '@type': 'ListItem', - position: i + 1, - name: crumb.label, - ...(crumb.href ? { item: origin + crumb.href } : {}) - })) - }).replace(/. + const breadcrumbLd = $derived( + breadcrumbSchema(items.map((crumb) => ({ name: crumb.label, path: crumb.href }))) ); - - {@html ``} - +