diff --git a/CLAUDE.md b/CLAUDE.md index ce500e3a..b9b91889 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -69,7 +69,8 @@ sessions `devenv` is a shell function wrapping it. in `src/lib/i18n/index.ts` gives a page's final URL; use it for canonical, hreflang, sitemap and feed links. Host redirects live in `vercel.json` (English pages on .de → www.maxdaten.io, `/` on .io → `/en`, www.maxdaten.de → apex), tested in `tests/vercel-redirects.test.ts`. -- `hero.subheadline` doubles as `meta.description` — keep them in sync. +- The meta description is the first paragraph of `hero.subheadline` (`heroSummary()`, also on the OG + card and in llms.txt); keep that paragraph at 155 characters or fewer. - When changing translation text, update `tests/e2e/i18n.test.ts`. `src/lib/i18n/i18n.test.ts` enforces identical keys across locales and differing values (except `nav.blog`, `nav.gems`, `footer.impressum`, `meta.title`, `services.platform.title`, `services.delivery.title`). Test both diff --git a/scripts/prod-check.mjs b/scripts/prod-check.mjs index 59869971..3fbb0aa8 100644 --- a/scripts/prod-check.mjs +++ b/scripts/prod-check.mjs @@ -12,6 +12,7 @@ const redirects = [ [`${de}/en`, 308, `${io}/en`], [`${de}${post}`, 308, `${io}${post}`], [`${io}/`, 308, '/en'], + [`${io}/impressum`, 308, `${de}/impressum`], ]; // [url, lang, canonical] @@ -64,6 +65,20 @@ for (const [url, lang, canonical] of pages) { ); } +// Social cards and the robots Sitemap line must point at the final host, not the build origin. +const postHtml = await (await get(`${io}${post}`)).text(); +const ogImage = postHtml.match( + / - import { HttpRegex } from '#lib/utils/regex.js'; + import { isExternalHref } from '#lib/i18n/index.js'; import type { Snippet } from 'svelte'; import type { ClassValue } from 'svelte/elements'; @@ -30,7 +30,7 @@ ...rest }: Props = $props(); - const isExternalLink = $derived(!!href && HttpRegex.test(href)); + const isExternalLink = $derived(!!href && isExternalHref(href)); let tag = $derived(href ? 'a' : 'button'); let linkProps = $derived({ href, diff --git a/src/lib/components/atoms/Card.svelte b/src/lib/components/atoms/Card.svelte index b999a245..18543752 100644 --- a/src/lib/components/atoms/Card.svelte +++ b/src/lib/components/atoms/Card.svelte @@ -1,5 +1,5 @@ + + import { page } from '$app/state'; import Card from '#lib/components/atoms/Card.svelte'; + import { siteHref } from '#lib/i18n/index.js'; import Tag from '#lib/components/atoms/Tag.svelte'; import type { ListingPost } from '#lib/sanity/listing.js'; import { urlFor, generateSrcSet } from '#lib/sanity/image.js'; @@ -49,7 +51,7 @@ {/snippet} diff --git a/src/lib/components/molecules/GemCard.svelte b/src/lib/components/molecules/GemCard.svelte index 1b8248de..c5a4ebef 100644 --- a/src/lib/components/molecules/GemCard.svelte +++ b/src/lib/components/molecules/GemCard.svelte @@ -12,6 +12,8 @@ } interface Props { + /** Anchor id, so internal links to a gem (/gems#slug) land on its card. */ + id?: string; title: string; coverImage: string | SanityCoverImage; excerpt: string; @@ -23,6 +25,7 @@ } let { + id, title, coverImage, excerpt, @@ -41,7 +44,7 @@ ); - + {#snippet image()} {#if cover}
diff --git a/src/lib/components/organisms/Footer.svelte b/src/lib/components/organisms/Footer.svelte index 04c909de..509877cb 100644 --- a/src/lib/components/organisms/Footer.svelte +++ b/src/lib/components/organisms/Footer.svelte @@ -1,5 +1,6 @@ +
+ +