From d15f62cf14c827c0fe7737b549ab35b82a1404dd Mon Sep 17 00:00:00 2001 From: Amir Date: Thu, 11 Jun 2026 12:53:23 +0200 Subject: [PATCH 1/3] feat(prose): add table styling support Tables rendered from Markdown (or raw HTML) inside were unstyled. Add bordered table styles with a public --reactist-prose-table-border custom property, horizontal scrolling for wide tables, and start-aligned headers that still respect explicit column alignment from Markdown renderers. Co-Authored-By: Claude Fable 5 --- src/prose/prose-example.ts | 8 +++++++ src/prose/prose.module.css | 48 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 56 insertions(+) diff --git a/src/prose/prose-example.ts b/src/prose/prose-example.ts index 62ee60dfe..4cd708e18 100644 --- a/src/prose/prose-example.ts +++ b/src/prose/prose-example.ts @@ -74,6 +74,14 @@ Now a nested list: Do not bump wooden spoon or it will fall. +And here's a table, with column alignment: + +| Item | Quantity | Price | +| ------- | :------: | ----: | +| Carrots | 3 | $1.20 | +| Celery | 1 | $0.95 | +| Lentils | 500g | $2.40 | + --- A horizontal rule follows. diff --git a/src/prose/prose.module.css b/src/prose/prose.module.css index b6042042f..f626606aa 100644 --- a/src/prose/prose.module.css +++ b/src/prose/prose.module.css @@ -10,6 +10,8 @@ --reactist-prose-link-hover-underline: #006f85; --reactist-prose-horizontal-rule-color: var(--reactist-divider-primary); + + --reactist-prose-table-border: var(--reactist-divider-primary); } /* Internals for spacing. These are not considered public API. */ @@ -212,6 +214,52 @@ overflow: auto; } +/* tables */ + +.prose table { + /* Let wide tables scroll horizontally instead of stretching the surrounding content. */ + display: block; + width: max-content; + max-width: 100%; + overflow: auto; + margin-top: var(--reactist-prose-space-2); + border-collapse: collapse; + + /* `.prose`'s `word-break: break-word` acts like `overflow-wrap: anywhere`, shrinking each + word's min-content width to a single character. That lets wide tables squeeze into + `max-width: 100%` by breaking words mid-word instead of overflowing into the scroll above. */ + word-break: normal; +} + +.prose th, +.prose td { + border: 1px solid var(--reactist-prose-table-border); + padding: calc(var(--reactist-prose-space-1) / 2) var(--reactist-prose-space-1); +} + +.prose th { + font-weight: 600; +} + +/* Browsers center `th` by default; align with `td` instead. Markdown renderers express explicit + column alignment either as an inline `text-align` style (which wins over this rule) or as the + `align` attribute (which would lose to it, hence the `:not([align])` guard). */ +.prose th:not([align]) { + text-align: start; +} + +/* Markdown table cells hold inline content only, but raw HTML tables can nest block elements + (e.g. `

`), whose margins would add awkward vertical spacing. */ +.prose th > :first-child, +.prose td > :first-child { + margin-top: 0; +} + +.prose th > :last-child, +.prose td > :last-child { + margin-bottom: 0; +} + /* headings in relation to other tags */ .prose h1 + * { From b723fe8ef66289a916fb50b0f4f98fb21c62b574 Mon Sep 17 00:00:00 2001 From: Amir Date: Thu, 11 Jun 2026 13:17:28 +0200 Subject: [PATCH 2/3] feat(prose): add a width-constrained table overflow story The playground example only shows a narrow table, so Chromatic never exercised the horizontal-scroll layout for wide tables. A dedicated story renders a table with unbreakable links inside a 400px container, guaranteeing the overflow path is snapshotted at any viewport size. Co-Authored-By: Claude Fable 5 --- src/prose/prose-example.ts | 10 ++++++++++ src/prose/prose.stories.tsx | 24 +++++++++++++++++++++++- 2 files changed, 33 insertions(+), 1 deletion(-) diff --git a/src/prose/prose-example.ts b/src/prose/prose-example.ts index 4cd708e18..99134d355 100644 --- a/src/prose/prose-example.ts +++ b/src/prose/prose-example.ts @@ -90,3 +90,13 @@ A horizontal rule follows. The End `) + +export const proseTableOverflowExample = marked(` +Tables whose content cannot wrap any further (e.g. long unbreakable links) scroll horizontally +instead of stretching the surrounding content: + +| Resource | Reference | +| -------- | --------- | +| Prose styles | https://github.com/Doist/reactist/blob/main/src/prose/prose.module.css | +| Prose stories | https://github.com/Doist/reactist/blob/main/src/prose/prose.stories.tsx | +`) diff --git a/src/prose/prose.stories.tsx b/src/prose/prose.stories.tsx index f4c175203..9e4ac931f 100644 --- a/src/prose/prose.stories.tsx +++ b/src/prose/prose.stories.tsx @@ -3,7 +3,7 @@ import * as React from 'react' import { Box } from '../box' import { Prose } from './prose' -import { proseExample } from './prose-example' +import { proseExample, proseTableOverflowExample } from './prose-example' import type { ProseProps } from './prose' @@ -68,3 +68,25 @@ ProsePlaygroundStory.argTypes = { dangerouslySetInnerHTML: { control: false }, exceptionallySetClassName: { control: false }, } + +// +// Table overflow +// + +export function ProseTableOverflowStory() { + // The narrow container guarantees the table overflows into a horizontal scroll at any + // viewport size, so the snapshot exercises the scrolling layout deterministically. + return ( + + + + ) +} + +ProseTableOverflowStory.storyName = 'Table overflow' +ProseTableOverflowStory.parameters = { + chromatic: { disableSnapshot: false }, +} From b2d675294fe5799d000b21f0428e955be6d62d3d Mon Sep 17 00:00:00 2001 From: Amir Date: Thu, 11 Jun 2026 19:33:05 +0200 Subject: [PATCH 3/3] feat(prose): add table background custom properties Allow consumers to configure the table header row background and odd/even body row backgrounds via --reactist-prose-table-header-fill, --reactist-prose-table-row-odd-fill, and --reactist-prose-table-row-even-fill. All default to transparent, so tables render unchanged unless customized. Co-Authored-By: Claude Fable 5 --- src/prose/prose-example.ts | 14 ++++++++++++++ src/prose/prose.module.css | 21 +++++++++++++++++++++ src/prose/prose.stories.tsx | 29 ++++++++++++++++++++++++++++- 3 files changed, 63 insertions(+), 1 deletion(-) diff --git a/src/prose/prose-example.ts b/src/prose/prose-example.ts index 99134d355..8b6fe1f8a 100644 --- a/src/prose/prose-example.ts +++ b/src/prose/prose-example.ts @@ -100,3 +100,17 @@ instead of stretching the surrounding content: | Prose styles | https://github.com/Doist/reactist/blob/main/src/prose/prose.module.css | | Prose stories | https://github.com/Doist/reactist/blob/main/src/prose/prose.stories.tsx | `) + +export const proseTableColorsExample = marked(` +Consumers can give the header row and odd/even body rows their own background colors via the +\`--reactist-prose-table-header-fill\`, \`--reactist-prose-table-row-odd-fill\`, and +\`--reactist-prose-table-row-even-fill\` custom properties (all default to \`transparent\`): + +| Item | Quantity | Price | +| ------- | :------: | ----: | +| Carrots | 3 | $1.20 | +| Celery | 1 | $0.95 | +| Lentils | 500g | $2.40 | +| Onions | 2 | $0.80 | +| Stock | 1L | $3.10 | +`) diff --git a/src/prose/prose.module.css b/src/prose/prose.module.css index f626606aa..a9ba8c2ac 100644 --- a/src/prose/prose.module.css +++ b/src/prose/prose.module.css @@ -12,6 +12,13 @@ --reactist-prose-horizontal-rule-color: var(--reactist-divider-primary); --reactist-prose-table-border: var(--reactist-divider-primary); + + /* `transparent` (the initial value of `background-color`) rather than `initial`, which is + special-cased in custom property values to mean "guaranteed invalid", or `inherit`, which + backgrounds achieve through transparency rather than inheritance. */ + --reactist-prose-table-header-fill: transparent; + --reactist-prose-table-row-odd-fill: transparent; + --reactist-prose-table-row-even-fill: transparent; } /* Internals for spacing. These are not considered public API. */ @@ -237,6 +244,20 @@ padding: calc(var(--reactist-prose-space-1) / 2) var(--reactist-prose-space-1); } +/* The header fill goes on the row rather than `th` so that row headers in table bodies + (`…`) stripe with their row instead of picking up the header fill. */ +.prose thead tr { + background-color: var(--reactist-prose-table-header-fill); +} + +.prose tbody tr:nth-child(odd) { + background-color: var(--reactist-prose-table-row-odd-fill); +} + +.prose tbody tr:nth-child(even) { + background-color: var(--reactist-prose-table-row-even-fill); +} + .prose th { font-weight: 600; } diff --git a/src/prose/prose.stories.tsx b/src/prose/prose.stories.tsx index 9e4ac931f..6093adf3f 100644 --- a/src/prose/prose.stories.tsx +++ b/src/prose/prose.stories.tsx @@ -3,7 +3,7 @@ import * as React from 'react' import { Box } from '../box' import { Prose } from './prose' -import { proseExample, proseTableOverflowExample } from './prose-example' +import { proseExample, proseTableColorsExample, proseTableOverflowExample } from './prose-example' import type { ProseProps } from './prose' @@ -90,3 +90,30 @@ ProseTableOverflowStory.storyName = 'Table overflow' ProseTableOverflowStory.parameters = { chromatic: { disableSnapshot: false }, } + +// +// Table colors +// + +export function ProseTableColorsStory() { + return ( + + + + ) +} + +ProseTableColorsStory.storyName = 'Table colors' +ProseTableColorsStory.parameters = { + chromatic: { disableSnapshot: false }, +}