improvement(docs): clean up leftovers from the code-block alignment PR (#6825)

* improvement(docs): clear leftovers from the reverted revisions

A cleanup pass over the final state. Every finding was residue from an approach
this PR tried and abandoned, or a claim that stopped being true when it did.

- Delete the copy-button svg sizing rule: a later rule sets `display: none` on
  that same element ungated, so sizing it was never observable. Superseded by
  the mask approach.
- Drop the paragraph in page.tsx arguing about a custom Shiki factory. The
  factory was deleted; nothing configures one now.
- Correct shiki-curl-json.ts, which still claimed the grammar "reaches the
  client path too". It does not — that was the justification for choosing a
  grammar over a transformer, so leaving it stated the opposite of the truth.
  Now records where it applies, where it does not, and why not to retry.
- Correct the global.css section header, which claimed the component owns the
  shell while the next rule defines it here.
- Qualify the `--copy-glyph` declarations with `:has(> svg[class*="lucide"])`,
  which the group's own comment asserts of every rule in it.
- Correct `getCode`'s TSDoc: the gutter is a `::before`, and pseudo-element
  content never reaches `textContent`, so line numbers were never what the
  clone guards. It guards transformer-emitted `.nd-copy-ignore` nodes.
- Compose `chipGeometryClass` and emcn's `ChipChevronDown` in the API example
  selector instead of restating their literals.
- Merge the duplicated `div[role="region"]` rule. The tablist pair stays split:
  biome's `noDuplicateProperties` reads a nested `@variant` setting the same
  property as a duplicate and fails the build — recorded so it is not remerged.
- Note that fumadocs ships its own gutter for `lines`-meta fences, which cannot
  be suppressed from here and would paint a second column.

* fix(docs): drop a highlighter registration that can never fire

fumadocs-openapi calls `renderCodeBlock` with a hard-coded `"json"` from both of
its call sites (`request-tabs.js:76`, `response-tabs.js:48`), so the docs
`CodeBlock` it routes through never receives a shell language. The
`getHighlighter('js', { langs: [curlJsonBodyGrammar] })` registering the
shell-scoped JSON-body injection therefore did nothing but await on every API
sample render, and the docblock claiming the grammar covers those samples was
wrong.

- Delete the call and its imports.
- State the grammar's real coverage: prose fences only, via `langs`. Both API
  reference paths are unreachable — samples are JSON, and the cURL usage tabs
  highlight client-side off fumadocs' own factory.
- Correct `code-block.tsx`'s TSDoc, which still said API samples come from
  fumadocs' own renderer. They come through this component; `UsageTab` is the
  renderer that bypasses it.
- Re-home a comment orphaned when two CSS rules merged — it had drifted onto
  the rule below and read as documenting it.
- Drop a `.nd-copy-ignore` claim about transformers emitting those nodes;
  nothing here does, and upstream parity is the reason the clone exists.
This commit is contained in:
Waleed
2026-08-18 15:28:02 -07:00
committed by GitHub
parent e522bc4c5d
commit 3ff91f0439
5 changed files with 60 additions and 57 deletions
+1 -11
View File
@@ -1,5 +1,5 @@
import type React from 'react'
import { getHighlighter, highlight } from 'fumadocs-core/highlight'
import { highlight } from 'fumadocs-core/highlight'
import type { Root } from 'fumadocs-core/page-tree'
import { findNeighbour } from 'fumadocs-core/page-tree'
import type { ApiPageProps } from 'fumadocs-openapi/ui'
@@ -18,7 +18,6 @@ import { Heading } from '@/components/ui/heading'
import { ResponseSection } from '@/components/ui/response-section'
import { i18n } from '@/lib/i18n'
import { getApiSpecContent, getAuthenticatedCodeSamples, openapi } from '@/lib/openapi'
import { curlJsonBodyGrammar } from '@/lib/shiki-curl-json'
import { simShikiOptions } from '@/lib/shiki-theme'
import { type PageData, source } from '@/lib/source'
import { DOCS_BASE_URL } from '@/lib/urls'
@@ -77,17 +76,8 @@ function stripLocalePrefix(url: string, lang: string): string {
* rather than fumadocs-openapi's built-in one, so those blocks get the emcn copy control
* instead of fumadocs' lucide clipboard. Mirrors the default renderer — same `highlight` call,
* same `Pre` component, same `my-0` — differing only in which shell wraps the result.
*
* One asymmetry: `highlight` resolves fumadocs' shared `defaultShikiFactory`, while the renderer
* this replaces uses whatever `shiki` factory the page was configured with. They are the same
* object because that factory is also the default; passing a custom one would be honored on API
* markdown and ignored here.
*/
async function ApiCodeBlock({ lang, code }: { lang: string; code: string }) {
// Registers the injection on the shared highlighter `highlight` resolves; an injection is a
// property of the highlighter, not a per-call option. Idempotent — already-loaded grammars are
// skipped.
await getHighlighter('js', { langs: [curlJsonBodyGrammar] })
return (
<CodeBlock className='my-0'>
{await highlight(code, { lang, ...simShikiOptions, components: { pre: Pre } })}
+21 -27
View File
@@ -1603,15 +1603,17 @@ main article tbody tr:last-child td {
/* Code blocks — platform field chrome.
The shell itself (radius, border, fill) is owned by components/ui/code-block.tsx, the way
an emcn component owns its chrome. What remains here is the styling of fumadocs internals
that component cannot reach: the title row it renders from a `title` prop, the scroll
viewport, and the tab strip that `<Tabs>` puts above a fence. */
Three renderers emit these figures and only two go through components/ui/code-block.tsx, so
the shell and the fumadocs internals that component cannot reach — the title row, the scroll
viewport, the tab strip `<Tabs>` puts above a fence — are all defined here. The component
keeps only the copy control and the prose margin. */
/* Local aliases, not new design values: each is an existing platform token whose light and
dark halves differ. Naming the pair once lets the rules below be written without a `.dark`
twin, which would otherwise have to restate their `:has()` selectors — the double-`:has()`
the tabbed group is matched by, and the sibling-combinator one the gutter is matched by. */
/* Local aliases, not new design values: each names an existing platform token pair whose light
and dark halves differ (`--surface-5`/`--code-bg`, `--text-muted`/`--code-line-number`).
`--code-surface` has three consumers. `--code-gutter` has one, and is an alias anyway because
a `.dark` twin would have to restate the sibling-combinator `:has()` its consumer is matched
by — the expensive form, which re-checks on every line insertion. */
:root {
--code-surface: var(--surface-5);
--code-gutter: var(--text-muted);
@@ -1689,10 +1691,9 @@ figure.shiki > div:first-child:has(figcaption) svg {
The `!important` and the viewport selector are belt-and-braces, not strictly required —
fumadocs' declaration is (0,2,0) and these selectors are (0,3,1) and (0,4,1), so they
already win, and nothing declares this property on the viewport. They are kept because
getting this wrong puts the line numbers on top of the code, which shipped once already, and
because the specificity of `:has()` and `:not()` is easy to miscount in exactly the
direction that reintroduces it. Remove them only alongside a visual check.
already win, and nothing declares this property on the viewport. They are kept because the
specificity of `:has()` and `:not()` is easy to miscount in the direction that puts the line
numbers on top of the code. Remove them only alongside a visual check.
*/
figure.shiki:has(.line ~ .line),
figure.shiki:has(.line ~ .line) > div[role="region"],
@@ -1733,6 +1734,9 @@ figure.shiki code:has(.line ~ .line) .line::before {
the code surface. The viewport is the one box all three renderers agree on. */
figure.shiki > div[role="region"] {
background-color: var(--code-surface);
/* fumadocs ships 14px of vertical padding, the platform's viewer 8px; 10px splits them and
keeps a single-line fence from looking hollow at the tighter 21px line box. */
padding-block: 10px;
}
/* Shiki emits `--shiki-*-bg` custom properties under `defaultColor: false`; keep the `pre` clear
@@ -1741,13 +1745,6 @@ figure.shiki pre {
background-color: transparent;
}
/* Viewport padding — fumadocs ships 14px vertical; the platform's viewer uses 8px. Split the
difference at 10px, which keeps a single-line fence from looking hollow at the tighter
21px line box. */
figure.shiki > div[role="region"] {
padding-block: 10px;
}
/* Untitled blocks float the copy control over the code, so the last column has to clear it:
an 8px offset plus emcn's 20px icon button, with room to breathe. fumadocs reserves 32px,
which the glyphs run into.
@@ -1802,12 +1799,6 @@ figure.shiki button[aria-label="Copied Text"]:has(> svg[class*="lucide"]) {
color 150ms;
}
figure.shiki button[aria-label="Copy Text"] > svg[class*="lucide"],
figure.shiki button[aria-label="Copied Text"] > svg[class*="lucide"] {
width: 14px;
height: 14px;
}
figure.shiki button[aria-label="Copy Text"]:has(> svg[class*="lucide"]) {
@variant hover-hover {
background-color: var(--surface-active);
@@ -1849,11 +1840,11 @@ figure.shiki button[aria-label$="Text"]:has(> svg[class*="lucide"])::before {
-webkit-mask-size: contain;
}
figure.shiki button[aria-label="Copy Text"] {
figure.shiki button[aria-label="Copy Text"]:has(> svg[class*="lucide"]) {
--copy-glyph: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-1 -2 24 24' fill='none' stroke='%23000' stroke-width='1.25' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M14.25 0.75H2.75C1.64543 0.75 0.75 1.64543 0.75 2.75V14.25'/%3E%3Crect x='5.25' y='5.25' width='14' height='14' rx='2'/%3E%3C/svg%3E");
}
figure.shiki button[aria-label="Copied Text"] {
figure.shiki button[aria-label="Copied Text"]:has(> svg[class*="lucide"]) {
--copy-glyph: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-1 -2 24 24' fill='none' stroke='%23000' stroke-width='1.25' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M18.25 2.75L7.25 15.75L1.75 10.25'/%3E%3C/svg%3E");
}
@@ -1901,6 +1892,9 @@ div:has(> div > figure.shiki) > [role="tablist"] button {
color 150ms;
}
/* Kept as its own rule rather than nested into the block above: biome's
`noDuplicateProperties` reads a nested `@variant` setting the same property as a duplicate
declaration and fails the build. */
div:has(> div > figure.shiki) > [role="tablist"] button {
@variant hover-hover {
color: var(--text-body);
@@ -2,8 +2,14 @@
import type { ComponentProps } from 'react'
import { useId } from 'react'
import { chipFieldSurfaceClass, chipFieldTextClass, chipHoverSurfaceClass, cn } from '@sim/emcn'
import { ChevronDown } from '@sim/emcn/icons'
import {
ChipChevronDown,
chipFieldSurfaceClass,
chipFieldTextClass,
chipGeometryClass,
chipHoverSurfaceClass,
cn,
} from '@sim/emcn'
import type { APIPageClientOptions } from 'fumadocs-openapi/ui/client'
type FumadocsAPIExampleSelector = NonNullable<
@@ -35,10 +41,11 @@ export function APIExampleSelector({ items, value, onValueChange }: APIExampleSe
* screen-reader behavior a custom listbox would have to rebuild.
*/
className={cn(
chipGeometryClass,
chipFieldSurfaceClass,
chipFieldTextClass,
chipHoverSurfaceClass,
'h-[30px] w-full appearance-none ps-2 pe-8 text-left'
'w-full appearance-none pe-8'
)}
>
{items.map((item) => (
@@ -47,7 +54,7 @@ export function APIExampleSelector({ items, value, onValueChange }: APIExampleSe
</option>
))}
</select>
<ChevronDown className='-translate-y-1/2 pointer-events-none absolute end-2 top-1/2 size-[14px] text-[var(--text-icon)]' />
<ChipChevronDown className='-translate-y-1/2 pointer-events-none absolute end-2 top-1/2' />
</div>
{selectedItem?.description && (
<p className='text-[var(--text-muted)] text-caption'>{selectedItem.description}</p>
+15 -11
View File
@@ -33,21 +33,24 @@ function CopyButton({ getCode }: { getCode: () => string }) {
}
/**
* Docs code block for prose fences, wired into the MDX `pre` mapping.
* Docs code block for prose fences and the API reference's request/response samples — the MDX
* `pre` mapping and fumadocs-openapi's `renderCodeBlock` both render it.
*
* The shell — radius, hairline, fill — is not set here. Request and response samples in the
* API reference come from fumadocs-openapi's own renderer, so the two share chrome through a
* `figure.shiki` rule in `global.css` instead; see the note there. What stays here is the
* part only this path has: the copy control, and the `my-4` prose rhythm that API samples,
* which sit flush in their panel, must not inherit.
* The shell — radius, hairline, fill — is not set here. A third renderer, fumadocs-openapi's
* `UsageTab`, emits these figures without going through any component, so all three share
* chrome through a `figure.shiki` rule in `global.css` instead; see the note there. What stays
* here is the copy control, and the `my-4` prose rhythm that API samples, which sit flush in
* their panel, override with `my-0`.
*/
export function CodeBlock({ title, ...props }: React.ComponentProps<typeof FumadocsCodeBlock>) {
const figureRef = useRef<HTMLElement>(null)
/**
* Reads the block's text the way fumadocs does: from a clone, with `.nd-copy-ignore` nodes
* replaced by newlines. Those nodes carry rendered gutter and diff markers, so copying the
* live `textContent` would paste line numbers along with the code.
* Reads the block's text the way fumadocs' own `CopyButton` does: from a clone, with
* `.nd-copy-ignore` nodes replaced by newlines — kept in step with upstream so a fence that
* gains such a node copies the same text there and here. (The line-number gutter is a
* `::before`, and pseudo-element content never reaches `textContent`, so it is not what this
* guards.)
*/
function getCode() {
const pre = figureRef.current?.getElementsByTagName('pre').item(0)
@@ -65,8 +68,9 @@ export function CodeBlock({ title, ...props }: React.ComponentProps<typeof Fumad
className={cn('my-4', props.className)}
allowCopy={false}
/**
* fumadocs' own `className` is discarded rather than merged — its untitled-block variant
* carries a `backdrop-blur-lg` that goes milky over an opaque fill.
* The `className` fumadocs passes this render prop is deliberately neither destructured nor
* merged — its untitled-block variant carries a `backdrop-blur-lg` that goes milky over an
* opaque fill.
*/
Actions={() => (
<div className={cn('flex items-center', title ? '-me-1' : 'absolute top-2 right-2 z-[1]')}>
+12 -4
View File
@@ -14,10 +14,18 @@ import type { LanguageRegistration } from 'shiki'
* when it owns the opening brace. Entering mid-string, keys keep `string.quoted.double.json`
* and stay string-colored, which is the entire difference this exists to remove. Hence the
* hand-written patterns below, which name that scope directly.
* - **A Shiki transformer.** A transformer can re-tokenize the body correctly, but it is a
* function, and the API reference's request tabs highlight in the browser off a `shikiOptions`
* object passed through RSC — where "Functions cannot be passed directly to Client
* Components". A grammar is plain data, so it reaches the client path too.
* - **A Shiki transformer.** A transformer re-tokenizes the body correctly, but it is a function,
* and `shikiOptions` is forwarded into a client component — "Functions cannot be passed
* directly to Client Components" takes down every API reference page. A grammar is plain data,
* so it survives that boundary.
*
* Applies to prose fences only, via `langs` on the MDX pipeline. Not the API reference:
* fumadocs-openapi calls `renderCodeBlock` with a hard-coded `"json"` for request and response
* samples, so a shell injection can never fire there, and its cURL usage tabs highlight in the
* browser off fumadocs' own factory — `ClientCodeBlockProvider` sits in a `"use client"` module
* the package does not expose through its `exports` map, so reaching it means importing
* `fumadocs-openapi/ui/base` from client code and dragging `remark` and
* `@fumari/json-schema-ts` into the browser bundle. That broke the deployment once.
*
* The opening brace requires a `}`, a quoted key, or end-of-line after it. That is what keeps
* `awk '{print $1}'` out, while still matching a body whose brace ends the line — Oniguruma