diff --git a/apps/website/src/app/global.css b/apps/website/src/app/global.css index ccda4df2b..f93e1e61e 100644 --- a/apps/website/src/app/global.css +++ b/apps/website/src/app/global.css @@ -50,6 +50,31 @@ pre { overflow-x: auto; } +/* + * Shiki `codeToHtml` output — components/landing/HighlightedCode.tsx and + * components/solutions/SolutionCodeBlock.tsx. + * + * Shiki writes the theme background INLINE on the
but emits no padding,
+ * so without this the code text sits flush against the dark surface's edges.
+ * That padding used to come from a global `.shiki` rule, deleted in #863 on a
+ * docs-only survey ("zero elements matched") that missed these two TSX call
+ * sites — rehype-pretty-code really does not emit `.shiki`, but `codeToHtml`
+ * does. Selector is `pre.shiki`, not `.shiki`: both call sites put the class on
+ * a wrapper too, and padding there lands OUTSIDE the dark surface.
+ *
+ * Held by a style contract (styles/style-contracts.spec.ts) — this comment is
+ * exactly the tell that registry asks for.
+ */
+pre.shiki {
+ margin: 0;
+ padding: 1.25rem 1.5rem;
+}
+pre.shiki code {
+ font-family: var(--font-mono), "JetBrains Mono", monospace;
+ font-size: inherit;
+ line-height: inherit;
+}
+
/* Long unbreakable tokens (e.g. package names like @threadplane/chat,
* file paths like app.config.ts) live inside marketing headings and pull
* the layout wider than the viewport on narrow phones. Allow breaking
diff --git a/apps/website/src/components/landing/HighlightedCode.spec.tsx b/apps/website/src/components/landing/HighlightedCode.spec.tsx
new file mode 100644
index 000000000..3bcbe3ad2
--- /dev/null
+++ b/apps/website/src/components/landing/HighlightedCode.spec.tsx
@@ -0,0 +1,46 @@
+// SPDX-License-Identifier: MIT
+import { describe, expect, it } from 'vitest';
+import { HighlightedCode } from './HighlightedCode';
+
+/**
+ * The markup half of the padding contract that regressed in #863.
+ *
+ * The CSS half — that `pre.shiki` actually declares a padding — is a style
+ * contract in styles/style-contracts.spec.ts. Both halves are needed and
+ * neither is sufficient: the contract survives this component switching
+ * highlighters (guarding a rule nothing emits any more), and these assertions
+ * survive the rule being deleted again as dead.
+ *
+ * What makes the pairing load-bearing is that Shiki puts the theme background
+ * INLINE on the while emitting no padding of its own. So the padding has
+ * to land on that same element — the wrapper carries `shiki` too, and
+ * padding there sits outside the dark surface as a light gutter.
+ */
+async function renderToHtml() {
+ // An async Server Component: await the element and read the markup it hands
+ // to `dangerouslySetInnerHTML`, no DOM needed.
+ const element = await HighlightedCode({ code: 'const answer = 42;' });
+ return element.props.dangerouslySetInnerHTML.__html as string;
+}
+
+describe('HighlightedCode', () => {
+ it('emits a for the pre.shiki contract to match', async () => {
+ expect(await renderToHtml()).toMatch(/', async () => {
+ // This is why padding must go on the and not on the wrapper.
+ expect(await renderToHtml()).toMatch(
+ /]*style="[^"]*background-color:/,
+ );
+ });
+
+ it('emits no padding of its own, leaving CSS as the only source', async () => {
+ expect(await renderToHtml()).not.toMatch(/]*style="[^"]*padding/);
+ });
+
+ it('puts `shiki` on the wrapper too, so the rule must stay pre-scoped', async () => {
+ const element = await HighlightedCode({ code: 'const answer = 42;' });
+ expect(element.props.className).toBe('shiki');
+ });
+});
diff --git a/apps/website/src/components/solutions/SolutionCodeBlock.tsx b/apps/website/src/components/solutions/SolutionCodeBlock.tsx
index 8c53c0ee3..f64465bf6 100644
--- a/apps/website/src/components/solutions/SolutionCodeBlock.tsx
+++ b/apps/website/src/components/solutions/SolutionCodeBlock.tsx
@@ -45,8 +45,9 @@ export async function SolutionCodeBlock({ code }: { code: SolutionCodeBlocks })
{block.label}
{/*
- Shiki emits a complete that already carries its own background,
- padding, and `overflow-x: auto`, so this wrapper owns only the frame.
+ Shiki emits a carrying its own background; its padding and
+ `overflow-x: auto` come from the `pre.shiki` / `pre` rules in
+ global.css, so this wrapper owns only the frame.
`overflow: hidden` is what makes the radius clip that background — it
must not be `auto`, which would nest a second scroll container around
a element that already scrolls and can show two scrollbars.
diff --git a/apps/website/src/styles/landing.css b/apps/website/src/styles/landing.css
index 8f90f35d4..a2e6a475b 100644
--- a/apps/website/src/styles/landing.css
+++ b/apps/website/src/styles/landing.css
@@ -986,17 +986,20 @@
}
/* HighlightedCode — components/landing/HighlightedCode.tsx
- * `.shiki[data-ui="highlighted-code"]` (specificity 0,2,0) intentionally beats
- * the plain `.shiki` rule in docs.css (0,1,0) regardless of stylesheet import
- * order, so this landing-only override of margin/padding/font-size/line-height
- * keeps winning the way the inline style used to. */
+ * The wrapper owns only type scale; the frame around it
+ * (.home-code-frame, .lg-show-card, ...) owns radius and clipping. Padding
+ * belongs on the , which is where Shiki's dark background lives — putting
+ * it here would inset the code from a light gutter instead. The base
+ * `pre.shiki` padding is in global.css; this is the tighter landing scale. */
.shiki[data-ui="highlighted-code"] {
margin: 0;
border-radius: 0;
- padding: 16px 20px;
font-size: 0.78rem;
line-height: 1.65;
}
+.shiki[data-ui="highlighted-code"] > pre.shiki {
+ padding: 16px 20px;
+}
/* RenderCodeShowcase — components/landing/render/RenderCodeShowcase.tsx */
.render-code {
diff --git a/apps/website/src/styles/style-contracts.spec.ts b/apps/website/src/styles/style-contracts.spec.ts
index 955d8beaa..405067289 100644
--- a/apps/website/src/styles/style-contracts.spec.ts
+++ b/apps/website/src/styles/style-contracts.spec.ts
@@ -49,6 +49,14 @@ const CONTRACTS: StyleContract[] = [
'align-self': /align-self:\s*flex-start/,
},
},
+ {
+ file: '../app/global.css',
+ selector: 'pre.shiki',
+ why: "Shiki writes the theme background inline on the but emits no padding, so losing this sits the code flush against the dark surface's edges — on the homepage Code tabs, /langgraph, /render, /chat and every /solutions page. Deleted once already in #863, on a docs-only survey that concluded `.shiki` matched nothing.",
+ requires: {
+ padding: /padding:/,
+ },
+ },
{
file: 'docs.css',
selector: '.docs-control-plane [data-control-plane-pane]',