From 72ca0419db8cad6c6a875cdb8f1cb0624feb84bb Mon Sep 17 00:00:00 2001 From: enricobuehler Date: Sat, 20 Jun 2026 15:42:39 +0200 Subject: [PATCH] feat(docs-site): add the site-wide footer, shared with the marketing site Pull the same footer from the shared unom CMS global (cms.unom.io) and render it globally under both the home and docs layouts. Read-only typed fetch in a server-side root loader (falls back to null on a CMS hiccup). Root-relative links target the marketing site, so they're resolved against its origin (the docs don't host /legal/* etc.); themed with Fumadocs tokens for light/dark. Co-Authored-By: Claude Opus 4.8 (1M context) --- docs-site/src/components/Footer.tsx | 51 +++++++++++++++++++++++++++++ docs-site/src/lib/cms.ts | 27 +++++++++++++++ docs-site/src/routes/__root.tsx | 24 +++++++++++++- 3 files changed, 101 insertions(+), 1 deletion(-) create mode 100644 docs-site/src/components/Footer.tsx create mode 100644 docs-site/src/lib/cms.ts diff --git a/docs-site/src/components/Footer.tsx b/docs-site/src/components/Footer.tsx new file mode 100644 index 0000000..ddd9c75 --- /dev/null +++ b/docs-site/src/components/Footer.tsx @@ -0,0 +1,51 @@ +import { getRouteApi } from '@tanstack/react-router' +import type { NavigationLink, NavigationSection } from '@/lib/cms' + +const rootApi = getRouteApi('__root__') + +// The docs share the marketing site's footer (same CMS global). Root-relative +// links target the website, so resolve them against its origin — the docs don't +// host /legal/* etc. themselves. Mirrors the website Footer, themed for docs. +const SITE_URL = 'https://punktfunk.unom.io' +const resolve = (to?: string | null) => + to ? (to.startsWith('/') ? `${SITE_URL}${to}` : to) : '#' + +export default function Footer() { + const { footer } = rootApi.useLoaderData() + const sections: NavigationSection[] = footer?.sections ?? [] + const tagline = footer?.tagline?.trim() + + if (!sections.length && !tagline) return null + + return ( +
+
+ {sections.map((group, gi) => ( +
+ {group.title && ( +

+ {group.title} +

+ )} +
+ {(group.entries ?? []).map((item: NavigationLink, i) => ( + + {item.label} + + ))} +
+
+ ))} + {tagline && ( +

+ {tagline} +

+ )} +
+
+ ) +} diff --git a/docs-site/src/lib/cms.ts b/docs-site/src/lib/cms.ts new file mode 100644 index 0000000..22ac944 --- /dev/null +++ b/docs-site/src/lib/cms.ts @@ -0,0 +1,27 @@ +// The docs reuse the punktfunk marketing site's footer — the same Payload CMS +// global on the shared unom CMS (cms.unom.io). It's a read-only GET, so a plain +// typed fetch rather than pulling in the Payload SDK + generated types. +const CMS_URL = 'https://cms.unom.io' + +export interface NavigationLink { + id?: string | null + label?: string | null + to?: string | null +} + +export interface NavigationSection { + id?: string | null + title?: string | null + entries?: NavigationLink[] | null +} + +export interface Footer { + tagline?: string | null + sections?: NavigationSection[] | null +} + +export async function findFooter(): Promise