From f51407b98b578031a332e5abd305f48f5b458e63 Mon Sep 17 00:00:00 2001 From: zihluwang Date: Fri, 25 Sep 2026 22:10:15 +0800 Subject: [PATCH] fix(theme): restore article styles on doc pages MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Overriding DocContent took the upstream module out of the bundle, and with it the `doc.css` it imports as a side effect. Every page rendering through DocLayout lost its article typography, table scroll container and heading anchors — while the rest of the theme stayed intact, which is why only some styles went missing. The sole reference to the upstream component was a `typeof`, erased at compile time, so the bundler saw a module it could drop. - Delegate to the upstream component when a page has no hoisted bands, which keeps that module and its stylesheet in the dependency graph. - Move the hoisting variant into its own component, so useScrollAfterNav is not called after a conditional return. DocContent survives client-side navigation, so its hook count has to stay constant. --- theme/components/DocContent.tsx | 48 ++++++++++++++++++++++++--------- 1 file changed, 36 insertions(+), 12 deletions(-) diff --git a/theme/components/DocContent.tsx b/theme/components/DocContent.tsx index aa9ba82..4dd4cb5 100644 --- a/theme/components/DocContent.tsx +++ b/theme/components/DocContent.tsx @@ -1,6 +1,6 @@ import type { ComponentProps } from "react" import { MDXProvider } from "@mdx-js/react" -import { Content, usePage, useSite } from "@rspress/core/runtime" +import { Content, useFrontmatter, usePage, useSite } from "@rspress/core/runtime" import { Callout, DocContent as OriginalDocContent, @@ -27,23 +27,25 @@ function FallbackTitle() { } /** - * Mirrors `@rspress/core`'s `DocContent`, with one addition: the bands a page - * marks `placement: top` render between the page title and the article body. + * Upstream's `DocContent`, with one addition: the bands a page marks + * `placement: top` render between the page title and the article body. * * Rspress has no slot for that position. `beforeDocContent` sits above the * title, and `afterDocContent` and the `Layout`'s `afterDoc` both sit below the * whole article — which is why sections used to land at the foot of the - * products page. Replacing this component is the only way in, so it is a - * deliberate copy of upstream and is worth re-diffing whenever Rspress is - * upgraded. Everything below comes straight from upstream: `useScrollAfterNav` - * restores the scroll position after a client-side navigation, and the MDX - * component map is layered as defaults, then the caller's overrides, then the - * placeholder the compiled MDX uses for callouts. + * products page. Replacing the component is the only way in. * - * Every page rendering through `DocLayout` goes through here, so be careful: - * a mistake affects blogs and project pages as much as the marketing ones. + * Everything except the inserted `` is copied from upstream: + * `useScrollAfterNav` restores the scroll position after a client-side + * navigation, and the MDX component map is layered as defaults, then the + * caller's overrides, then the placeholder the compiled MDX uses for callouts. + * Worth re-diffing whenever Rspress is upgraded. + * + * A separate component rather than a branch inside `DocContent`, because the + * hook below must not be called conditionally — `DocContent` survives + * client-side navigation, so the number of hooks it calls has to stay constant. */ -export function DocContent({ +function HoistedDocContent({ components, isOverviewPage = false, beforeDocContent, @@ -67,3 +69,25 @@ export function DocContent({ ) } + +/** + * Renders the page body. Pages with nothing to hoist delegate to upstream + * rather than re-rendering its markup here. + * + * That is not only about drift. Upstream's module carries `doc.css` as a + * side-effect import, and the only reference to the component above is in a + * `typeof` — which a bundler may treat as no reference at all. It then drops + * the module, and the article stylesheet is dropped with it. Keeping a real + * render path to upstream is what holds those styles in the bundle. + */ +export function DocContent(props: DocContentProps) { + const { frontmatter } = useFrontmatter() + + const raw = frontmatter?.sections + const hasHoistedBands = + Array.isArray(raw) && + raw.some((entry) => (entry as { placement?: unknown } | null)?.placement === "top") + + if (!hasHoistedBands) return + return +}