shelving 1.254.1 → 1.255.1

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "shelving",
3
- "version": "1.254.1",
3
+ "version": "1.255.1",
4
4
  "author": "Dave Houlbrooke <dave@shax.com>",
5
5
  "repository": {
6
6
  "type": "git",
package/ui/README.md CHANGED
@@ -25,7 +25,7 @@ The styling system lives in `style/` and has four moving parts: design tokens, t
25
25
 
26
26
  **Cascade layers.** Styles are ordered by `@layer`, lowest to highest priority: `defaults` (`:root` tokens, the tint ladder, body baseline) → `components` (the bulk of the CSS: `.card`, `.button`, …) → `variants` (cross-cutting opt-in modifiers, which always beat components) → `overrides` (top-priority structural fixes like `:first-child` / `:last-child` margin collapses). Unlayered rules beat all layered rules, so a theme should set tokens at `:root` or wrap its rules in `@layer`.
27
27
 
28
- **Styling props.** The cross-cutting visual options are props, each backed by a helper in `style/` that maps the prop to a class. Colour and status move the tint anchor — `getColorClass()` and `getStatusClass()`; font size, weight, family, and case, plus text alignment, tint, and wrapping, all come from `getTypographyClass()`; spacing, padding, and gap from `getSpaceClass()`, `getPaddingClass()`, and `getGapClass()`; width constraints from `getWidthClass()`; flex layout from `getFlexClass()`; and opt-in scrolling from `getScrollClass()`. Each helper's page lists its exact prop values and what they set. A component opts into the props it wants by extending the matching `*Props` interfaces and composing the `getXxxClass(props)` calls.
28
+ **Styling props.** The cross-cutting visual options are props, each backed by a helper in `style/` that maps the prop to a class. Colour and status move the tint anchor — `getColorClass()` and `getStatusClass()`; font size, weight, family, and case, plus text alignment, tint, and wrapping, all come from `getTypographyClass()`; spacing, block-padding, inline-padding ("indent"), and gap from `getSpaceClass()`, `getPaddingClass()`, `getIndentClass()`, and `getGapClass()`; width constraints from `getWidthClass()`; flex layout from `getFlexClass()`; and opt-in scrolling from `getScrollClass()`. Each helper's page lists its exact prop values and what they set. A component opts into the props it wants by extending the matching `*Props` interfaces and composing the `getXxxClass(props)` calls.
29
29
 
30
30
  Each painting component also exposes its own theme hooks — a single tint hook (`--card-tint`) to recolour the whole component, plus per-property hooks (`--card-background`, `--card-radius`, …) for surgical overrides. Those are documented in each component's own **Styling** section (see `<Card>` for the precedent).
31
31
 
@@ -1,5 +1,6 @@
1
1
  import type { ReactElement } from "react";
2
2
  import { type ColorVariants } from "../style/Color.js";
3
+ import { type IndentVariants } from "../style/Indent.js";
3
4
  import { type SpaceVariants } from "../style/Space.js";
4
5
  import { type TypographyVariants } from "../style/Typography.js";
5
6
  import { type WidthVariants } from "../style/Width.js";
@@ -15,7 +16,7 @@ export type BlockElement = "div" | "section" | "header" | "footer" | "article" |
15
16
  *
16
17
  * @see https://shelving.cc/ui/BlockProps
17
18
  */
18
- export interface BlockProps extends ColorVariants, SpaceVariants, TypographyVariants, WidthVariants, OptionalChildProps {
19
+ export interface BlockProps extends ColorVariants, IndentVariants, SpaceVariants, TypographyVariants, WidthVariants, OptionalChildProps {
19
20
  /**
20
21
  * Element this `<Block>` renders as, e.g. "header" to output a "<header>"
21
22
  * @default "div"
package/ui/block/Block.js CHANGED
@@ -1,5 +1,6 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { getColorClass } from "../style/Color.js";
3
+ import { getIndentClass } from "../style/Indent.js";
3
4
  import { getSpaceClass } from "../style/Space.js";
4
5
  import { getTypographyClass } from "../style/Typography.js";
5
6
  import { getWidthClass } from "../style/Width.js";
@@ -18,7 +19,7 @@ const BLOCK_CLASS = getModuleClass(BLOCK_CSS, "block");
18
19
  */
19
20
  export function getBlockClass(variants) {
20
21
  return getClass(BLOCK_CLASS, //
21
- getColorClass(variants), getSpaceClass(variants), getTypographyClass(variants), getWidthClass(variants));
22
+ getColorClass(variants), getIndentClass(variants), getSpaceClass(variants), getTypographyClass(variants), getWidthClass(variants));
22
23
  }
23
24
  /**
24
25
  * Plain `<div>` block with block-level spacing.
@@ -1,5 +1,6 @@
1
1
  @import "../style/layers.css";
2
2
  @import "../style/Color.module.css";
3
+ @import "../style/Indent.module.css";
3
4
  @import "../style/Space.module.css";
4
5
  @import "../style/Typography.module.css";
5
6
  @import "../style/Width.module.css";
@@ -1,5 +1,6 @@
1
1
  import type { ReactElement } from "react";
2
2
  import { type ColorVariants, getColorClass } from "../style/Color.js";
3
+ import { getIndentClass, type IndentVariants } from "../style/Indent.js";
3
4
  import { getSpaceClass, type SpaceVariants } from "../style/Space.js";
4
5
  import { getTypographyClass, type TypographyVariants } from "../style/Typography.js";
5
6
  import { getWidthClass, type WidthVariants } from "../style/Width.js";
@@ -21,7 +22,7 @@ export type BlockElement = "div" | "section" | "header" | "footer" | "article" |
21
22
  *
22
23
  * @see https://shelving.cc/ui/BlockProps
23
24
  */
24
- export interface BlockProps extends ColorVariants, SpaceVariants, TypographyVariants, WidthVariants, OptionalChildProps {
25
+ export interface BlockProps extends ColorVariants, IndentVariants, SpaceVariants, TypographyVariants, WidthVariants, OptionalChildProps {
25
26
  /**
26
27
  * Element this `<Block>` renders as, e.g. "header" to output a "<header>"
27
28
  * @default "div"
@@ -43,6 +44,7 @@ export function getBlockClass(variants: BlockProps): string {
43
44
  return getClass(
44
45
  BLOCK_CLASS, //
45
46
  getColorClass(variants),
47
+ getIndentClass(variants),
46
48
  getSpaceClass(variants),
47
49
  getTypographyClass(variants),
48
50
  getWidthClass(variants),
@@ -1,5 +1,6 @@
1
1
  import type { ReactElement } from "react";
2
2
  import { type ColorVariants } from "../style/Color.js";
3
+ import { type IndentVariants } from "../style/Indent.js";
3
4
  import { type PaddingVariants } from "../style/Padding.js";
4
5
  import { type StatusVariants } from "../style/Status.js";
5
6
  import { type TypographyVariants } from "../style/Typography.js";
@@ -10,7 +11,7 @@ import type { BlockElement } from "./Block.js";
10
11
  *
11
12
  * @see https://shelving.cc/ui/PanelProps
12
13
  */
13
- export interface PanelProps extends ColorVariants, PaddingVariants, StatusVariants, TypographyVariants, OptionalChildProps {
14
+ export interface PanelProps extends ColorVariants, IndentVariants, PaddingVariants, StatusVariants, TypographyVariants, OptionalChildProps {
14
15
  /**
15
16
  * Element this `<Panel>` renders as, e.g. "header" to output a "<header>"
16
17
  * @default "section"
package/ui/block/Panel.js CHANGED
@@ -1,5 +1,6 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { getColorClass } from "../style/Color.js";
3
+ import { getIndentClass } from "../style/Indent.js";
3
4
  import { getPaddingClass } from "../style/Padding.js";
4
5
  import { getStatusClass } from "../style/Status.js";
5
6
  import { getTypographyClass } from "../style/Typography.js";
@@ -21,5 +22,5 @@ const PANEL_CLASS = getModuleClass(PANEL_CSS, "panel");
21
22
  */
22
23
  export function Panel({ as: Element = "section", children, ...props }) {
23
24
  return (_jsx(Element, { className: getClass(PANEL_CLASS, //
24
- getStatusClass(props), getColorClass(props), getPaddingClass(props), getTypographyClass(props)), children: children }));
25
+ getStatusClass(props), getColorClass(props), getIndentClass(props), getPaddingClass(props), getTypographyClass(props)), children: children }));
25
26
  }
package/ui/block/Panel.md CHANGED
@@ -5,7 +5,8 @@ A full-width vertical region that paints the current surface colour. Use panels
5
5
  **Things to know:**
6
6
 
7
7
  - A panel always spans the full width of its container. To constrain the content inside, compose a `<Block>` `width="narrow"` (or `width="wide"`) within it.
8
- - Block margin is always zero so panels stack flush; control the vertical breathing room with the `padding` variant (`<Panel padding="large">`, `<Panel padding="none">`). Inline padding is fixed.
8
+ - Block margin is always zero so panels stack flush; control the vertical breathing room with the `padding` variant (`<Panel padding="large">`, `<Panel padding="none">`).
9
+ - Inline padding ("indent") keeps content off the edges by default. Override it per-property with `--panel-indent`, or change it with the shared `indent` variant (`<Panel indent="large">`, `<Panel indent="none">`).
9
10
  - `color=` / `status=` move the tint anchor for the whole panel scope, so the surface, border, and text re-derive together and cascade into nested content.
10
11
  - The top and bottom borders are dropped on the first and last panel so the page doesn't gain stray edge lines.
11
12
 
@@ -39,5 +40,6 @@ import { Panel, Block, Title, Paragraph } from "shelving/ui";
39
40
  | `--panel-color` | Text colour | `var(--tint-00)` |
40
41
  | `--panel-border` | Top/bottom border shorthand | `var(--panel-stroke) solid var(--tint-80)` |
41
42
  | `--panel-stroke` | Border thickness | `var(--stroke-normal)` (2px) |
43
+ | `--panel-indent` | Inline padding (left + right) keeping content off the edges | `var(--space-normal)` (16px) |
42
44
 
43
- **Global tokens it reads:** the tint-ladder steps `--tint-00` / `--tint-80` / `--tint-90`, plus `--stroke-normal`. Vertical padding comes from the shared `padding` variant.
45
+ **Global tokens it reads:** the tint-ladder steps `--tint-00` / `--tint-80` / `--tint-90`, plus `--stroke-normal` and `--space-normal`. Vertical padding comes from the shared `padding` variant; inline padding from the shared `indent` variant.
@@ -3,14 +3,18 @@
3
3
  @import "../style/Typography.module.css";
4
4
  @import "../style/Status.module.css";
5
5
  @import "../style/Color.module.css";
6
+ @import "../style/Indent.module.css";
6
7
  @import "../style/Padding.module.css";
8
+ @import "../style/Space.module.css";
7
9
 
8
10
  /*
9
11
  * Full-width vertical region that paints the current surface. Panels always have zero block-space
10
12
  * (they butt up against each other vertically to create stacked page sections). Vertical breathing
11
13
  * room comes from the shared `padding` variant (`.large`, `.xxlarge`, etc. from `style/Padding`);
12
14
  * with no `padding` the block padding is zero and the inner `<Section>` margins provide the spacing.
13
- * For narrower content inside a wide panel, compose a `<Block narrow>` (or `<Block wide>`) inside it.
15
+ * Inline padding ("indent") keeps content off the edges by default (`--panel-indent`); override it
16
+ * with the shared `indent` variant. For narrower content inside a wide panel, compose a
17
+ * `<Block narrow>` (or `<Block wide>`) inside it.
14
18
  */
15
19
  @layer components {
16
20
  .panel {
@@ -20,9 +24,11 @@
20
24
  /* Box */
21
25
  position: relative;
22
26
  display: flow-root;
27
+ box-sizing: border-box;
23
28
  inline-size: 100%;
24
29
  margin-inline: 0;
25
30
  margin-block: 0;
31
+ padding-inline: var(--panel-indent, var(--space-normal));
26
32
  border-top: var(--panel-border, var(--panel-stroke, var(--stroke-normal)) solid var(--tint-80));
27
33
  border-bottom: var(--panel-border, var(--panel-stroke, var(--stroke-normal)) solid var(--tint-80));
28
34
 
@@ -1,5 +1,6 @@
1
1
  import type { ReactElement } from "react";
2
2
  import { type ColorVariants, getColorClass } from "../style/Color.js";
3
+ import { getIndentClass, type IndentVariants } from "../style/Indent.js";
3
4
  import { getPaddingClass, type PaddingVariants } from "../style/Padding.js";
4
5
  import { getStatusClass, type StatusVariants } from "../style/Status.js";
5
6
  import { getTypographyClass, type TypographyVariants } from "../style/Typography.js";
@@ -15,7 +16,7 @@ const PANEL_CLASS = getModuleClass(PANEL_CSS, "panel");
15
16
  *
16
17
  * @see https://shelving.cc/ui/PanelProps
17
18
  */
18
- export interface PanelProps extends ColorVariants, PaddingVariants, StatusVariants, TypographyVariants, OptionalChildProps {
19
+ export interface PanelProps extends ColorVariants, IndentVariants, PaddingVariants, StatusVariants, TypographyVariants, OptionalChildProps {
19
20
  /**
20
21
  * Element this `<Panel>` renders as, e.g. "header" to output a "<header>"
21
22
  * @default "section"
@@ -43,6 +44,7 @@ export function Panel({ as: Element = "section", children, ...props }: PanelProp
43
44
  PANEL_CLASS, //
44
45
  getStatusClass(props),
45
46
  getColorClass(props),
47
+ getIndentClass(props),
46
48
  getPaddingClass(props),
47
49
  getTypographyClass(props),
48
50
  )}
@@ -1,5 +1,6 @@
1
1
  import type { ReactElement } from "react";
2
2
  import { type ColorVariants } from "../style/Color.js";
3
+ import { type IndentVariants } from "../style/Indent.js";
3
4
  import { type SpaceVariants } from "../style/Space.js";
4
5
  import { type TypographyVariants } from "../style/Typography.js";
5
6
  import { type WidthVariants } from "../style/Width.js";
@@ -10,7 +11,7 @@ import type { BlockElement } from "./Block.js";
10
11
  *
11
12
  * @see https://shelving.cc/ui/SectionProps
12
13
  */
13
- export interface SectionProps extends ColorVariants, SpaceVariants, TypographyVariants, WidthVariants, OptionalChildProps {
14
+ export interface SectionProps extends ColorVariants, IndentVariants, SpaceVariants, TypographyVariants, WidthVariants, OptionalChildProps {
14
15
  /**
15
16
  * Element this `<Section>` renders as, e.g. "header" to output a "<header>"
16
17
  * @default "section"
@@ -1,5 +1,6 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { getColorClass } from "../style/Color.js";
3
+ import { getIndentClass } from "../style/Indent.js";
3
4
  import { getSpaceClass } from "../style/Space.js";
4
5
  import { getTypographyClass } from "../style/Typography.js";
5
6
  import { getWidthClass } from "../style/Width.js";
@@ -18,7 +19,7 @@ const SECTION_CLASS = getModuleClass(SECTION_CSS, "section");
18
19
  */
19
20
  export function getSectionClass(variants) {
20
21
  return getClass(SECTION_CLASS, //
21
- getColorClass(variants), getSpaceClass(variants), getTypographyClass(variants), getWidthClass(variants));
22
+ getColorClass(variants), getIndentClass(variants), getSpaceClass(variants), getTypographyClass(variants), getWidthClass(variants));
22
23
  }
23
24
  /**
24
25
  * `<section>` block with section-level spacing.
@@ -5,8 +5,8 @@ A landmark content region — renders a `<section>` with block-level spacing and
5
5
  **Things to know:**
6
6
 
7
7
  - Pick the component whose HTML element matches the semantic meaning rather than reaching for a generic `<Block>`. `<Section>` is a `<section>`, `<Figure>` a `<figure>`, and so on.
8
- - Every section centres its content and caps the line length so text never touches the viewport edges. Nested sections relax that cap so they can fill their parent.
9
- - Sections default to the `--width-normal` content width, so most of the time you set no width at all. Pass `width="narrow"` / `"wide"` / `"full"` (or `"fit"`) to change that, and the usual `color` / `space` / typography variants to retint and respace.
8
+ - Every section centres its content and caps the line length, but it adds **no inline padding of its own**, so its content can run to the edge of its container. Keep content off the edges by placing the section inside something that already pads a `<Panel>`, a `<Block indent="normal">`, or a layout that pads its scroll area — or by giving the section its own `indent` variant (`<Section indent="normal">`).
9
+ - Sections default to the `--width-normal` content width, so most of the time you set no width at all. Pass `width="narrow"` / `"wide"` / `"full"` (or `"fit"`) to change that, and the usual `color` / `space` / `indent` / typography variants to retint, respace, and inset.
10
10
  - Pair `Figure` with `<Caption>` for a `<figure>` / `<figcaption>` pair.
11
11
 
12
12
  ## Usage
@@ -43,7 +43,6 @@ import { Header, Nav, Footer } from "shelving/ui";
43
43
  | Variable | Styles | Default |
44
44
  |---|---|---|
45
45
  | `--section-width` | Content width | `var(--width-normal)` (55rem) |
46
- | `--section-indent` | Inline gutter kept on each side so text doesn't touch the edges | `var(--space-normal)` (16px) |
47
46
  | `--section-space` | Outer block margin (top + bottom) | `var(--space-section)` (2rem) |
48
47
 
49
- **Global tokens it reads:** `--space-normal`, `--space-section`, and `--width-normal` (the default content width). The `width` variant (`narrow` / `normal` / `wide` / `full` / `fit`) comes from the shared `shelving/ui` styling system.
48
+ **Global tokens it reads:** `--space-section` and `--width-normal` (the default content width). The `width` variant (`narrow` / `normal` / `wide` / `full` / `fit`) and the `indent` variant (inline padding, via `getIndentClass()`) come from the shared `shelving/ui` styling system.
@@ -1,5 +1,6 @@
1
1
  @import "../style/layers.css";
2
2
  @import "../style/Color.module.css";
3
+ @import "../style/Indent.module.css";
3
4
  @import "../style/Typography.module.css";
4
5
  @import "../style/Space.module.css";
5
6
  @import "../style/Width.module.css";
@@ -10,15 +11,10 @@
10
11
  /* Box */
11
12
  position: relative;
12
13
  display: block;
13
- box-sizing: border-box;
14
- max-inline-size: calc(100% - (var(--section-indent, var(--space-normal)) * 2)); /* Stop text touching the sides. */
14
+ box-sizing: border-box; /* Keep the `indent` variant's inline padding inside the width. */
15
+ max-inline-size: 100%;
15
16
  inline-size: var(--section-width, var(--width-normal)); /* Default to the normal content width; override with the `width` variant. */
16
17
  margin-inline: auto;
17
18
  margin-block: var(--section-space, var(--space-section));
18
-
19
- .prose :where(section, article, aside, nav, header, footer, figure),
20
- .section {
21
- max-inline-size: 100%; /* Nested sections can touch the side again */
22
- }
23
19
  }
24
20
  }
@@ -1,5 +1,6 @@
1
1
  import type { ReactElement } from "react";
2
2
  import { type ColorVariants, getColorClass } from "../style/Color.js";
3
+ import { getIndentClass, type IndentVariants } from "../style/Indent.js";
3
4
  import { getSpaceClass, type SpaceVariants } from "../style/Space.js";
4
5
  import { getTypographyClass, type TypographyVariants } from "../style/Typography.js";
5
6
  import { getWidthClass, type WidthVariants } from "../style/Width.js";
@@ -15,7 +16,7 @@ const SECTION_CLASS = getModuleClass(SECTION_CSS, "section");
15
16
  *
16
17
  * @see https://shelving.cc/ui/SectionProps
17
18
  */
18
- export interface SectionProps extends ColorVariants, SpaceVariants, TypographyVariants, WidthVariants, OptionalChildProps {
19
+ export interface SectionProps extends ColorVariants, IndentVariants, SpaceVariants, TypographyVariants, WidthVariants, OptionalChildProps {
19
20
  /**
20
21
  * Element this `<Section>` renders as, e.g. "header" to output a "<header>"
21
22
  * @default "section"
@@ -37,6 +38,7 @@ export function getSectionClass(variants: SectionProps): string {
37
38
  return getClass(
38
39
  SECTION_CLASS, //
39
40
  getColorClass(variants),
41
+ getIndentClass(variants),
40
42
  getSpaceClass(variants),
41
43
  getTypographyClass(variants),
42
44
  getWidthClass(variants),
@@ -19,5 +19,5 @@ import { TreeMarkup } from "../tree/TreeMarkup.js";
19
19
  * @see https://shelving.cc/ui/DocumentationHomePage
20
20
  */
21
21
  export function DocumentationHomePage({ title, name, description, content, children }) {
22
- return (_jsx(Page, { title: title ?? name, description: description, children: _jsxs(Block, { color: "red", children: [_jsx(Panel, { padding: "5x", children: _jsx(Title, { center: true, children: title ?? name }) }), content && (_jsx(Section, { children: _jsx(Prose, { children: _jsx(TreeMarkup, { children: content }) }) })), _jsx(Section, { children: _jsx(TreeCards, { children: children }) })] }) }));
22
+ return (_jsx(Page, { title: title ?? name, description: description, children: _jsxs(Block, { color: "red", children: [_jsx(Panel, { padding: "5x", children: _jsx(Title, { center: true, children: title ?? name }) }), _jsxs(Block, { indent: "normal", children: [content && (_jsx(Section, { children: _jsx(Prose, { children: _jsx(TreeMarkup, { children: content }) }) })), _jsx(Section, { children: _jsx(TreeCards, { children: children }) })] })] }) }));
23
23
  }
@@ -27,16 +27,18 @@ export function DocumentationHomePage({ title, name, description, content, child
27
27
  <Panel padding="5x">
28
28
  <Title center>{title ?? name}</Title>
29
29
  </Panel>
30
- {content && (
30
+ <Block indent="normal">
31
+ {content && (
32
+ <Section>
33
+ <Prose>
34
+ <TreeMarkup>{content}</TreeMarkup>
35
+ </Prose>
36
+ </Section>
37
+ )}
31
38
  <Section>
32
- <Prose>
33
- <TreeMarkup>{content}</TreeMarkup>
34
- </Prose>
39
+ <TreeCards>{children}</TreeCards>
35
40
  </Section>
36
- )}
37
- <Section>
38
- <TreeCards>{children}</TreeCards>
39
- </Section>
41
+ </Block>
40
42
  </Block>
41
43
  </Page>
42
44
  );
@@ -69,5 +69,5 @@ function DocumentationChildren({ elements }) {
69
69
  * @see https://shelving.cc/ui/DocumentationPage
70
70
  */
71
71
  export function DocumentationPage({ title, name, kind = "unknown", description, content, signatures, params, returns, throws, properties, types, examples, children, ...props }) {
72
- return (_jsx(Page, { title: title ?? name, description: description, children: _jsxs(Block, { color: getDocumentationKindColor(kind), children: [_jsx(Panel, { children: _jsxs(Header, { children: [_jsx(TreeBreadcrumbs, {}), _jsx(Title, { children: _jsxs(Row, { left: true, wrap: true, children: [title ?? name, kind && _jsx(DocumentationKind, { kind: kind, size: "normal" })] }) }), _jsx(DocumentationButtons, { ...props })] }) }), signatures?.length || params?.length || returns?.length || throws?.length || properties?.length || types?.length ? (_jsxs(Section, { children: [_jsx(DocumentationSignatures, { signatures: signatures }), _jsx(DocumentationParams, { params: params }), _jsx(DocumentationReturns, { returns: returns }), _jsx(DocumentationThrows, { throws: throws }), _jsx(DocumentationProperties, { properties: properties }), _jsx(DocumentationReferences, { types: types })] })) : null, content && (_jsx(Section, { children: _jsx(Prose, { children: _jsx(TreeMarkup, { children: content }) }) })), examples?.length && (_jsxs(Section, { children: [_jsx(Heading, { children: "Examples" }), examples.map(({ description }) => (_jsx(Preformatted, { children: description }, description)))] })), _jsx(DocumentationChildren, { elements: children })] }) }));
72
+ return (_jsx(Page, { title: title ?? name, description: description, children: _jsxs(Block, { color: getDocumentationKindColor(kind), children: [_jsx(Panel, { children: _jsxs(Header, { children: [_jsx(TreeBreadcrumbs, {}), _jsx(Title, { children: _jsxs(Row, { left: true, wrap: true, children: [title ?? name, kind && _jsx(DocumentationKind, { kind: kind, size: "normal" })] }) }), _jsx(DocumentationButtons, { ...props })] }) }), _jsxs(Block, { indent: "normal", children: [signatures?.length || params?.length || returns?.length || throws?.length || properties?.length || types?.length ? (_jsxs(Section, { children: [_jsx(DocumentationSignatures, { signatures: signatures }), _jsx(DocumentationParams, { params: params }), _jsx(DocumentationReturns, { returns: returns }), _jsx(DocumentationThrows, { throws: throws }), _jsx(DocumentationProperties, { properties: properties }), _jsx(DocumentationReferences, { types: types })] })) : null, content && (_jsx(Section, { children: _jsx(Prose, { children: _jsx(TreeMarkup, { children: content }) }) })), examples?.length && (_jsxs(Section, { children: [_jsx(Heading, { children: "Examples" }), examples.map(({ description }) => (_jsx(Preformatted, { children: description }, description)))] })), _jsx(DocumentationChildren, { elements: children })] })] }) }));
73
73
  }
@@ -110,32 +110,34 @@ export function DocumentationPage({
110
110
  <DocumentationButtons {...props} />
111
111
  </Header>
112
112
  </Panel>
113
- {signatures?.length || params?.length || returns?.length || throws?.length || properties?.length || types?.length ? (
114
- <Section>
115
- <DocumentationSignatures signatures={signatures} />
116
- <DocumentationParams params={params} />
117
- <DocumentationReturns returns={returns} />
118
- <DocumentationThrows throws={throws} />
119
- <DocumentationProperties properties={properties} />
120
- <DocumentationReferences types={types} />
121
- </Section>
122
- ) : null}
123
- {content && (
124
- <Section>
125
- <Prose>
126
- <TreeMarkup>{content}</TreeMarkup>
127
- </Prose>
128
- </Section>
129
- )}
130
- {examples?.length && (
131
- <Section>
132
- <Heading>Examples</Heading>
133
- {examples.map(({ description }) => (
134
- <Preformatted key={description}>{description}</Preformatted>
135
- ))}
136
- </Section>
137
- )}
138
- <DocumentationChildren elements={children} />
113
+ <Block indent="normal">
114
+ {signatures?.length || params?.length || returns?.length || throws?.length || properties?.length || types?.length ? (
115
+ <Section>
116
+ <DocumentationSignatures signatures={signatures} />
117
+ <DocumentationParams params={params} />
118
+ <DocumentationReturns returns={returns} />
119
+ <DocumentationThrows throws={throws} />
120
+ <DocumentationProperties properties={properties} />
121
+ <DocumentationReferences types={types} />
122
+ </Section>
123
+ ) : null}
124
+ {content && (
125
+ <Section>
126
+ <Prose>
127
+ <TreeMarkup>{content}</TreeMarkup>
128
+ </Prose>
129
+ </Section>
130
+ )}
131
+ {examples?.length && (
132
+ <Section>
133
+ <Heading>Examples</Heading>
134
+ {examples.map(({ description }) => (
135
+ <Preformatted key={description}>{description}</Preformatted>
136
+ ))}
137
+ </Section>
138
+ )}
139
+ <DocumentationChildren elements={children} />
140
+ </Block>
139
141
  </Block>
140
142
  </Page>
141
143
  );
package/ui/form/Form.d.ts CHANGED
@@ -3,6 +3,7 @@ import type { DataSchema } from "../../schema/DataSchema.js";
3
3
  import type { Data, PartialData } from "../../util/data.js";
4
4
  import type { ImmutableDictionary } from "../../util/dictionary.js";
5
5
  import type { Arguments } from "../../util/function.js";
6
+ import { type IndentVariants } from "../style/Indent.js";
6
7
  import { type NoticeCallback } from "../util/notice.js";
7
8
  import type { OptionalChildProps } from "../util/props.js";
8
9
  import { FormStore } from "./FormStore.js";
@@ -19,7 +20,7 @@ export type FormCallback<T extends Data> = (data: T, event: SubmitEvent<HTMLForm
19
20
  *
20
21
  * @see https://shelving.cc/ui/FormProps
21
22
  */
22
- export interface FormProps<T extends Data> extends OptionalChildProps {
23
+ export interface FormProps<T extends Data> extends IndentVariants, OptionalChildProps {
23
24
  /** Schema for the form. */
24
25
  schema: DataSchema<T>;
25
26
  /** Initial data for the form. */
package/ui/form/Form.js CHANGED
@@ -2,7 +2,8 @@ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-run
2
2
  import { useInstance } from "../../react/useInstance.js";
3
3
  import { useStore } from "../../react/useStore.js";
4
4
  import { isAsync } from "../../util/async.js";
5
- import { getModuleClass } from "../util/css.js";
5
+ import { getIndentClass } from "../style/Indent.js";
6
+ import { getClass, getModuleClass } from "../util/css.js";
6
7
  import { notifySuccess, notifyThrown } from "../util/notice.js";
7
8
  import FORM_CSS from "./Form.module.css";
8
9
  import { FormContext } from "./FormContext.js";
@@ -11,7 +12,7 @@ import { FormFooter } from "./FormFooter.js";
11
12
  import { FormStore } from "./FormStore.js";
12
13
  const FORM_CLASS = getModuleClass(FORM_CSS, "form");
13
14
  const FORM_FIELDSET_CLASS = getModuleClass(FORM_CSS, "fieldset");
14
- export function Form({ schema, data: initialData, onSubmit, submit, messages, children = (_jsxs(_Fragment, { children: [_jsx(FormFields, {}), _jsx(FormFooter, { submit: submit })] })), }) {
15
+ export function Form({ schema, data: initialData, onSubmit, submit, messages, children = (_jsxs(_Fragment, { children: [_jsx(FormFields, {}), _jsx(FormFooter, { submit: submit })] })), ...props }) {
15
16
  // Create a form store instance and subscribe to changes in it.
16
17
  const store = useStore(useInstance(FormStore, schema, initialData, messages));
17
18
  const busy = useStore(store.busy).value;
@@ -26,7 +27,7 @@ export function Form({ schema, data: initialData, onSubmit, submit, messages, ch
26
27
  // Close the parent dialog on successful submit.
27
28
  if (result)
28
29
  dialog?.close();
29
- }, className: FORM_CLASS, noValidate: true, children: _jsx("fieldset", { className: FORM_FIELDSET_CLASS, disabled: busy, children: _jsx(FormContext, { value: store, children: children }) }) }, store.key));
30
+ }, className: getClass(FORM_CLASS, getIndentClass(props)), noValidate: true, children: _jsx("fieldset", { className: FORM_FIELDSET_CLASS, disabled: busy, children: _jsx(FormContext, { value: store, children: children }) }) }, store.key));
30
31
  }
31
32
  /**
32
33
  * Run a form submit callback and publish success/error notices based on its return or thrown value.
package/ui/form/Form.md CHANGED
@@ -105,4 +105,4 @@ When an `onSubmit` callback returns a non-empty `ReactNode`, `Form` dispatches i
105
105
  |---|---|---|
106
106
  | `--form-space` | Outer block margin (top + bottom) | `var(--space-paragraph)` (16px) |
107
107
 
108
- **Global tokens it reads:** `--space-paragraph`.
108
+ **Global tokens it reads:** `--space-paragraph`. The `indent` variant (inline padding, via `getIndentClass()`) comes from the shared `shelving/ui` styling system — set `<Form indent="normal">` to inset the form's content from the edges.
@@ -1,4 +1,5 @@
1
1
  @import "../style/layers.css";
2
+ @import "../style/Indent.module.css";
2
3
  @import "../style/Space.module.css";
3
4
 
4
5
  @layer components {
@@ -18,7 +19,7 @@
18
19
  border: 0;
19
20
 
20
21
  /* Chrome sets a default min-inline-size that we override. */
21
- min-inline-size: none;
22
+ min-inline-size: auto;
22
23
  }
23
24
  }
24
25
 
package/ui/form/Form.tsx CHANGED
@@ -6,7 +6,8 @@ import { isAsync } from "../../util/async.js";
6
6
  import type { Data, PartialData } from "../../util/data.js";
7
7
  import type { ImmutableDictionary } from "../../util/dictionary.js";
8
8
  import type { Arguments } from "../../util/function.js";
9
- import { getModuleClass } from "../util/css.js";
9
+ import { getIndentClass, type IndentVariants } from "../style/Indent.js";
10
+ import { getClass, getModuleClass } from "../util/css.js";
10
11
  import { type NoticeCallback, notifySuccess, notifyThrown } from "../util/notice.js";
11
12
  import type { OptionalChildProps } from "../util/props.js";
12
13
  import FORM_CSS from "./Form.module.css";
@@ -35,7 +36,7 @@ export type FormCallback<T extends Data> = (
35
36
  *
36
37
  * @see https://shelving.cc/ui/FormProps
37
38
  */
38
- export interface FormProps<T extends Data> extends OptionalChildProps {
39
+ export interface FormProps<T extends Data> extends IndentVariants, OptionalChildProps {
39
40
  /** Schema for the form. */
40
41
  schema: DataSchema<T>;
41
42
  /** Initial data for the form. */
@@ -71,6 +72,7 @@ export function Form({
71
72
  <FormFooter submit={submit} />
72
73
  </>
73
74
  ),
75
+ ...props
74
76
  }: FormProps<Data>): ReactElement {
75
77
  // Create a form store instance and subscribe to changes in it.
76
78
  const store = useStore(useInstance(FormStore, schema, initialData, messages));
@@ -94,7 +96,7 @@ export function Form({
94
96
  // Close the parent dialog on successful submit.
95
97
  if (result) dialog?.close();
96
98
  }}
97
- className={FORM_CLASS}
99
+ className={getClass(FORM_CLASS, getIndentClass(props))}
98
100
  noValidate={true}
99
101
  >
100
102
  <fieldset className={FORM_FIELDSET_CLASS} disabled={busy}>
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Allowed values for inline-padding ("indent") for components that support `IndentVariants`
3
+ *
4
+ * @see https://shelving.cc/ui/IndentVariant
5
+ */
6
+ export type IndentVariant = "none" | "xxsmall" | "xsmall" | "small" | "normal" | "large" | "xlarge" | "xxlarge" | "1x" | "2x" | "3x" | "4x" | "5x" | "6x" | "7x" | "8x" | "9x" | "10x";
7
+ /**
8
+ * Variant props for the inline-padding ("indent", left + right) of a component, e.g. `indent="normal"`.
9
+ *
10
+ * @see https://shelving.cc/ui/IndentVariants
11
+ */
12
+ export interface IndentVariants {
13
+ /** Inline-padding (left + right) of the element, keeping its content off the edges. */
14
+ indent?: IndentVariant | undefined;
15
+ }
16
+ /**
17
+ * Get the inline-padding ("indent") class for a component from its `indent` variant prop.
18
+ *
19
+ * @param variants
20
+ * @returns The indent class string, or `undefined` when no `indent` is set.
21
+ * @example getIndentClass({ indent: "normal" }) // "indent-normal"
22
+ * @see https://shelving.cc/ui/getIndentClass
23
+ */
24
+ export declare function getIndentClass({ indent }: IndentVariants): string | undefined;
@@ -0,0 +1,13 @@
1
+ import { getModuleClass } from "../util/css.js";
2
+ import INDENT_CSS from "./Indent.module.css";
3
+ /**
4
+ * Get the inline-padding ("indent") class for a component from its `indent` variant prop.
5
+ *
6
+ * @param variants
7
+ * @returns The indent class string, or `undefined` when no `indent` is set.
8
+ * @example getIndentClass({ indent: "normal" }) // "indent-normal"
9
+ * @see https://shelving.cc/ui/getIndentClass
10
+ */
11
+ export function getIndentClass({ indent }) {
12
+ return indent && getModuleClass(INDENT_CSS, `indent-${indent}`);
13
+ }
@@ -0,0 +1,62 @@
1
+ @import "layers.css";
2
+ @import "Space.module.css";
3
+
4
+ @layer variants {
5
+ /* Inline-padding ("indent") variants — left + right padding to keep content off the edges. */
6
+ .indent-none {
7
+ padding-inline: 0;
8
+ }
9
+ .indent-xxsmall {
10
+ padding-inline: var(--space-xxsmall);
11
+ }
12
+ .indent-xsmall {
13
+ padding-inline: var(--space-xsmall);
14
+ }
15
+ .indent-small {
16
+ padding-inline: var(--space-small);
17
+ }
18
+ .indent-normal {
19
+ padding-inline: var(--space-normal);
20
+ }
21
+ .indent-large {
22
+ padding-inline: var(--space-large);
23
+ }
24
+ .indent-xlarge {
25
+ padding-inline: var(--space-xlarge);
26
+ }
27
+ .indent-xxlarge {
28
+ padding-inline: var(--space-xxlarge);
29
+ }
30
+
31
+ /* Numeric inline-padding multiples of `--space-normal` (`1x` … `10x`). */
32
+ .indent-1x {
33
+ padding-inline: calc(var(--space-normal) * 1);
34
+ }
35
+ .indent-2x {
36
+ padding-inline: calc(var(--space-normal) * 2);
37
+ }
38
+ .indent-3x {
39
+ padding-inline: calc(var(--space-normal) * 3);
40
+ }
41
+ .indent-4x {
42
+ padding-inline: calc(var(--space-normal) * 4);
43
+ }
44
+ .indent-5x {
45
+ padding-inline: calc(var(--space-normal) * 5);
46
+ }
47
+ .indent-6x {
48
+ padding-inline: calc(var(--space-normal) * 6);
49
+ }
50
+ .indent-7x {
51
+ padding-inline: calc(var(--space-normal) * 7);
52
+ }
53
+ .indent-8x {
54
+ padding-inline: calc(var(--space-normal) * 8);
55
+ }
56
+ .indent-9x {
57
+ padding-inline: calc(var(--space-normal) * 9);
58
+ }
59
+ .indent-10x {
60
+ padding-inline: calc(var(--space-normal) * 10);
61
+ }
62
+ }
@@ -0,0 +1,49 @@
1
+ import { getModuleClass } from "../util/css.js";
2
+ import INDENT_CSS from "./Indent.module.css";
3
+
4
+ /**
5
+ * Allowed values for inline-padding ("indent") for components that support `IndentVariants`
6
+ *
7
+ * @see https://shelving.cc/ui/IndentVariant
8
+ */
9
+ export type IndentVariant =
10
+ | "none"
11
+ | "xxsmall"
12
+ | "xsmall"
13
+ | "small"
14
+ | "normal"
15
+ | "large"
16
+ | "xlarge"
17
+ | "xxlarge"
18
+ | "1x"
19
+ | "2x"
20
+ | "3x"
21
+ | "4x"
22
+ | "5x"
23
+ | "6x"
24
+ | "7x"
25
+ | "8x"
26
+ | "9x"
27
+ | "10x";
28
+
29
+ /**
30
+ * Variant props for the inline-padding ("indent", left + right) of a component, e.g. `indent="normal"`.
31
+ *
32
+ * @see https://shelving.cc/ui/IndentVariants
33
+ */
34
+ export interface IndentVariants {
35
+ /** Inline-padding (left + right) of the element, keeping its content off the edges. */
36
+ indent?: IndentVariant | undefined;
37
+ }
38
+
39
+ /**
40
+ * Get the inline-padding ("indent") class for a component from its `indent` variant prop.
41
+ *
42
+ * @param variants
43
+ * @returns The indent class string, or `undefined` when no `indent` is set.
44
+ * @example getIndentClass({ indent: "normal" }) // "indent-normal"
45
+ * @see https://shelving.cc/ui/getIndentClass
46
+ */
47
+ export function getIndentClass({ indent }: IndentVariants): string | undefined {
48
+ return indent && getModuleClass(INDENT_CSS, `indent-${indent}`);
49
+ }
@@ -2,9 +2,9 @@
2
2
 
3
3
  The `space` variant prop sets a block-level element's `margin-block` (top + bottom) from the spacing scale — `<Section space="large">`, `<Paragraph space="none">`. It's an **override** for one-off spacing; for an app-wide change, retune the spacing variables below in a theme file.
4
4
 
5
- `getSpaceClass({ space })` maps the prop to a margin class (e.g. `space="large"` → `large`). The same scale also backs `getPaddingClass()` and `getGapClass()`, so all three move together when you change `--space-normal`.
5
+ `getSpaceClass({ space })` maps the prop to a margin class (e.g. `space="large"` → `large`). The same scale also backs `getPaddingClass()` (block padding), `getIndentClass()` (inline padding), and `getGapClass()`, so they all move together when you change `--space-normal`.
6
6
 
7
- Alongside the named scale, `space` and `padding` also accept numeric multiples of `--space-normal` — `1x` … `10x` (e.g. `padding="5x"` → `calc(var(--space-normal) * 5)`) — for larger one-off blocks like hero panels.
7
+ Alongside the named scale, `space`, `padding`, and `indent` also accept numeric multiples of `--space-normal` — `1x` … `10x` (e.g. `padding="5x"` → `calc(var(--space-normal) * 5)`) — for larger one-off blocks like hero panels.
8
8
 
9
9
  ## Theme variables
10
10
 
@@ -2,6 +2,7 @@ export * from "./Color.js";
2
2
  export * from "./Duration.js";
3
3
  export * from "./Flex.js";
4
4
  export * from "./Gap.js";
5
+ export * from "./Indent.js";
5
6
  export * from "./Padding.js";
6
7
  export * from "./Radius.js";
7
8
  export * from "./Scroll.js";
package/ui/style/index.js CHANGED
@@ -2,6 +2,7 @@ export * from "./Color.js";
2
2
  export * from "./Duration.js";
3
3
  export * from "./Flex.js";
4
4
  export * from "./Gap.js";
5
+ export * from "./Indent.js";
5
6
  export * from "./Padding.js";
6
7
  export * from "./Radius.js";
7
8
  export * from "./Scroll.js";
@@ -2,6 +2,7 @@ export * from "./Color.js";
2
2
  export * from "./Duration.js";
3
3
  export * from "./Flex.js";
4
4
  export * from "./Gap.js";
5
+ export * from "./Indent.js";
5
6
  export * from "./Padding.js";
6
7
  export * from "./Radius.js";
7
8
  export * from "./Scroll.js";
@@ -2,6 +2,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { useMemo, useState } from "react";
3
3
  import { toggleArrayItem } from "../../util/array.js";
4
4
  import { searchTree } from "../../util/tree.js";
5
+ import { Block } from "../block/Block.js";
5
6
  import { Header, Section } from "../block/Section.js";
6
7
  import { Title } from "../block/Title.js";
7
8
  import { DocumentationKind } from "../docs/DocumentationKind.js";
@@ -51,5 +52,5 @@ export function TreeIndexPage() {
51
52
  const filter = selected.length ? { kind: selected } : undefined;
52
53
  // Each element's `key` is its unique canonical path (stamped by `flattenTree()`), so this flat cross-tree listing reconciles correctly.
53
54
  const cards = root ? searchTree(root, trimmed, { limit: trimmed ? 20 : INDEX_LIMIT, filter }) : [];
54
- return (_jsxs(Page, { title: INDEX_TITLE, description: INDEX_DESCRIPTION, children: [_jsx(Header, { children: _jsx(Title, { children: INDEX_TITLE }) }), _jsxs(Section, { children: [_jsx(TextInput, { name: "search", title: "Search", placeholder: "Search\u2026", value: query, onValue: v => setQuery(v ?? "") }), !!kinds.length && (_jsx(Row, { left: true, wrap: true, children: kinds.map(kind => (_jsx(CheckboxInput, { name: kind, width: "fit", value: selected.includes(kind), onValue: () => setSelected(s => toggleArrayItem(s, kind)), children: _jsx(DocumentationKind, { kind: kind }) }, kind))) }))] }), _jsx(Section, { children: _jsx(TreeCards, { children: cards }) })] }));
55
+ return (_jsx(Page, { title: INDEX_TITLE, description: INDEX_DESCRIPTION, children: _jsxs(Block, { indent: "normal", children: [_jsx(Header, { children: _jsx(Title, { children: INDEX_TITLE }) }), _jsxs(Section, { children: [_jsx(TextInput, { name: "search", title: "Search", placeholder: "Search\u2026", value: query, onValue: v => setQuery(v ?? "") }), !!kinds.length && (_jsx(Row, { left: true, wrap: true, children: kinds.map(kind => (_jsx(CheckboxInput, { name: kind, width: "fit", value: selected.includes(kind), onValue: () => setSelected(s => toggleArrayItem(s, kind)), children: _jsx(DocumentationKind, { kind: kind }) }, kind))) }))] }), _jsx(Section, { children: _jsx(TreeCards, { children: cards }) })] }) }));
55
56
  }
@@ -4,6 +4,7 @@ import type { AbsolutePath } from "../../util/path.js";
4
4
  import type { Query } from "../../util/query.js";
5
5
  import type { DocumentationElementProps } from "../../util/tree.js";
6
6
  import { searchTree } from "../../util/tree.js";
7
+ import { Block } from "../block/Block.js";
7
8
  import { Header, Section } from "../block/Section.js";
8
9
  import { Title } from "../block/Title.js";
9
10
  import { DocumentationKind } from "../docs/DocumentationKind.js";
@@ -64,30 +65,32 @@ export function TreeIndexPage(): ReactNode {
64
65
 
65
66
  return (
66
67
  <Page title={INDEX_TITLE} description={INDEX_DESCRIPTION}>
67
- <Header>
68
- <Title>{INDEX_TITLE}</Title>
69
- </Header>
70
- <Section>
71
- <TextInput name="search" title="Search" placeholder="Search…" value={query} onValue={v => setQuery(v ?? "")} />
72
- {!!kinds.length && (
73
- <Row left wrap>
74
- {kinds.map(kind => (
75
- <CheckboxInput
76
- key={kind}
77
- name={kind}
78
- width="fit"
79
- value={selected.includes(kind)}
80
- onValue={() => setSelected(s => toggleArrayItem(s, kind))}
81
- >
82
- <DocumentationKind kind={kind} />
83
- </CheckboxInput>
84
- ))}
85
- </Row>
86
- )}
87
- </Section>
88
- <Section>
89
- <TreeCards>{cards}</TreeCards>
90
- </Section>
68
+ <Block indent="normal">
69
+ <Header>
70
+ <Title>{INDEX_TITLE}</Title>
71
+ </Header>
72
+ <Section>
73
+ <TextInput name="search" title="Search" placeholder="Search…" value={query} onValue={v => setQuery(v ?? "")} />
74
+ {!!kinds.length && (
75
+ <Row left wrap>
76
+ {kinds.map(kind => (
77
+ <CheckboxInput
78
+ key={kind}
79
+ name={kind}
80
+ width="fit"
81
+ value={selected.includes(kind)}
82
+ onValue={() => setSelected(s => toggleArrayItem(s, kind))}
83
+ >
84
+ <DocumentationKind kind={kind} />
85
+ </CheckboxInput>
86
+ ))}
87
+ </Row>
88
+ )}
89
+ </Section>
90
+ <Section>
91
+ <TreeCards>{cards}</TreeCards>
92
+ </Section>
93
+ </Block>
91
94
  </Page>
92
95
  );
93
96
  }
@@ -1,4 +1,5 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Block } from "../block/Block.js";
2
3
  import { Prose } from "../block/Prose.js";
3
4
  import { Header, Section } from "../block/Section.js";
4
5
  import { Title } from "../block/Title.js";
@@ -17,5 +18,5 @@ import { TreeMarkup } from "./TreeMarkup.js";
17
18
  * @see https://shelving.cc/ui/TreePage
18
19
  */
19
20
  export function TreePage({ title, name, description, content, children }) {
20
- return (_jsxs(Page, { title: title ?? name, description: description, children: [_jsx(Header, { children: _jsx(Title, { children: title ?? name }) }), _jsx(Section, { children: content && (_jsx(Prose, { children: _jsx(TreeMarkup, { children: content }) })) }), _jsx(Section, { children: _jsx(TreeCards, { children: children }) })] }));
21
+ return (_jsx(Page, { title: title ?? name, description: description, children: _jsxs(Block, { indent: "normal", children: [_jsx(Header, { children: _jsx(Title, { children: title ?? name }) }), _jsx(Section, { children: content && (_jsx(Prose, { children: _jsx(TreeMarkup, { children: content }) })) }), _jsx(Section, { children: _jsx(TreeCards, { children: children }) })] }) }));
21
22
  }
@@ -1,5 +1,6 @@
1
1
  import type { ReactNode } from "react";
2
2
  import type { TreeElementProps } from "../../util/tree.js";
3
+ import { Block } from "../block/Block.js";
3
4
  import { Prose } from "../block/Prose.js";
4
5
  import { Header, Section } from "../block/Section.js";
5
6
  import { Title } from "../block/Title.js";
@@ -21,19 +22,21 @@ import { TreeMarkup } from "./TreeMarkup.js";
21
22
  export function TreePage({ title, name, description, content, children }: TreeElementProps): ReactNode {
22
23
  return (
23
24
  <Page title={title ?? name} description={description}>
24
- <Header>
25
- <Title>{title ?? name}</Title>
26
- </Header>
27
- <Section>
28
- {content && (
29
- <Prose>
30
- <TreeMarkup>{content}</TreeMarkup>
31
- </Prose>
32
- )}
33
- </Section>
34
- <Section>
35
- <TreeCards>{children}</TreeCards>
36
- </Section>
25
+ <Block indent="normal">
26
+ <Header>
27
+ <Title>{title ?? name}</Title>
28
+ </Header>
29
+ <Section>
30
+ {content && (
31
+ <Prose>
32
+ <TreeMarkup>{content}</TreeMarkup>
33
+ </Prose>
34
+ )}
35
+ </Section>
36
+ <Section>
37
+ <TreeCards>{children}</TreeCards>
38
+ </Section>
39
+ </Block>
37
40
  </Page>
38
41
  );
39
42
  }