diff --git a/tools/web-ux/browser-tests/memory_document_fixture_server.ts b/tools/web-ux/browser-tests/memory_document_fixture_server.ts
index 3d9c44183..ba1c24614 100644
--- a/tools/web-ux/browser-tests/memory_document_fixture_server.ts
+++ b/tools/web-ux/browser-tests/memory_document_fixture_server.ts
@@ -25,7 +25,7 @@ const paragraphs = Array.from(
const bodyMd =
`# Workspace Memory\n\nThis document is read-only.\n\n| Very wide heading | Another heading | Decision |\n| --- | --- | --- |\n| ${
"wide-value-".repeat(16)
- } | stable | keep scrolling local |\n\n\`\`\`rust\nfn example() { println!("${
+ } | stable | keep scrolling local |\n\n##### Deep document heading\n\n###### Deepest document heading\n\n\`\`\`rust\nfn example() { println!("${
"wide-code-".repeat(18)
}"); }\n\`\`\`\n\n[Safe link](https://example.com) [Unsafe link](javascript:alert(1))\n\n
\n\n${paragraphs}\n\n## T-661 UNIQUE END MARKER`;
diff --git a/tools/web-ux/browser-tests/memory_document_shell_test.ts b/tools/web-ux/browser-tests/memory_document_shell_test.ts
index 75f13de3d..54c8d403f 100644
--- a/tools/web-ux/browser-tests/memory_document_shell_test.ts
+++ b/tools/web-ux/browser-tests/memory_document_shell_test.ts
@@ -165,6 +165,21 @@ Deno.test("production Memory shell renders safe Markdown and scrolls to its end
);
assertEquals(await page.locator("blockquote").count() > 0, true);
assertEquals(await page.locator("ul li").count() > 0, true);
+ for (
+ const heading of [
+ { selector: "h5", text: "Deep document heading" },
+ { selector: "h6", text: "Deepest document heading" },
+ ]
+ ) {
+ const element = page.locator(heading.selector, { hasText: heading.text });
+ assertEquals(await element.count(), 1);
+ assert(
+ await element.evaluate((node) =>
+ Number.parseFloat((globalThis as any).getComputedStyle(node).fontSize) >= 12
+ ),
+ `${heading.selector} fell below the design language minimum text size`,
+ );
+ }
assertEquals(await page.locator('a[href="https://example.com"]').count(), 1);
assertEquals(await page.locator('a[href^="javascript:"]').count(), 0);
assertEquals(await page.locator("article img, article script").count(), 0);
diff --git a/web/workspace/src/lib/workspace/markdown/DocumentMarkdown.browser.spec.ts b/web/workspace/src/lib/workspace/markdown/DocumentMarkdown.browser.spec.ts
index 16b1c5084..0c1fe7808 100644
--- a/web/workspace/src/lib/workspace/markdown/DocumentMarkdown.browser.spec.ts
+++ b/web/workspace/src/lib/workspace/markdown/DocumentMarkdown.browser.spec.ts
@@ -10,6 +10,10 @@ test("renders document Markdown semantics through the shared safe policy", async
const source = [
"# Memory heading",
"",
+ "##### Deep heading",
+ "",
+ "###### Deepest heading",
+ "",
"A paragraph with **strong context** and `inline-code`.",
"",
"- first item",
@@ -36,10 +40,15 @@ test("renders document Markdown semantics through the shared safe policy", async
expect(container.querySelector("table")).not.toBeNull();
});
+ expect(container.querySelector("h5")?.textContent).toBe("Deep heading");
+ expect(container.querySelector("h6")?.textContent).toBe("Deepest heading");
expect(container.querySelector("strong")?.textContent).toBe("strong context");
expect(container.querySelectorAll("li")).toHaveLength(2);
- expect(container.querySelector("blockquote")?.textContent).toContain("durable quote");
- expect(container.querySelector('a[href="https://example.com"]')).not.toBeNull();
+ expect(container.querySelector("blockquote")?.textContent).toContain(
+ "durable quote",
+ );
+ expect(container.querySelector('a[href="https://example.com"]')).not
+ .toBeNull();
expect(container.querySelectorAll("a")).toHaveLength(1);
expect(container.querySelector("img")).toBeNull();
expect(container.querySelector("script")).toBeNull();
@@ -52,13 +61,16 @@ test("renders document Markdown semantics through the shared safe policy", async
);
expect(tableRegion?.tabIndex).toBe(0);
expect(codeRegion?.tabIndex).toBe(0);
- expect(codeRegion?.querySelector("pre > code")?.textContent).toContain("println!");
+ expect(codeRegion?.querySelector("pre > code")?.textContent).toContain(
+ "println!",
+ );
});
test("wraps long prose and identifiers without turning them into document-level controls", async () => {
const longIdentifier = "memory-identifier-".repeat(40);
const { container } = render(DocumentMarkdown, {
- text: `Long value: ${longIdentifier}\n\nunsafe HTML
`,
+ text:
+ `Long value: ${longIdentifier}\n\nunsafe HTML
`,
});
await waitFor(() => expect(container.textContent).toContain(longIdentifier));
diff --git a/web/workspace/src/lib/workspace/markdown/DocumentMarkdown.svelte b/web/workspace/src/lib/workspace/markdown/DocumentMarkdown.svelte
index 6eeef04c2..77add1dce 100644
--- a/web/workspace/src/lib/workspace/markdown/DocumentMarkdown.svelte
+++ b/web/workspace/src/lib/workspace/markdown/DocumentMarkdown.svelte
@@ -41,7 +41,9 @@
:global(.document-markdown h2),
:global(.document-markdown h3),
- :global(.document-markdown h4) {
+ :global(.document-markdown h4),
+ :global(.document-markdown h5),
+ :global(.document-markdown h6) {
margin: var(--space-6) 0 var(--space-2);
color: var(--text-strong);
font-size: var(--font-size-body);
@@ -53,7 +55,9 @@
}
:global(.document-markdown h3),
- :global(.document-markdown h4) {
+ :global(.document-markdown h4),
+ :global(.document-markdown h5),
+ :global(.document-markdown h6) {
font-weight: 600;
}