diff --git a/src/prose/prose-example.ts b/src/prose/prose-example.ts index 62ee60dfe..8b6fe1f8a 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. @@ -82,3 +90,27 @@ 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 | +`) + +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 b6042042f..a9ba8c2ac 100644 --- a/src/prose/prose.module.css +++ b/src/prose/prose.module.css @@ -10,6 +10,15 @@ --reactist-prose-link-hover-underline: #006f85; --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. */ @@ -212,6 +221,66 @@ 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); +} + +/* 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; +} + +/* 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 + * { diff --git a/src/prose/prose.stories.tsx b/src/prose/prose.stories.tsx index f4c175203..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 } from './prose-example' +import { proseExample, proseTableColorsExample, proseTableOverflowExample } from './prose-example' import type { ProseProps } from './prose' @@ -68,3 +68,52 @@ 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 }, +} + +// +// Table colors +// + +export function ProseTableColorsStory() { + return ( + + + + ) +} + +ProseTableColorsStory.storyName = 'Table colors' +ProseTableColorsStory.parameters = { + chromatic: { disableSnapshot: false }, +}