shelving 1.225.0 → 1.227.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. package/markup/rule/fenced.js +6 -2
  2. package/markup/rule/table.js +2 -1
  3. package/package.json +1 -1
  4. package/ui/app/App.module.css +4 -0
  5. package/ui/block/Block.d.ts +26 -3
  6. package/ui/block/Block.js +37 -5
  7. package/ui/block/Block.module.css +87 -3
  8. package/ui/block/Block.tsx +65 -7
  9. package/ui/block/Heading.module.css +1 -2
  10. package/ui/block/Preformatted.d.ts +1 -1
  11. package/ui/block/Preformatted.js +1 -1
  12. package/ui/block/Preformatted.module.css +0 -1
  13. package/ui/block/Preformatted.tsx +1 -1
  14. package/ui/block/Prose.js +2 -2
  15. package/ui/block/Prose.tsx +2 -2
  16. package/ui/block/Subheading.module.css +1 -2
  17. package/ui/block/Table.d.ts +6 -1
  18. package/ui/block/Table.js +5 -1
  19. package/ui/block/Table.module.css +0 -25
  20. package/ui/block/Table.tsx +7 -6
  21. package/ui/block/Title.module.css +1 -2
  22. package/ui/block/index.d.ts +0 -2
  23. package/ui/block/index.js +0 -2
  24. package/ui/block/index.ts +0 -2
  25. package/ui/docs/DirectoryPage.d.ts +1 -1
  26. package/ui/docs/DirectoryPage.js +5 -2
  27. package/ui/docs/DirectoryPage.tsx +9 -2
  28. package/ui/docs/DocumentationPage.js +1 -1
  29. package/ui/docs/DocumentationPage.tsx +1 -1
  30. package/ui/form/FormFooter.js +1 -1
  31. package/ui/form/FormFooter.tsx +1 -1
  32. package/ui/inline/Code.module.css +1 -1
  33. package/ui/block/Figure.d.ts +0 -6
  34. package/ui/block/Figure.js +0 -5
  35. package/ui/block/Figure.module.css +0 -27
  36. package/ui/block/Figure.tsx +0 -16
  37. package/ui/block/Section.d.ts +0 -28
  38. package/ui/block/Section.js +0 -26
  39. package/ui/block/Section.module.css +0 -28
  40. package/ui/block/Section.tsx +0 -53
@@ -1,4 +1,4 @@
1
- import { jsx as _jsx } from "react/jsx-runtime";
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { createMarkupRule } from "../MarkupRule.js";
3
3
  import { BLOCK_CONTENT_REGEXP, BLOCK_START_REGEXP, createBlockRegExp, LINE_CONTENT_REGEXP, LINE_SPACE_REGEXP } from "../util/regexp.js";
4
4
  const FENCE = "`{3,}|~{3,}";
@@ -12,4 +12,8 @@ const FENCE = "`{3,}|~{3,}";
12
12
  * - Markdown-style four-space indent syntax is not supported (only fenced code since it's less confusing and more common).
13
13
  */
14
14
  export const FENCED_RULE = createMarkupRule(createBlockRegExp(`(?<code>${BLOCK_CONTENT_REGEXP})`, `${BLOCK_START_REGEXP}(?<fence>${FENCE}) *(?<title>${LINE_CONTENT_REGEXP})\n`, // Starts with the fence
15
- `(?:${LINE_SPACE_REGEXP}*\n\\k<fence>(?:\\s*\\n)?|$)`), (key, { title, code }) => (_jsx("pre", { children: _jsx("code", { title: title?.trim() || undefined, children: code.trim() }) }, key)), ["block", "list"], 10);
15
+ `(?:${LINE_SPACE_REGEXP}*\n\\k<fence>(?:\\s*\\n)?|$)`), (key, { title, code }) => {
16
+ const caption = title?.trim();
17
+ // Scrollable region pattern: focusable, labelled <figure> wraps the code block so keyboard users can arrow-scroll wide lines.
18
+ return caption ? (_jsxs("figure", { tabIndex: 0, role: "region", "aria-label": "Scrollable region", children: [_jsx("figcaption", { children: caption }), _jsx("pre", { children: _jsx("code", { children: code.trim() }) })] }, key)) : (_jsx("figure", { tabIndex: 0, role: "region", "aria-label": "Scrollable region", children: _jsx("pre", { children: _jsx("code", { children: code.trim() }) }) }, key));
19
+ }, ["block", "list"], 10);
@@ -53,7 +53,8 @@ export const TABLE_RULE = createMarkupRule(createBlockRegExp(`(?<table>${_ROW}\\
53
53
  }
54
54
  body.push(_jsx(Section, { children: rows }, s));
55
55
  }
56
- return _jsx("table", { children: body }, key);
56
+ // Scrollable region pattern: focusable, labelled <figure> wraps the table so keyboard users can arrow-scroll wide columns.
57
+ return (_jsx("figure", { tabIndex: 0, role: "region", "aria-label": "Scrollable region", children: _jsx("table", { children: body }) }, key));
57
58
  }, ["block"]);
58
59
  /** Split a table row into trimmed cell strings, honouring `\|` escaped pipes. */
59
60
  function _splitRow(row) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "shelving",
3
- "version": "1.225.0",
3
+ "version": "1.227.0",
4
4
  "author": "Dave Houlbrooke <dave@shax.com>",
5
5
  "repository": {
6
6
  "type": "git",
@@ -56,6 +56,10 @@
56
56
  --space-xlarge: calc(var(--space) * 2); /* 32px */
57
57
  --space-xxlarge: calc(var(--space) * 3); /* 48px */
58
58
 
59
+ /* Semantic spacings */
60
+ /* Gap between sibling sections/blocks and the top margin of titles/headings, so the rhythm stays consistent whether a heading is wrapped in a Section or rendered bare. */
61
+ --spacing-section: 2rem;
62
+
59
63
  /* Radii */
60
64
  --radius: 1rem;
61
65
  --radius-xxsmall: calc(var(--radius) * 0.25); /* 4px */
@@ -1,8 +1,31 @@
1
1
  import type { ReactElement } from "react";
2
2
  import type { OptionalChildProps } from "../util/props.js";
3
3
  export declare const BLOCK_CLASS: string | undefined;
4
- /** Variants for elements. */
5
4
  export interface BlockProps extends OptionalChildProps {
5
+ /** Constrain the block to narrow width. */
6
+ narrow?: boolean | undefined;
7
+ /** Constrain the block to wide width. */
8
+ wide?: boolean | undefined;
9
+ /** Use larger block spacing. */
10
+ spacious?: boolean | undefined;
11
+ /** Mark as a keyboard-focusable horizontal scroll region — adds `tabindex="0"`, `role="region"`, an `aria-label`, and `overflow-x: auto`. */
12
+ scrollable?: boolean | undefined;
6
13
  }
7
- /** A single `<div>` element with element spacing. */
8
- export declare function Block({ children }: BlockProps): ReactElement;
14
+ /** Plain `<div>` block with block-level spacing. Base building block; use a semantic variant (`<Section>`, `<Figure>`, etc.) when the element matters. */
15
+ export declare function Block(props: BlockProps): ReactElement;
16
+ /** `<section>` block with block-level spacing. */
17
+ export declare function Section(props: BlockProps): ReactElement;
18
+ /** `<header>` block with block-level spacing. */
19
+ export declare function Header(props: BlockProps): ReactElement;
20
+ /** `<footer>` block with block-level spacing. */
21
+ export declare function Footer(props: BlockProps): ReactElement;
22
+ /** `<nav>` block with block-level spacing. */
23
+ export declare function Nav(props: BlockProps): ReactElement;
24
+ /** `<aside>` block with block-level spacing. */
25
+ export declare function Aside(props: BlockProps): ReactElement;
26
+ /** `<figure>` block with block-level spacing. Pair with `<Caption>` for `<figcaption>` content. */
27
+ export declare function Figure(props: BlockProps): ReactElement;
28
+ export interface CaptionProps extends OptionalChildProps {
29
+ }
30
+ /** `<figcaption>` block — caption text for a `<Figure>`. */
31
+ export declare function Caption({ children }: CaptionProps): ReactElement;
package/ui/block/Block.js CHANGED
@@ -1,8 +1,40 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { getModuleClass } from "../util/css.js";
3
- import BLOCK_CSS from "./Block.module.css";
4
- export const BLOCK_CLASS = getModuleClass(BLOCK_CSS, "block");
5
- /** A single `<div>` element with element spacing. */
6
- export function Block({ children }) {
7
- return _jsx("div", { className: BLOCK_CSS.element, children: children });
3
+ import styles from "./Block.module.css";
4
+ export const BLOCK_CLASS = getModuleClass(styles, "block");
5
+ function renderBlock(Component, { children, ...variants }) {
6
+ const className = getModuleClass(styles, "block", variants);
7
+ return variants.scrollable ? (_jsx(Component, { className: className, tabIndex: 0, role: "region", "aria-label": "Scrollable region", children: children })) : (_jsx(Component, { className: className, children: children }));
8
+ }
9
+ /** Plain `<div>` block with block-level spacing. Base building block; use a semantic variant (`<Section>`, `<Figure>`, etc.) when the element matters. */
10
+ export function Block(props) {
11
+ return renderBlock("div", props);
12
+ }
13
+ /** `<section>` block with block-level spacing. */
14
+ export function Section(props) {
15
+ return renderBlock("section", props);
16
+ }
17
+ /** `<header>` block with block-level spacing. */
18
+ export function Header(props) {
19
+ return renderBlock("header", props);
20
+ }
21
+ /** `<footer>` block with block-level spacing. */
22
+ export function Footer(props) {
23
+ return renderBlock("footer", props);
24
+ }
25
+ /** `<nav>` block with block-level spacing. */
26
+ export function Nav(props) {
27
+ return renderBlock("nav", props);
28
+ }
29
+ /** `<aside>` block with block-level spacing. */
30
+ export function Aside(props) {
31
+ return renderBlock("aside", props);
32
+ }
33
+ /** `<figure>` block with block-level spacing. Pair with `<Caption>` for `<figcaption>` content. */
34
+ export function Figure(props) {
35
+ return renderBlock("figure", props);
36
+ }
37
+ /** `<figcaption>` block — caption text for a `<Figure>`. */
38
+ export function Caption({ children }) {
39
+ return _jsx("figcaption", { className: getModuleClass(styles, "caption"), children: children });
8
40
  }
@@ -1,12 +1,96 @@
1
1
  .block {
2
2
  /* Box */
3
- margin: var(--element-spacing, var(--space-small)) 0;
3
+ position: relative;
4
+ display: block;
5
+ margin-inline: 0;
6
+ margin-block: var(--block-spacing, var(--spacing-section));
4
7
 
5
8
  /* Psuedo-classes */
6
9
  &:first-child {
7
- margin-top: 0;
10
+ margin-block-start: 0;
8
11
  }
9
12
  &:last-child {
10
- margin-bottom: 0;
13
+ margin-block-end: 0;
11
14
  }
15
+
16
+ /* Variants */
17
+ &.narrow {
18
+ margin-inline: auto;
19
+ max-width: var(--block-width-narrow, var(--width-narrow));
20
+ }
21
+ &.wide {
22
+ margin-inline: auto;
23
+ max-width: var(--block-width-wide, var(--width-wide));
24
+ }
25
+ &.spacious {
26
+ margin-block: var(--block-spacious-spacing, var(--space-xxlarge));
27
+ }
28
+ }
29
+
30
+ /* Plain semantic blocks inside prose pick up the same spacing as a <Block>. */
31
+ .prose :where(section, article, aside, nav, header, footer, figure) {
32
+ margin-inline: 0;
33
+ margin-block: var(--block-spacing, var(--spacing-section));
34
+
35
+ &:first-child {
36
+ margin-block-start: 0;
37
+ }
38
+ &:last-child {
39
+ margin-block-end: 0;
40
+ }
41
+ }
42
+
43
+ /* Scrollable region — keyboard-focusable horizontal scroll container. Targets both the .scrollable variant and any prose element marked `role="region" tabindex="0"` (e.g. Markdown fenced/table output). */
44
+ .scrollable,
45
+ .prose [role="region"][tabindex="0"] {
46
+ overflow-x: auto;
47
+ overscroll-behavior-x: contain;
48
+
49
+ &:focus-visible {
50
+ outline: var(--focus-stroke, var(--stroke-thick)) solid var(--focus-color, var(--color-accent));
51
+ outline-offset: var(--focus-offset, var(--space-xxsmall));
52
+ }
53
+
54
+ /* Stretch the inner block to fill the scroll container so short content still spans the width. */
55
+ > :is(table, pre) {
56
+ min-width: 100%;
57
+ }
58
+
59
+ /* Force long lines so they trigger horizontal scroll instead of wrapping; grow the box with the content (`width: max-content`) so the pre's own right-side padding survives at scroll-end. */
60
+ > pre {
61
+ width: max-content;
62
+ white-space: pre;
63
+ overflow-wrap: normal;
64
+ }
65
+
66
+ /* Caption stays pinned to the left during horizontal scroll. Spacing/typography come from the shared figcaption rule below. */
67
+ > :is(.caption, figcaption) {
68
+ position: sticky;
69
+ left: 0;
70
+ }
71
+ }
72
+
73
+ .caption,
74
+ .prose figcaption {
75
+ display: block;
76
+ margin-block: var(--caption-gap, var(--space-xsmall));
77
+ font-size: var(--caption-font-size, var(--size-small));
78
+ color: var(--caption-color, var(--color-quiet));
79
+
80
+ &:first-child {
81
+ margin-block-start: 0;
82
+ }
83
+ &:last-child {
84
+ margin-block-end: 0;
85
+ }
86
+ }
87
+
88
+ /* Captions have tighter margins than most block elements, so zero the abutting margin of any neighbouring sibling — otherwise margin collapse would let the bigger neighbour margin win, drifting the caption away from its content. */
89
+ .caption + *,
90
+ .prose figcaption + * {
91
+ margin-block-start: 0;
92
+ }
93
+ :has(+ .caption),
94
+ .prose :has(+ figcaption) {
95
+ margin-block-end: 0;
12
96
  }
@@ -1,14 +1,72 @@
1
1
  import type { ReactElement } from "react";
2
2
  import { getModuleClass } from "../util/css.js";
3
3
  import type { OptionalChildProps } from "../util/props.js";
4
- import BLOCK_CSS from "./Block.module.css";
4
+ import styles from "./Block.module.css";
5
5
 
6
- export const BLOCK_CLASS = getModuleClass(BLOCK_CSS, "block");
6
+ export const BLOCK_CLASS = getModuleClass(styles, "block");
7
7
 
8
- /** Variants for elements. */
9
- export interface BlockProps extends OptionalChildProps {}
8
+ export interface BlockProps extends OptionalChildProps {
9
+ /** Constrain the block to narrow width. */
10
+ narrow?: boolean | undefined;
11
+ /** Constrain the block to wide width. */
12
+ wide?: boolean | undefined;
13
+ /** Use larger block spacing. */
14
+ spacious?: boolean | undefined;
15
+ /** Mark as a keyboard-focusable horizontal scroll region — adds `tabindex="0"`, `role="region"`, an `aria-label`, and `overflow-x: auto`. */
16
+ scrollable?: boolean | undefined;
17
+ }
18
+
19
+ type BlockElement = "div" | "section" | "header" | "footer" | "nav" | "aside" | "figure";
20
+
21
+ function renderBlock(Component: BlockElement, { children, ...variants }: BlockProps): ReactElement {
22
+ const className = getModuleClass(styles, "block", variants);
23
+ return variants.scrollable ? (
24
+ <Component className={className} tabIndex={0} role="region" aria-label="Scrollable region">
25
+ {children}
26
+ </Component>
27
+ ) : (
28
+ <Component className={className}>{children}</Component>
29
+ );
30
+ }
31
+
32
+ /** Plain `<div>` block with block-level spacing. Base building block; use a semantic variant (`<Section>`, `<Figure>`, etc.) when the element matters. */
33
+ export function Block(props: BlockProps): ReactElement {
34
+ return renderBlock("div", props);
35
+ }
36
+
37
+ /** `<section>` block with block-level spacing. */
38
+ export function Section(props: BlockProps): ReactElement {
39
+ return renderBlock("section", props);
40
+ }
41
+
42
+ /** `<header>` block with block-level spacing. */
43
+ export function Header(props: BlockProps): ReactElement {
44
+ return renderBlock("header", props);
45
+ }
46
+
47
+ /** `<footer>` block with block-level spacing. */
48
+ export function Footer(props: BlockProps): ReactElement {
49
+ return renderBlock("footer", props);
50
+ }
51
+
52
+ /** `<nav>` block with block-level spacing. */
53
+ export function Nav(props: BlockProps): ReactElement {
54
+ return renderBlock("nav", props);
55
+ }
56
+
57
+ /** `<aside>` block with block-level spacing. */
58
+ export function Aside(props: BlockProps): ReactElement {
59
+ return renderBlock("aside", props);
60
+ }
61
+
62
+ /** `<figure>` block with block-level spacing. Pair with `<Caption>` for `<figcaption>` content. */
63
+ export function Figure(props: BlockProps): ReactElement {
64
+ return renderBlock("figure", props);
65
+ }
66
+
67
+ export interface CaptionProps extends OptionalChildProps {}
10
68
 
11
- /** A single `<div>` element with element spacing. */
12
- export function Block({ children }: BlockProps): ReactElement {
13
- return <div className={BLOCK_CSS.element}>{children}</div>;
69
+ /** `<figcaption>` block caption text for a `<Figure>`. */
70
+ export function Caption({ children }: CaptionProps): ReactElement {
71
+ return <figcaption className={getModuleClass(styles, "caption")}>{children}</figcaption>;
14
72
  }
@@ -4,8 +4,7 @@
4
4
  display: block;
5
5
  inline-size: 100%;
6
6
  margin-inline: 0;
7
- /* `em` top margin scales with font-size, so larger headings get proportionally more space above them. */
8
- margin-block-start: var(--heading-spacing-before, 1.5em);
7
+ margin-block-start: var(--heading-spacing-before, var(--spacing-section));
9
8
  margin-block-end: var(--heading-spacing, var(--space-normal));
10
9
 
11
10
  /* Style */
@@ -7,6 +7,6 @@ export interface PreformattedProps extends OptionalChildProps {
7
7
  /**
8
8
  * Preformatted block of text — rendered as `<pre>`.
9
9
  * - Defaults to wrapping long lines (`white-space: pre-wrap`) so code fits the container width while preserving newlines and indentation within wrapped lines.
10
- * - Pass `nowrap` to restore strict `<pre>` behaviour when exact whitespace matters (ASCII art, fixed-column tables).
10
+ * - Pass `nowrap` to restore strict `<pre>` behaviour when exact whitespace matters (ASCII art, fixed-column tables). Wrap in a `<Figure scrollable>` (or any `scrollable` block) to add horizontal scrolling.
11
11
  */
12
12
  export declare function Preformatted({ children, ...variants }: PreformattedProps): ReactElement;
@@ -4,7 +4,7 @@ import styles from "./Preformatted.module.css";
4
4
  /**
5
5
  * Preformatted block of text — rendered as `<pre>`.
6
6
  * - Defaults to wrapping long lines (`white-space: pre-wrap`) so code fits the container width while preserving newlines and indentation within wrapped lines.
7
- * - Pass `nowrap` to restore strict `<pre>` behaviour when exact whitespace matters (ASCII art, fixed-column tables).
7
+ * - Pass `nowrap` to restore strict `<pre>` behaviour when exact whitespace matters (ASCII art, fixed-column tables). Wrap in a `<Figure scrollable>` (or any `scrollable` block) to add horizontal scrolling.
8
8
  */
9
9
  export function Preformatted({ children, ...variants }) {
10
10
  return _jsx("pre", { className: getModuleClass(styles, "preformatted", variants), children: children });
@@ -30,6 +30,5 @@
30
30
  &.nowrap {
31
31
  white-space: pre;
32
32
  overflow-wrap: normal;
33
- overflow-x: auto;
34
33
  }
35
34
  }
@@ -11,7 +11,7 @@ export interface PreformattedProps extends OptionalChildProps {
11
11
  /**
12
12
  * Preformatted block of text — rendered as `<pre>`.
13
13
  * - Defaults to wrapping long lines (`white-space: pre-wrap`) so code fits the container width while preserving newlines and indentation within wrapped lines.
14
- * - Pass `nowrap` to restore strict `<pre>` behaviour when exact whitespace matters (ASCII art, fixed-column tables).
14
+ * - Pass `nowrap` to restore strict `<pre>` behaviour when exact whitespace matters (ASCII art, fixed-column tables). Wrap in a `<Figure scrollable>` (or any `scrollable` block) to add horizontal scrolling.
15
15
  */
16
16
  export function Preformatted({ children, ...variants }: PreformattedProps): ReactElement {
17
17
  return <pre className={getModuleClass(styles, "preformatted", variants)}>{children}</pre>;
package/ui/block/Prose.js CHANGED
@@ -11,10 +11,10 @@ import subscriptStyles from "../inline/Subscript.module.css";
11
11
  import superscriptStyles from "../inline/Superscript.module.css";
12
12
  import { getClass } from "../util/css.js";
13
13
  import addressStyles from "./Address.module.css";
14
+ import blockStyles from "./Block.module.css";
14
15
  import blockquoteStyles from "./Blockquote.module.css";
15
16
  import definitionsStyles from "./Definitions.module.css";
16
17
  import dividerStyles from "./Divider.module.css";
17
- import figureStyles from "./Figure.module.css";
18
18
  import headingStyles from "./Heading.module.css";
19
19
  import imageStyles from "./Image.module.css";
20
20
  import listStyles from "./List.module.css";
@@ -22,7 +22,7 @@ import paragraphStyles from "./Paragraph.module.css";
22
22
  import preformattedStyles from "./Preformatted.module.css";
23
23
  import subheadingStyles from "./Subheading.module.css";
24
24
  import tableStyles from "./Table.module.css";
25
- const PROSE_STYLES = getClass(paragraphStyles.prose, headingStyles.prose, subheadingStyles.prose, addressStyles.prose, blockquoteStyles.prose, codeStyles.prose, definitionsStyles.prose, deletedStyles.prose, emphasisStyles.prose, figureStyles.prose, imageStyles.prose, insertedStyles.prose, listStyles.prose, linkStyles.prose, markStyles.prose, preformattedStyles.prose, smallStyles.prose, strongStyles.prose, subscriptStyles.prose, superscriptStyles.prose, tableStyles.prose, dividerStyles.prose);
25
+ const PROSE_STYLES = getClass(paragraphStyles.prose, headingStyles.prose, subheadingStyles.prose, addressStyles.prose, blockStyles.prose, blockquoteStyles.prose, codeStyles.prose, definitionsStyles.prose, deletedStyles.prose, emphasisStyles.prose, imageStyles.prose, insertedStyles.prose, listStyles.prose, linkStyles.prose, markStyles.prose, preformattedStyles.prose, smallStyles.prose, strongStyles.prose, subscriptStyles.prose, superscriptStyles.prose, tableStyles.prose, dividerStyles.prose);
26
26
  /** A section of longform text containing lots of `<p>` or `<ul>` style elements. */
27
27
  export function Prose({ children }) {
28
28
  return _jsx("div", { className: PROSE_STYLES, children: children });
@@ -12,10 +12,10 @@ import superscriptStyles from "../inline/Superscript.module.css";
12
12
  import { getClass } from "../util/css.js";
13
13
  import type { OptionalChildProps } from "../util/props.js";
14
14
  import addressStyles from "./Address.module.css";
15
+ import blockStyles from "./Block.module.css";
15
16
  import blockquoteStyles from "./Blockquote.module.css";
16
17
  import definitionsStyles from "./Definitions.module.css";
17
18
  import dividerStyles from "./Divider.module.css";
18
- import figureStyles from "./Figure.module.css";
19
19
  import headingStyles from "./Heading.module.css";
20
20
  import imageStyles from "./Image.module.css";
21
21
  import listStyles from "./List.module.css";
@@ -29,12 +29,12 @@ const PROSE_STYLES = getClass(
29
29
  headingStyles.prose,
30
30
  subheadingStyles.prose,
31
31
  addressStyles.prose,
32
+ blockStyles.prose,
32
33
  blockquoteStyles.prose,
33
34
  codeStyles.prose,
34
35
  definitionsStyles.prose,
35
36
  deletedStyles.prose,
36
37
  emphasisStyles.prose,
37
- figureStyles.prose,
38
38
  imageStyles.prose,
39
39
  insertedStyles.prose,
40
40
  listStyles.prose,
@@ -4,8 +4,7 @@
4
4
  display: block;
5
5
  inline-size: 100%;
6
6
  margin-inline: 0;
7
- /* `em` top margin scales with font-size, so larger headings get proportionally more space above them. */
8
- margin-block-start: var(--subheading-spacing-before, 1.5em);
7
+ margin-block-start: var(--subheading-spacing-before, var(--spacing-section));
9
8
  margin-block-end: var(--subheading-spacing, var(--space-normal));
10
9
 
11
10
  /* Style */
@@ -1,4 +1,9 @@
1
+ import type { ReactElement } from "react";
1
2
  import type { ChildProps } from "../util/props.js";
2
3
  export interface TableProps extends ChildProps {
3
4
  }
4
- export declare function Table({ children }: TableProps): import("react/jsx-runtime").JSX.Element;
5
+ /**
6
+ * Table block — rendered as `<table>`.
7
+ * - Wrap in a `<Figure scrollable>` (or any `scrollable` block) if the table may exceed the container width on small screens.
8
+ */
9
+ export declare function Table({ children }: TableProps): ReactElement;
package/ui/block/Table.js CHANGED
@@ -1,5 +1,9 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import styles from "./Table.module.css";
3
+ /**
4
+ * Table block — rendered as `<table>`.
5
+ * - Wrap in a `<Figure scrollable>` (or any `scrollable` block) if the table may exceed the container width on small screens.
6
+ */
3
7
  export function Table({ children }) {
4
- return (_jsx("div", { className: styles.wrap, children: _jsx("table", { className: styles.table, children: children }) }));
8
+ return _jsx("table", { className: styles.table, children: children });
5
9
  }
@@ -58,28 +58,3 @@
58
58
  font-size: var(--table-label-size, var(--size-small));
59
59
  }
60
60
  }
61
-
62
- .wrap {
63
- /* Box */
64
- display: block;
65
- margin-block: var(--table-spacing, var(--space-normal));
66
- margin-inline: calc(var(--space-normal) * -1);
67
- padding-inline: var(--space-normal);
68
-
69
- /* Contents */
70
- overflow-x: auto;
71
- white-space: nowrap;
72
-
73
- /* Psuedo-classes */
74
- &:first-child {
75
- margin-block-start: 0;
76
- }
77
- &:last-child {
78
- margin-block-end: 0;
79
- }
80
-
81
- /* Children */
82
- .table {
83
- min-width: 100%;
84
- }
85
- }
@@ -1,12 +1,13 @@
1
+ import type { ReactElement } from "react";
1
2
  import type { ChildProps } from "../util/props.js";
2
3
  import styles from "./Table.module.css";
3
4
 
4
5
  export interface TableProps extends ChildProps {}
5
6
 
6
- export function Table({ children }: TableProps) {
7
- return (
8
- <div className={styles.wrap}>
9
- <table className={styles.table}>{children}</table>
10
- </div>
11
- );
7
+ /**
8
+ * Table block — rendered as `<table>`.
9
+ * - Wrap in a `<Figure scrollable>` (or any `scrollable` block) if the table may exceed the container width on small screens.
10
+ */
11
+ export function Table({ children }: TableProps): ReactElement {
12
+ return <table className={styles.table}>{children}</table>;
12
13
  }
@@ -4,8 +4,7 @@
4
4
  display: block;
5
5
  inline-size: 100%;
6
6
  margin-inline: 0;
7
- /* `em` top margin scales with font-size, so larger headings get proportionally more space above them. */
8
- margin-block-start: var(--title-spacing-before, 1.5em);
7
+ margin-block-start: var(--title-spacing-before, var(--spacing-section));
9
8
  margin-block-end: var(--title-spacing, var(--space-normal));
10
9
 
11
10
  /* Style */
@@ -4,7 +4,6 @@ export * from "./Blockquote.js";
4
4
  export * from "./Card.js";
5
5
  export * from "./Definitions.js";
6
6
  export * from "./Divider.js";
7
- export * from "./Figure.js";
8
7
  export * from "./Flex.js";
9
8
  export * from "./Heading.js";
10
9
  export * from "./Image.js";
@@ -12,7 +11,6 @@ export * from "./List.js";
12
11
  export * from "./Paragraph.js";
13
12
  export * from "./Preformatted.js";
14
13
  export * from "./Prose.js";
15
- export * from "./Section.js";
16
14
  export * from "./Subheading.js";
17
15
  export * from "./Table.js";
18
16
  export * from "./Title.js";
package/ui/block/index.js CHANGED
@@ -4,7 +4,6 @@ export * from "./Blockquote.js";
4
4
  export * from "./Card.js";
5
5
  export * from "./Definitions.js";
6
6
  export * from "./Divider.js";
7
- export * from "./Figure.js";
8
7
  export * from "./Flex.js";
9
8
  export * from "./Heading.js";
10
9
  export * from "./Image.js";
@@ -12,7 +11,6 @@ export * from "./List.js";
12
11
  export * from "./Paragraph.js";
13
12
  export * from "./Preformatted.js";
14
13
  export * from "./Prose.js";
15
- export * from "./Section.js";
16
14
  export * from "./Subheading.js";
17
15
  export * from "./Table.js";
18
16
  export * from "./Title.js";
package/ui/block/index.ts CHANGED
@@ -4,7 +4,6 @@ export * from "./Blockquote.js";
4
4
  export * from "./Card.js";
5
5
  export * from "./Definitions.js";
6
6
  export * from "./Divider.js";
7
- export * from "./Figure.js";
8
7
  export * from "./Flex.js";
9
8
  export * from "./Heading.js";
10
9
  export * from "./Image.js";
@@ -12,7 +11,6 @@ export * from "./List.js";
12
11
  export * from "./Paragraph.js";
13
12
  export * from "./Preformatted.js";
14
13
  export * from "./Prose.js";
15
- export * from "./Section.js";
16
14
  export * from "./Subheading.js";
17
15
  export * from "./Table.js";
18
16
  export * from "./Title.js";
@@ -1,5 +1,5 @@
1
1
  import type { ReactNode } from "react";
2
- import type { DirectoryElementProps } from "../../util/element.js";
2
+ import { type DirectoryElementProps } from "../../util/element.js";
3
3
  import type { AbsolutePath } from "../../util/path.js";
4
4
  interface DirectoryPageProps extends DirectoryElementProps {
5
5
  /** Site-root-relative path of this page — threaded down so child cards build correct hrefs. */
@@ -1,4 +1,6 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
1
+ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { walkElements } from "../../util/element.js";
3
+ import { Heading } from "../block/Heading.js";
2
4
  import { Prose } from "../block/Prose.js";
3
5
  import { Title } from "../block/Title.js";
4
6
  import { Markup } from "../misc/Markup.js";
@@ -6,5 +8,6 @@ import { Page } from "../page/Page.js";
6
8
  import { TreeCards } from "../tree/TreeCards.js";
7
9
  /** Page renderer for a `tree-directory` element — shows title, content, and child cards. */
8
10
  export function DirectoryPage({ path, title, name, description, content, children }) {
9
- return (_jsxs(Page, { title: title ?? name, description: description, children: [_jsx(Title, { children: title ?? name }), content && (_jsx(Prose, { children: _jsx(Markup, { children: content }) })), _jsx(TreeCards, { path: path, children: children })] }));
11
+ const cards = Array.from(walkElements(children));
12
+ return (_jsxs(Page, { title: title ?? name, description: description, children: [_jsx(Title, { children: title ?? name }), content && (_jsx(Prose, { children: _jsx(Markup, { children: content }) })), cards.length > 0 && (_jsxs(_Fragment, { children: [_jsx(Heading, { children: "Modules" }), _jsx(TreeCards, { path: path, children: cards })] }))] }));
10
13
  }
@@ -1,6 +1,7 @@
1
1
  import type { ReactNode } from "react";
2
- import type { DirectoryElementProps } from "../../util/element.js";
2
+ import { type DirectoryElementProps, walkElements } from "../../util/element.js";
3
3
  import type { AbsolutePath } from "../../util/path.js";
4
+ import { Heading } from "../block/Heading.js";
4
5
  import { Prose } from "../block/Prose.js";
5
6
  import { Title } from "../block/Title.js";
6
7
  import { Markup } from "../misc/Markup.js";
@@ -14,6 +15,7 @@ interface DirectoryPageProps extends DirectoryElementProps {
14
15
 
15
16
  /** Page renderer for a `tree-directory` element — shows title, content, and child cards. */
16
17
  export function DirectoryPage({ path, title, name, description, content, children }: DirectoryPageProps): ReactNode {
18
+ const cards = Array.from(walkElements(children));
17
19
  return (
18
20
  <Page title={title ?? name} description={description}>
19
21
  <Title>{title ?? name}</Title>
@@ -22,7 +24,12 @@ export function DirectoryPage({ path, title, name, description, content, childre
22
24
  <Markup>{content}</Markup>
23
25
  </Prose>
24
26
  )}
25
- <TreeCards path={path}>{children}</TreeCards>
27
+ {cards.length > 0 && (
28
+ <>
29
+ <Heading>Modules</Heading>
30
+ <TreeCards path={path}>{cards}</TreeCards>
31
+ </>
32
+ )}
26
33
  </Page>
27
34
  );
28
35
  }
@@ -1,11 +1,11 @@
1
1
  import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { queryElements } from "../../util/element.js";
3
+ import { Section } from "../block/Block.js";
3
4
  import { Definition, Definitions } from "../block/Definitions.js";
4
5
  import { Flex } from "../block/Flex.js";
5
6
  import { Heading } from "../block/Heading.js";
6
7
  import { Preformatted } from "../block/Preformatted.js";
7
8
  import { Prose } from "../block/Prose.js";
8
- import { Section } from "../block/Section.js";
9
9
  import { Title } from "../block/Title.js";
10
10
  import { Code } from "../inline/Code.js";
11
11
  import { Markup } from "../misc/Markup.js";
@@ -2,12 +2,12 @@ import type { ReactNode } from "react";
2
2
  import { type DocumentationElementProps, type Element, queryElements, type TreeElement } from "../../util/element.js";
3
3
  import type { AbsolutePath } from "../../util/path.js";
4
4
  import type { Query } from "../../util/query.js";
5
+ import { Section } from "../block/Block.js";
5
6
  import { Definition, Definitions } from "../block/Definitions.js";
6
7
  import { Flex } from "../block/Flex.js";
7
8
  import { Heading } from "../block/Heading.js";
8
9
  import { Preformatted } from "../block/Preformatted.js";
9
10
  import { Prose } from "../block/Prose.js";
10
- import { Section } from "../block/Section.js";
11
11
  import { Title } from "../block/Title.js";
12
12
  import { Code } from "../inline/Code.js";
13
13
  import { Markup } from "../misc/Markup.js";
@@ -1,6 +1,6 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Footer } from "../block/Block.js";
2
3
  import { Flex } from "../block/Flex.js";
3
- import { Footer } from "../block/Section.js";
4
4
  import { FormMessage } from "./FormMessage.js";
5
5
  import { SubmitButton } from "./SubmitButton.js";
6
6
  /**
@@ -1,6 +1,6 @@
1
1
  import type { ReactElement, ReactNode } from "react";
2
+ import { Footer } from "../block/Block.js";
2
3
  import { Flex } from "../block/Flex.js";
3
- import { Footer } from "../block/Section.js";
4
4
  import type { OptionalChildProps } from "../util/props.js";
5
5
  import { FormMessage } from "./FormMessage.js";
6
6
  import { SubmitButton } from "./SubmitButton.js";
@@ -1,5 +1,5 @@
1
1
  .code,
2
- .prose :is(code, kbd, samp, var) {
2
+ .prose :is(code, kbd, samp, var):not(pre *) {
3
3
  /* Box */
4
4
  display: inline-block;
5
5
  padding-inline: 0.375rem;
@@ -1,6 +0,0 @@
1
- import type { ReactElement, ReactNode } from "react";
2
- import type { OptionalChildProps } from "../util/props.js";
3
- export interface FigureProps extends OptionalChildProps {
4
- caption?: ReactNode;
5
- }
6
- export declare function Figure({ children, caption }: FigureProps): ReactElement;
@@ -1,5 +0,0 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import styles from "./Figure.module.css";
3
- export function Figure({ children, caption }) {
4
- return (_jsxs("figure", { className: styles.figure, children: [children, caption && _jsx("figcaption", { className: styles.caption, children: caption })] }));
5
- }
@@ -1,27 +0,0 @@
1
- .figure,
2
- .prose figure {
3
- /* Box */
4
- position: relative;
5
- display: block;
6
- margin-inline: 0;
7
- margin-block: var(--figure-spacing, var(--space-normal));
8
-
9
- /* Psuedo-classes */
10
- &:first-child {
11
- margin-block-start: 0;
12
- }
13
- &:last-child {
14
- margin-block-end: 0;
15
- }
16
- }
17
-
18
- .caption,
19
- .prose figcaption {
20
- /* Box */
21
- display: block;
22
- margin-block-start: var(--caption-gap, var(--space-normal));
23
-
24
- /* Style */
25
- font-size: var(--caption-font-size, var(--size-small));
26
- color: var(--caption-color, var(--color-quiet));
27
- }
@@ -1,16 +0,0 @@
1
- import type { ReactElement, ReactNode } from "react";
2
- import type { OptionalChildProps } from "../util/props.js";
3
- import styles from "./Figure.module.css";
4
-
5
- export interface FigureProps extends OptionalChildProps {
6
- caption?: ReactNode;
7
- }
8
-
9
- export function Figure({ children, caption }: FigureProps): ReactElement {
10
- return (
11
- <figure className={styles.figure}>
12
- {children}
13
- {caption && <figcaption className={styles.caption}>{caption}</figcaption>}
14
- </figure>
15
- );
16
- }
@@ -1,28 +0,0 @@
1
- import type { ReactElement } from "react";
2
- import type { OptionalChildProps } from "../util/props.js";
3
- export interface SectionProps extends OptionalChildProps {
4
- /** Constrain the section to narrow width (defaults to full-width). */
5
- narrow?: boolean;
6
- /** Constrain the section to wide width (defaults to full-width). */
7
- wide?: boolean;
8
- /** Constrain the section to spacious spacing (defaults to full-width). */
9
- spacious?: boolean;
10
- }
11
- export interface HeaderProps extends SectionProps {
12
- }
13
- /** A single HTML `<header>` with correct spacing. */
14
- export declare function Header(props: HeaderProps): ReactElement;
15
- /** A single HTML `<section>` with correct spacing. */
16
- export declare function Section(props: SectionProps): ReactElement;
17
- export interface NavProps extends SectionProps {
18
- }
19
- /** A single HTML `<nav>` with correct spacing. */
20
- export declare function Nav(props: NavProps): ReactElement;
21
- export interface AsideProps extends SectionProps {
22
- }
23
- /** A single HTML `<aside>` with correct spacing. */
24
- export declare function Aside(props: AsideProps): ReactElement;
25
- export interface FooterProps extends SectionProps {
26
- }
27
- /** A single HTML `<footer>` with correct spacing. */
28
- export declare function Footer(props: FooterProps): ReactElement;
@@ -1,26 +0,0 @@
1
- import { jsx as _jsx } from "react/jsx-runtime";
2
- import { getModuleClass } from "../util/css.js";
3
- import styles from "./Section.module.css";
4
- function renderSection(Component, { children, ...variants }) {
5
- return _jsx(Component, { className: getModuleClass(styles, "section", variants), children: children });
6
- }
7
- /** A single HTML `<header>` with correct spacing. */
8
- export function Header(props) {
9
- return renderSection("header", props);
10
- }
11
- /** A single HTML `<section>` with correct spacing. */
12
- export function Section(props) {
13
- return renderSection("section", props);
14
- }
15
- /** A single HTML `<nav>` with correct spacing. */
16
- export function Nav(props) {
17
- return renderSection("nav", props);
18
- }
19
- /** A single HTML `<aside>` with correct spacing. */
20
- export function Aside(props) {
21
- return renderSection("aside", props);
22
- }
23
- /** A single HTML `<footer>` with correct spacing. */
24
- export function Footer(props) {
25
- return renderSection("footer", props);
26
- }
@@ -1,28 +0,0 @@
1
- .section {
2
- /* Box */
3
- position: relative;
4
- display: block;
5
- margin-inline: 0;
6
- margin-block: var(--section-spacing, var(--space-normal));
7
-
8
- /* Psuedo-classes */
9
- &:first-child {
10
- margin-block-start: 0;
11
- }
12
- &:last-child {
13
- margin-block-end: 0;
14
- }
15
-
16
- /* Variants */
17
- &.narrow {
18
- margin-inline: auto;
19
- max-width: var(--section-width-narrow, var(--width-narrow));
20
- }
21
- &.wide {
22
- margin-inline: auto;
23
- max-width: var(--section-width-wide, var(--width-wide));
24
- }
25
- &.spacious {
26
- margin-block: var(--paragraph-spacious-spacing, var(--space-xxlarge));
27
- }
28
- }
@@ -1,53 +0,0 @@
1
- import type { ReactElement } from "react";
2
- import { getModuleClass } from "../util/css.js";
3
- import type { OptionalChildProps } from "../util/props.js";
4
- import styles from "./Section.module.css";
5
-
6
- export interface SectionProps extends OptionalChildProps {
7
- /** Constrain the section to narrow width (defaults to full-width). */
8
- narrow?: boolean;
9
- /** Constrain the section to wide width (defaults to full-width). */
10
- wide?: boolean;
11
- /** Constrain the section to spacious spacing (defaults to full-width). */
12
- spacious?: boolean;
13
- }
14
-
15
- function renderSection(
16
- Component: "header" | "footer" | "nav" | "section" | "aside",
17
- { children, ...variants }: SectionProps,
18
- ): ReactElement {
19
- return <Component className={getModuleClass(styles, "section", variants)}>{children}</Component>;
20
- }
21
-
22
- export interface HeaderProps extends SectionProps {}
23
-
24
- /** A single HTML `<header>` with correct spacing. */
25
- export function Header(props: HeaderProps): ReactElement {
26
- return renderSection("header", props);
27
- }
28
-
29
- /** A single HTML `<section>` with correct spacing. */
30
- export function Section(props: SectionProps): ReactElement {
31
- return renderSection("section", props);
32
- }
33
-
34
- export interface NavProps extends SectionProps {}
35
-
36
- /** A single HTML `<nav>` with correct spacing. */
37
- export function Nav(props: NavProps): ReactElement {
38
- return renderSection("nav", props);
39
- }
40
-
41
- export interface AsideProps extends SectionProps {}
42
-
43
- /** A single HTML `<aside>` with correct spacing. */
44
- export function Aside(props: AsideProps): ReactElement {
45
- return renderSection("aside", props);
46
- }
47
-
48
- export interface FooterProps extends SectionProps {}
49
-
50
- /** A single HTML `<footer>` with correct spacing. */
51
- export function Footer(props: FooterProps): ReactElement {
52
- return renderSection("footer", props);
53
- }