From fb72b4fee33671aa38f346c8e3096ca345ac35ee Mon Sep 17 00:00:00 2001 From: amosblomqvist Date: Wed, 23 Sep 2026 21:55:51 +0300 Subject: [PATCH] Bold renders the real element: the Bold color setting applies The owner's repro: the appearance Bold color never applied anywhere. Root cause: streamdown's default `strong` component renders bold as - NOT a element - while the app's ink plumbing (.lh-prose :where(strong, b) in globals.css, zero specificity by design) matches strong/b ELEMENTS. So bold text never took --lh-ink-strong: not the factory #c96b4a, not any settings boldColor (it rendered plain foreground at weight 600). Italic always worked because streamdown leaves `em` native - hence the asymmetry the owner noticed. Fix: a `strong` override in mdComponents (the stable module-level set, so Streamdown's memoized tree keeps identity) renders the semantic element: . Screen readers announce emphasis again, the designed token rule applies unmodified, and the weight reads 700 (the .lh-prose rule) instead of the span's 600. Both Streamdown surfaces (MessageResponse, reasoning) pass mdComponents, so every markdown surface is covered. Verified live: bold in seedplan now renders #c96b4a at weight 700 in a real ; after PUTting boldColor #7dd3fc the live strong flips to rgb(125,211,252) with no reload; italic unchanged (#6fa9d6 native em). Scratch record restored to unset. --- components/md-blocks.tsx | 18 ++++++++++++++++++ tests/render-markdown.test.tsx | 28 ++++++++++++++++++++++++++++ 2 files changed, 46 insertions(+) diff --git a/components/md-blocks.tsx b/components/md-blocks.tsx index c55a5e6..4d5d162 100644 --- a/components/md-blocks.tsx +++ b/components/md-blocks.tsx @@ -338,8 +338,26 @@ export function MathDisplay(props: ComponentProps<"span"> & ExtraProps) { // ─── The stable override set (module identity: Streamdown is memoized) ──────── +/** Bold as the REAL element (appearance fix, 2026-09-23): streamdown's + * default `strong` renders `` — and `.lh-prose :where(strong, b)` matches strong/b + * ELEMENTS, so neither the factory ink (#c96b4a) nor a settings + * boldColor ever landed on bold text (italic worked: streamdown leaves + * `em` native). Rendering the semantic element restores screen-reader + * emphasis AND lets the designed token rule apply unmodified — color + * --lh-ink-strong, weight 700 (beating the old 600 utility). */ +export function MarkdownStrong(props: ComponentProps<"strong"> & ExtraProps) { + const { children, className, node, ...rest } = props; + return ( + + {children} + + ); +} + export const mdComponents = { code: MarkdownCode, table: MarkdownTable, span: MathDisplay, + strong: MarkdownStrong, }; diff --git a/tests/render-markdown.test.tsx b/tests/render-markdown.test.tsx index 444c62d..a968eb6 100644 --- a/tests/render-markdown.test.tsx +++ b/tests/render-markdown.test.tsx @@ -212,3 +212,31 @@ describe("render — mermaid", () => { assert.match(html, />Retry (the dead Bold-color fix)", () => { + // Streamdown's default `strong` renders — and the app's ink token rule + // (.lh-prose :where(strong, b), globals.css) matches strong/b + // ELEMENTS, so bold never took the --lh-ink-strong color (neither the + // factory #c96b4a nor a settings boldColor). The mdComponents override + // renders the semantic element. + it("bold renders a real element inside .lh-prose", () => { + const html = render("plain **bold** plain"); + assert.ok( + html.includes('bold'), + "bold renders as a semantic strong element", + ); + assert.ok(html.includes("lh-prose"), "the markdown surface carries .lh-prose (the token rule's anchor)"); + }); + + it("no span-for-bold regression: the font-semibold span is gone", () => { + const html = render("plain **bold** plain"); + assert.ok(!html.includes('class="font-semibold"'), "streamdown's span/600 default must not render bold"); + }); + + it("italic stays native (the case that always worked)", () => { + const html = render("plain *emph* plain"); + assert.ok(html.includes(""), "italic renders a native em element"); + }); +});