feat(theme): add top placement for sections

- Add `placement: top|bottom` to the section frontmatter
- Override `DocContent` to render hoisted bands under the page title
- Derive the zebra stripe from list position so split bands stay continuous
- Opt marketing bands out of doc typography with `rp-not-doc`
- Point the legal and privacy contact address at legal@onixbyte.com
- Condense the products pages' body copy into their section frontmatter
This commit is contained in:
zihluwang committed 2026-09-25 18:04:49 +08:00
1 parent d21b8f6b2f
commit 9d095eba1b
15 files changed
+145 -54

No files matched your search

+11 -3
View File
@@ -6,6 +6,8 @@ interface SectionBandProps {
section: Section
/** Whether the section body produced any content. */
hasContent: boolean
/** Whether this band takes the soft background in the page's zebra stripe. */
tinted: boolean
children: ReactNode
}
@@ -13,15 +15,21 @@ interface SectionBandProps {
* Shell for a marketing band: the `<section>` wrapper, its heading and its
* subtitle. Renders nothing at all when the section has neither content nor a
* heading, so an unfilled page degrades to just its hero.
*
* `rp-not-doc` opts the band out of the doc typography, which applies to
* everything inside `.rp-doc` and would otherwise restyle our headings and
* paragraphs. It is inert for bands rendered outside the article, as on the
* home page.
*/
export function SectionBand({ section, hasContent, children }: SectionBandProps) {
export function SectionBand({ section, hasContent, tinted, children }: SectionBandProps) {
const title = str(section.title)
const subtitle = str(section.subtitle)
const className = `rp-site-section rp-not-doc${tinted ? " rp-site-section--tinted" : ""}`
if (!hasContent && !title && !subtitle) {
if (!SHOW_COPY_PLACEHOLDERS) return null
return (
<section id={section.id} className="rp-site-section">
<section id={section.id} className={className}>
<div className="mx-auto max-w-5xl px-6">
<div className="rounded-lg border border-dashed border-(--rp-c-divider) p-8 text-center text-sm text-(--rp-c-text-3)">
TODO: {section.type}
@@ -32,7 +40,7 @@ export function SectionBand({ section, hasContent, children }: SectionBandProps)
}
return (
<section id={section.id} className="rp-site-section">
<section id={section.id} className={className}>
<div className="mx-auto max-w-5xl px-6">
{(title || subtitle) && (
<header className="mb-10 text-center">
-3
View File
@@ -3,9 +3,6 @@ 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) ?? [])
+15
View File
@@ -50,6 +50,16 @@ interface SectionBase {
id?: string
title?: string
subtitle?: string
/**
* Where the band sits relative to the article body. `"top"` hoists it above
* the body, directly under the page title; `"bottom"` — the default — leaves
* it below the article.
*
* Only doc pages can honour this, because only they have an article to sit
* between. The home page renders every band below the features grid, in
* declaration order, whatever this says.
*/
placement?: "top" | "bottom"
}
export type Section =
@@ -100,3 +110,8 @@ export function hasContent(section: Section): boolean {
)
}
}
/** Whether a section asked to be hoisted above the article body. */
export function isHoisted(section: Section): boolean {
return section.placement === "top"
}