feat(theme): add site footer and marketing sections

- Add SiteFooter, rendered through the Layout bottom slot so it appears on
  every page type. themeConfig.footer is deliberately left unset: setting
  it would render the built-in HomeFooter as well, on the home page only.
- Add a frontmatter-driven section system (advantages, contentGrid,
  techStack, cases and cta) with a single ordered array, so bands can be
  reordered or removed without touching code.
- Bands render only when they have content, so an unfilled page degrades
  to its heading rather than showing empty boxes.
- Add a SHOW_COPY_PLACEHOLDERS flag for drafting, false by default.
This commit is contained in:
zihluwang committed 2026-09-25 14:48:42 +08:00
1 parent b2eb6b9638
commit 638c71da44
14 files changed
+652 -3

No files matched your search

+57
View File
@@ -0,0 +1,57 @@
import { arr, str, type TechStackGroup, type TechStackItem } from "./types"
/**
* Technology matrix: named groups of technologies, each with an optional
* emoji/icon and link.
*
* TODO(copy): populate `frontmatter.sections` with real entries. Groups with no
* items are skipped, so a half-filled list still renders sensibly.
*/
export function TechStack({ groups }: { groups?: TechStackGroup[] }) {
const visible = (arr<TechStackGroup>(groups) ?? [])
.map((group) => ({ group, items: arr<TechStackItem>(group?.items) ?? [] }))
.filter(({ items }) => items.length > 0)
if (visible.length === 0) return null
return (
<div className="flex flex-col gap-10">
{visible.map(({ group, items }, groupIndex) => (
<div key={str(group.name) ?? `group-${groupIndex}`}>
<h3 className="mb-4 text-sm font-semibold uppercase tracking-wide text-(--rp-c-text-3)">
{str(group.name) ?? ""}
</h3>
<ul className="m-0 grid list-none grid-cols-2 gap-3 p-0 sm:grid-cols-3 md:grid-cols-4">
{items.map((item, itemIndex) => {
const name = str(item?.name)
if (!name) return null
const url = str(item?.url)
const icon = str(item?.icon)
const body = (
<span className="flex items-center gap-2">
{icon && <span aria-hidden="true">{icon}</span>}
<span>{name}</span>
</span>
)
return (
<li key={`${name}-${itemIndex}`} className="m-0">
{url ? (
<a
className="rp-link flex items-center rounded-md border border-(--rp-c-divider-light) px-3 py-2 no-underline transition-colors hover:bg-(--rp-c-bg-mute)"
href={url}>
{body}
</a>
) : (
<span className="flex items-center rounded-md border border-(--rp-c-divider-light) px-3 py-2">
{body}
</span>
)}
</li>
)
})}
</ul>
</div>
))}
</div>
)
}