diff --git a/src/components/announcements/Markdown.tsx b/src/components/announcements/Markdown.tsx index b2c9946..a2cd35d 100644 --- a/src/components/announcements/Markdown.tsx +++ b/src/components/announcements/Markdown.tsx @@ -1,3 +1,4 @@ +import { createContext, useContext } from 'react'; import ReactMarkdown from 'react-markdown'; import remarkGfm from 'remark-gfm'; import type { ComponentPropsWithoutRef } from 'react'; @@ -6,6 +7,21 @@ export interface MarkdownProps { children: string; } +/** + * Whether the `code` being rendered sits inside a fenced block. + * + * react-markdown v10 removed the `inline` prop its `code` component used to + * receive, and the usual replacement — "treat `className: language-*` as a + * block" — is wrong: a fence written without a language gets no className at + * all and would silently fall back to inline styling. The `pre` component + * below is the only thing that reliably knows, so it says so here. + * + * Descendant CSS would be the other way to do this (`pre code { … }`), but + * the inline-code style sets its background through a `style` attribute, + * which no class from an ancestor can override. + */ +const InCodeBlock = createContext(false); + /** * The one place in this library that renders announcement body text. Deliberately * carries no `rehype-raw` and no `dangerouslySetInnerHTML` — react-markdown parses @@ -14,6 +30,12 @@ export interface MarkdownProps { * element the DOM would execute. That is the whole security model here: there is * nothing to sanitise because raw HTML is never turned into elements in the first * place. Do not add `rehype-raw` to this file. + * + * Every element markdown (plus GFM) can produce is given a style. An element + * left out does not fail loudly — it renders at browser defaults, which on + * this dark card means an invisible `
`, a borderless table and a quote + * indistinguishable from a paragraph. The composer accepts all of it, so all + * of it is styled here. */ export function Markdown({ children }: MarkdownProps) { return ( @@ -24,15 +46,86 @@ export function Markdown({ children }: MarkdownProps) { h1: props =>

, h2: props =>

, h3: props =>

, + // h4 and below share h3's treatment: past the third level the + // distinction is decorative, and an announcement that nests deeper + // than that has a structure problem no type scale will fix. What + // matters is that they stop rendering at browser defaults, which + // made an h4 smaller than the body text around it. + h4: props =>

, + h5: props =>

, + h6: props =>
, p: props =>

, ul: props =>