@usableapp/cardds 0.7.105 → 0.7.107

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 (100) hide show
  1. package/CHANGELOG.md +134 -0
  2. package/bin/cardds.mjs +6 -2
  3. package/cardds.css +1 -0
  4. package/css/actions.css +16 -9
  5. package/css/base.css +6 -9
  6. package/css/card.css +21 -173
  7. package/css/finish.css +4 -4
  8. package/css/journey.css +4 -1
  9. package/css/layover.css +4 -14
  10. package/css/lists.css +3 -18
  11. package/css/media.css +1 -7
  12. package/css/parts.css +380 -0
  13. package/css/sheet.css +21 -140
  14. package/css/stack.css +15 -26
  15. package/css/stamp.css +1 -1
  16. package/css/step.css +3 -3
  17. package/css/theme.template.css +1 -1
  18. package/css/tokens.css +5 -4
  19. package/dist/actions/BarNote.d.ts +2 -2
  20. package/dist/actions/BarNote.js +2 -2
  21. package/dist/actions/Btn.d.ts +2 -2
  22. package/dist/actions/Btn.js +1 -1
  23. package/dist/actions/BtnRow.d.ts +1 -1
  24. package/dist/actions/BtnRow.js +1 -1
  25. package/dist/actions/Chip.d.ts +7 -3
  26. package/dist/actions/Chip.js +5 -5
  27. package/dist/actions/IconBtn.d.ts +1 -1
  28. package/dist/actions/IconBtn.js +1 -1
  29. package/dist/actions/IconBtnGroup.d.ts +1 -1
  30. package/dist/cardds.css +486 -410
  31. package/dist/cards/Card.d.ts +6 -22
  32. package/dist/cards/Card.js +11 -22
  33. package/dist/cards/CardFeed.d.ts +1 -1
  34. package/dist/cards/CardFeed.js +5 -5
  35. package/dist/cards/CardParts.d.ts +0 -12
  36. package/dist/cards/CardParts.js +0 -12
  37. package/dist/cards/cardGo.js +1 -1
  38. package/dist/cards/cardGrounds.js +2 -2
  39. package/dist/catalog.json +1 -1
  40. package/dist/forms/Field.d.ts +1 -1
  41. package/dist/forms/Field.js +1 -1
  42. package/dist/index.d.ts +6 -5
  43. package/dist/index.js +6 -5
  44. package/dist/lists/Row.d.ts +7 -22
  45. package/dist/lists/Row.js +6 -4
  46. package/dist/media/MapArea.d.ts +7 -3
  47. package/dist/media/MapArea.js +4 -3
  48. package/dist/media/Tile.d.ts +3 -7
  49. package/dist/media/Tile.js +5 -5
  50. package/dist/numbers/Spinner.d.ts +1 -1
  51. package/dist/numbers/Spinner.js +1 -1
  52. package/dist/parts/Body.d.ts +7 -0
  53. package/dist/parts/Body.js +11 -0
  54. package/dist/parts/End.d.ts +10 -0
  55. package/dist/parts/End.js +7 -0
  56. package/dist/parts/Foot.d.ts +17 -0
  57. package/dist/parts/Foot.js +11 -0
  58. package/dist/parts/Head.d.ts +15 -0
  59. package/dist/parts/Head.js +34 -0
  60. package/dist/parts/Lead.d.ts +10 -0
  61. package/dist/parts/Lead.js +9 -0
  62. package/dist/parts/Words.d.ts +13 -0
  63. package/dist/parts/Words.js +20 -0
  64. package/dist/parts/headCtx.d.ts +7 -0
  65. package/dist/parts/headCtx.js +9 -0
  66. package/dist/scaffold/EmptyState.d.ts +1 -1
  67. package/dist/scaffold/EmptyState.js +1 -1
  68. package/dist/scaffold/Screen.js +1 -1
  69. package/dist/sheets/Modal.d.ts +3 -7
  70. package/dist/sheets/Modal.js +3 -7
  71. package/dist/sheets/Sheet.d.ts +2 -2
  72. package/dist/sheets/Sheet.js +8 -9
  73. package/dist/sheets/SheetStack.d.ts +3 -3
  74. package/dist/sheets/SheetStack.js +4 -4
  75. package/dist/sheets/SheetSteps.d.ts +1 -1
  76. package/dist/sheets/SheetSteps.js +2 -2
  77. package/dist/sheets/StackBack.d.ts +1 -1
  78. package/dist/sheets/StackBack.js +1 -1
  79. package/dist/sheets/StackSheet.js +7 -7
  80. package/dist/sheets/sheetMeasure.js +3 -3
  81. package/dist/sheets/sheetStepsPile.js +2 -2
  82. package/dist/sheets/stackCard.js +6 -6
  83. package/dist/templates/CallSheet.js +6 -5
  84. package/dist/templates/Walkthrough.js +3 -2
  85. package/docs/reference.md +75 -36
  86. package/lib/catalog.mjs +2 -0
  87. package/lib/mcp.mjs +1 -1
  88. package/lint/rules.mjs +22 -8
  89. package/package.json +2 -1
  90. package/scripts/codemod-parts.mjs +324 -0
  91. package/dist/cards/CardFoot.d.ts +0 -15
  92. package/dist/cards/CardFoot.js +0 -9
  93. package/dist/cards/CardHead.d.ts +0 -21
  94. package/dist/cards/CardHead.js +0 -39
  95. package/dist/cards/Stat.d.ts +0 -19
  96. package/dist/cards/Stat.js +0 -14
  97. package/dist/sheets/SheetBody.d.ts +0 -5
  98. package/dist/sheets/SheetBody.js +0 -9
  99. package/dist/sheets/SheetHead.d.ts +0 -15
  100. package/dist/sheets/SheetHead.js +0 -6
@@ -38,7 +38,7 @@ export interface FieldProps extends Omit<ComponentProps<'input'>, 'className' |
38
38
  export declare function applyMask(v: string, mask: string): string;
39
39
  /**
40
40
  * Field — the flat outlined input, and where a form field goes is the SHEET: the paper the member fills in, never a Card (Lh 2026-10-18).
41
- * It stands on a Sheet's `SheetBody`, in a `SheetStep`, or on a stack sheet, 1–3 per step; a card is for reading and pressing buttons
41
+ * It stands on a Sheet's `Body`, in a `SheetStep`, or on a stack sheet, 1–3 per step; a card is for reading and pressing buttons
42
42
  * (`cardds check` rule `field-in-card` flags a Field, or any control the member fills or picks, inside a `<Card>`).
43
43
  * The LABEL IS THE PLACEHOLDER. 48px tall (56px with an action inside).
44
44
  * Variation `rows={n}`: several lines in the same frame, for what the member writes in words (the request and its reason on the ask
@@ -21,7 +21,7 @@ export function applyMask(v, mask) {
21
21
  }
22
22
  /**
23
23
  * Field — the flat outlined input, and where a form field goes is the SHEET: the paper the member fills in, never a Card (Lh 2026-10-18).
24
- * It stands on a Sheet's `SheetBody`, in a `SheetStep`, or on a stack sheet, 1–3 per step; a card is for reading and pressing buttons
24
+ * It stands on a Sheet's `Body`, in a `SheetStep`, or on a stack sheet, 1–3 per step; a card is for reading and pressing buttons
25
25
  * (`cardds check` rule `field-in-card` flags a Field, or any control the member fills or picks, inside a `<Card>`).
26
26
  * The LABEL IS THE PLACEHOLDER. 48px tall (56px with an action inside).
27
27
  * Variation `rows={n}`: several lines in the same frame, for what the member writes in words (the request and its reason on the ask
package/dist/index.d.ts CHANGED
@@ -23,17 +23,12 @@ export * from './scaffold/LayerHistory.js';
23
23
  export * from './cards/Card.js';
24
24
  export * from './cards/cardGrounds.js';
25
25
  export * from './cards/inkGrounds.js';
26
- export * from './cards/CardHead.js';
27
- export * from './cards/CardFoot.js';
28
26
  export * from './cards/CardParts.js';
29
27
  export * from './cards/CardFeed.js';
30
- export * from './cards/Stat.js';
31
28
  export * from './cards/Placeholder.js';
32
29
  export * from './sheets/SheetStack.js';
33
30
  export * from './sheets/StackBack.js';
34
31
  export * from './sheets/Sheet.js';
35
- export * from './sheets/SheetHead.js';
36
- export * from './sheets/SheetBody.js';
37
32
  export * from './sheets/SheetStage.js';
38
33
  export * from './sheets/Step.js';
39
34
  export * from './sheets/SheetSteps.js';
@@ -72,6 +67,12 @@ export * from './numbers/Bars.js';
72
67
  export * from './people/Avatar.js';
73
68
  export * from './people/AvatarPick.js';
74
69
  export * from './lists/Row.js';
70
+ export * from './parts/Head.js';
71
+ export * from './parts/Lead.js';
72
+ export * from './parts/Words.js';
73
+ export * from './parts/Body.js';
74
+ export * from './parts/Foot.js';
75
+ export * from './parts/End.js';
75
76
  export * from './lists/Kv.js';
76
77
  export * from './lists/Timeline.js';
77
78
  export * from './lists/Legend.js';
package/dist/index.js CHANGED
@@ -27,17 +27,12 @@ export * from './scaffold/LayerHistory.js';
27
27
  export * from './cards/Card.js';
28
28
  export * from './cards/cardGrounds.js';
29
29
  export * from './cards/inkGrounds.js';
30
- export * from './cards/CardHead.js';
31
- export * from './cards/CardFoot.js';
32
30
  export * from './cards/CardParts.js';
33
31
  export * from './cards/CardFeed.js';
34
- export * from './cards/Stat.js';
35
32
  export * from './cards/Placeholder.js';
36
33
  export * from './sheets/SheetStack.js';
37
34
  export * from './sheets/StackBack.js';
38
35
  export * from './sheets/Sheet.js';
39
- export * from './sheets/SheetHead.js';
40
- export * from './sheets/SheetBody.js';
41
36
  export * from './sheets/SheetStage.js';
42
37
  export * from './sheets/Step.js';
43
38
  export * from './sheets/SheetSteps.js';
@@ -76,6 +71,12 @@ export * from './numbers/Bars.js';
76
71
  export * from './people/Avatar.js';
77
72
  export * from './people/AvatarPick.js';
78
73
  export * from './lists/Row.js';
74
+ export * from './parts/Head.js';
75
+ export * from './parts/Lead.js';
76
+ export * from './parts/Words.js';
77
+ export * from './parts/Body.js';
78
+ export * from './parts/Foot.js';
79
+ export * from './parts/End.js';
79
80
  export * from './lists/Kv.js';
80
81
  export * from './lists/Timeline.js';
81
82
  export * from './lists/Legend.js';
@@ -1,29 +1,12 @@
1
1
  import { type ComponentProps, type ReactNode } from 'react';
2
- import { type IconName } from '../type/Icon.js';
3
2
  export interface RowProps extends Omit<ComponentProps<'div'>, 'title'> {
4
- /** the leading visual: an Avatar, an AvatarStack, an Icon */
5
- lead?: ReactNode;
6
- /** an icon in the outlined 48px lead box (menu rows) */
7
- leadIcon?: IconName;
8
- /** the title line */
9
- title?: ReactNode;
10
- /** the caption under it (may hold a Dot) */
11
- meta?: ReactNode;
12
- /** custom body content instead of (or under) title/meta — a Wave for a voice note */
13
- body?: ReactNode;
14
- /** the trailing control: an IconBtn, a Chip, a Check, a Toggle, a caption */
15
- end?: ReactNode;
16
- /** a trailing value, right-aligned (title type) */
17
- endValue?: ReactNode;
18
- /** the muted caption under `endValue` */
19
- endCaption?: ReactNode;
20
3
  /** menu row: more air, a bigger title */
21
4
  lg?: boolean;
22
- /** a finished checklist item: muted words, a filled tick at the end, no longer a tap target (aria-disabled) — pass no `end` */
5
+ /** a finished checklist item: muted words, a filled tick at the end, no longer a tap target (aria-disabled) — give it no control at the end */
23
6
  done?: boolean;
24
7
  /** div (default), a link, a label wrapping a Check/Toggle, li */
25
8
  as?: 'div' | 'a' | 'label' | 'li';
26
- /** with `as="label"`: the `id` of the control the row presses (its `Check`). Needed when the `end` holds a button BEFORE the check (a picked row's own move, "open the map"): a label presses the first control inside it, so without this a touch on the row's words would press that button */
9
+ /** with `as="label"`: the `id` of the control the row presses (its `Check`). Needed when a button stands BEFORE the check (a picked row's own move, "open the map"): a label presses the first control inside it, so without this a touch on the row's words would press that button */
27
10
  htmlFor?: string;
28
11
  /** makes the row a link (an `a`); not with `more`, whose row is a summary */
29
12
  href?: string;
@@ -37,13 +20,15 @@ export interface RowProps extends Omit<ComponentProps<'div'>, 'title'> {
37
20
  onOpenChange?: (open: boolean) => void;
38
21
  }
39
22
  /**
40
- * Row — a frameless row inside a card: lead · title + meta · end. 48px+ tall, a tap target.
23
+ * Row — a frameless row inside a card: lead · words · end, its children in that order. 48px+ tall, a tap target.
24
+ * The lead is a `Lead` box, or a bare Avatar / AvatarStack; the words are a `Words`; the end is an `End` (a value and its caption)
25
+ * or a bare control (two side by side).
41
26
  * Several go in Rows (hairlines between). A row that IS a card is `Card row`.
42
27
  * `done` is the checklist's finished line: ticked, muted, not tappable any more.
43
28
  * `more` makes it expand: the row is the summary of a `<details class="row-expand">`, `more` comes out under it; `open` + `onOpenChange`, or `defaultOpen`.
44
- * An `end` chevron turns when it is open — the app fetches whatever `more` lists; the row only shows it.
29
+ * A chevron at the end turns when it is open — the app fetches whatever `more` lists; the row only shows it.
45
30
  */
46
- export declare function Row({ lead, leadIcon, title, meta, body, end, endValue, endCaption, lg, done, as, href, more, open, defaultOpen, onOpenChange, className, children, ...rest }: RowProps): import("react").JSX.Element;
31
+ export declare function Row({ lg, done, as, href, more, open, defaultOpen, onOpenChange, className, children, ...rest }: RowProps): import("react").JSX.Element;
47
32
  export interface RowsProps extends ComponentProps<'div'> {
48
33
  /** the group's name, above its rows (Today · Tomorrow · Later): a long list is GROUPED, not spaced out — several Rows one after another.
49
34
  * Words = the small label; an ELEMENT (`<Text level="title" as="h3">`) is used as the heading itself — the group is labelled by it. */
package/dist/lists/Row.js CHANGED
@@ -4,16 +4,18 @@ import { cx } from '../cx.js';
4
4
  import { Icon } from '../type/Icon.js';
5
5
  import { glide, reducedMotion, setRefs, stopMoves, useDisclosure } from '../state.js';
6
6
  /**
7
- * Row — a frameless row inside a card: lead · title + meta · end. 48px+ tall, a tap target.
7
+ * Row — a frameless row inside a card: lead · words · end, its children in that order. 48px+ tall, a tap target.
8
+ * The lead is a `Lead` box, or a bare Avatar / AvatarStack; the words are a `Words`; the end is an `End` (a value and its caption)
9
+ * or a bare control (two side by side).
8
10
  * Several go in Rows (hairlines between). A row that IS a card is `Card row`.
9
11
  * `done` is the checklist's finished line: ticked, muted, not tappable any more.
10
12
  * `more` makes it expand: the row is the summary of a `<details class="row-expand">`, `more` comes out under it; `open` + `onOpenChange`, or `defaultOpen`.
11
- * An `end` chevron turns when it is open — the app fetches whatever `more` lists; the row only shows it.
13
+ * A chevron at the end turns when it is open — the app fetches whatever `more` lists; the row only shows it.
12
14
  */
13
- export function Row({ lead, leadIcon, title, meta, body, end, endValue, endCaption, lg, done, as, href, more, open, defaultOpen, onOpenChange, className, children, ...rest }) {
15
+ export function Row({ lg, done, as, href, more, open, defaultOpen, onOpenChange, className, children, ...rest }) {
14
16
  const disclosure = useDisclosure(open, defaultOpen, onOpenChange);
15
17
  const el = more !== undefined ? 'summary' : as ?? (href ? 'a' : 'div');
16
- const row = createElement(el, { href, className: cx('row', lg && 'row--lg', done && 'row--done', className), 'aria-disabled': done || undefined, ...(more === undefined ? rest : {}) }, leadIcon ? _jsx("span", { className: "row__lead", children: _jsx(Icon, { name: leadIcon }) }) : lead, (title != null || meta != null || body != null) && (_jsxs("div", { className: "row__body", children: [title != null && _jsx("span", { className: "row__title", children: title }), meta != null && _jsx("span", { className: "row__meta", children: meta }), body] })), children, (endValue != null || endCaption != null) && (_jsxs("span", { className: "row__end", children: [endValue != null && _jsx("span", { className: "t-title", children: endValue }), endCaption != null && _jsx("span", { className: "t-caption t-muted", children: endCaption })] })), end, done && _jsx("span", { className: "row__done", children: _jsx(Icon, { name: "check", size: "sm" }) }));
18
+ const row = createElement(el, { href, className: cx('row', lg && 'row--lg', done && 'row--done', className), 'aria-disabled': done || undefined, ...(more === undefined ? rest : {}) }, children, done && _jsx("span", { className: "row__done", children: _jsx(Icon, { name: "check", size: "sm" }) }));
17
19
  if (more === undefined)
18
20
  return row;
19
21
  return (_jsxs("details", { className: "row-expand", ...rest, ...disclosure, children: [row, _jsx("div", { className: "row-expand__more", children: more })] }));
@@ -1,8 +1,11 @@
1
1
  import type { ComponentProps, ReactNode, Ref } from 'react';
2
2
  export interface MapAreaProps extends ComponentProps<'div'> {
3
3
  /** IN FLOW instead of behind a stage: the map is a box in a card or a sheet's body, the ring and the hint in its own box.
4
- * The only thing in a SheetBody, it fills the body: the sheet's head stays on top, its foot under it */
4
+ * The only thing in a sheet's Body, it fills the body: the sheet's head stays on top, its foot under it */
5
5
  inline?: boolean;
6
+ /** with `inline`: EDGE TO EDGE of what holds it, the sheet's or the card's inset given back, no corner — the map under a step's
7
+ * controls (Lh 2026-10-19, the ask sheet's จุดนัด step). In a `SheetStep` it reaches the sheet's edges; ignored without `inline` */
8
+ bleed?: boolean;
6
9
  /** the words over the map, under the bar's line */
7
10
  hint?: ReactNode;
8
11
  /** the tile provider's credit, bottom right (OSM requires it) — a caption, never the engine's own control */
@@ -22,11 +25,12 @@ export interface MapAreaProps extends ComponentProps<'div'> {
22
25
  /**
23
26
  * MapArea — the box a map engine draws in: the first child is the engine's mount and fills it; before tiles arrive (or
24
27
  * offline) the box shows the wireframe street pattern, never a hole. Two places: behind a stage sheet (the default), or
25
- * `inline`, in flow inside a card or a sheet's body — as a SheetBody's only child it fills the body, so the open sheet
28
+ * `inline`, in flow inside a card or a sheet's body — as a sheet's Body's only child it fills the body, so the open sheet
26
29
  * becomes the map while its head stays. The ring marks the rough area: behind a stage it is centred in the band a half
27
30
  * sheet leaves open, inline at the centre of its own box. Dragging the map underneath is that choice, never a pin to place;
28
31
  * `ringRef` is how the engine learns where that is. Places to pick go after the mount, as `MapPick`s.
29
32
  * Variation `mark="pin"`: for a meeting place the centre is an exact point, so a clear pin stands there instead of the ring,
30
33
  * its tip on the point the ring's centre would be (the engine reads the pin's bottom-centre). The ring stays for a rough area.
34
+ * Variation `bleed` (with `inline`): the map edge to edge of what holds it, no corner, for the map under a step's controls.
31
35
  */
32
- export declare function MapArea({ inline, hint, credit, mark, ringLabel, ringRef, className, children, ...rest }: MapAreaProps): import("react").JSX.Element;
36
+ export declare function MapArea({ inline, bleed, hint, credit, mark, ringLabel, ringRef, className, children, ...rest }: MapAreaProps): import("react").JSX.Element;
@@ -4,15 +4,16 @@ import { Icon } from '../type/Icon.js';
4
4
  /**
5
5
  * MapArea — the box a map engine draws in: the first child is the engine's mount and fills it; before tiles arrive (or
6
6
  * offline) the box shows the wireframe street pattern, never a hole. Two places: behind a stage sheet (the default), or
7
- * `inline`, in flow inside a card or a sheet's body — as a SheetBody's only child it fills the body, so the open sheet
7
+ * `inline`, in flow inside a card or a sheet's body — as a sheet's Body's only child it fills the body, so the open sheet
8
8
  * becomes the map while its head stays. The ring marks the rough area: behind a stage it is centred in the band a half
9
9
  * sheet leaves open, inline at the centre of its own box. Dragging the map underneath is that choice, never a pin to place;
10
10
  * `ringRef` is how the engine learns where that is. Places to pick go after the mount, as `MapPick`s.
11
11
  * Variation `mark="pin"`: for a meeting place the centre is an exact point, so a clear pin stands there instead of the ring,
12
12
  * its tip on the point the ring's centre would be (the engine reads the pin's bottom-centre). The ring stays for a rough area.
13
+ * Variation `bleed` (with `inline`): the map edge to edge of what holds it, no corner, for the map under a step's controls.
13
14
  */
14
- export function MapArea({ inline, hint, credit, mark = 'ring', ringLabel = mark === 'pin' ? 'จุดนัด' : 'ย่านของคุณโดยประมาณ', ringRef, className, children, ...rest }) {
15
- return (_jsxs("div", { className: cx('map', inline && 'map--inline', className), ...rest, children: [children, mark === 'pin'
15
+ export function MapArea({ inline, bleed, hint, credit, mark = 'ring', ringLabel = mark === 'pin' ? 'จุดนัด' : 'ย่านของคุณโดยประมาณ', ringRef, className, children, ...rest }) {
16
+ return (_jsxs("div", { className: cx('map', inline && 'map--inline', inline && bleed && 'map--bleed', className), ...rest, children: [children, mark === 'pin'
16
17
  ? _jsx("span", { ref: ringRef, className: "map__pin", role: "img", "aria-label": ringLabel, children: _jsx(Icon, { name: "map-pin" }) })
17
18
  : _jsx("span", { ref: ringRef, className: "map__ring", role: "img", "aria-label": ringLabel }), hint != null && _jsx("span", { className: "map__hint", children: hint }), credit != null && _jsx("span", { className: "map__credit", children: credit })] }));
18
19
  }
@@ -13,9 +13,7 @@ export interface TileProps extends Omit<ComponentProps<'div'>, 'onChange'> {
13
13
  icon?: IconName;
14
14
  /** the label at the bottom */
15
15
  label?: ReactNode;
16
- /** a big value (stat tiles) */
17
- value?: ReactNode;
18
- /** a caption under the label or value */
16
+ /** a caption under the label */
19
17
  sub?: ReactNode;
20
18
  /** a pick tile: a checkbox (or radio) inside, filled when checked */
21
19
  pick?: boolean;
@@ -31,8 +29,6 @@ export interface TileProps extends Omit<ComponentProps<'div'>, 'onChange'> {
31
29
  onChange?: React.ChangeEventHandler<HTMLInputElement>;
32
30
  /** filled accent (a static "on" tile) */
33
31
  on?: boolean;
34
- /** icon beside a value + caption, shorter */
35
- stat?: boolean;
36
32
  /** dashed, centred: an upload slot */
37
33
  upload?: boolean;
38
34
  /** spans the whole grid */
@@ -42,8 +38,8 @@ export interface TileProps extends Omit<ComponentProps<'div'>, 'onChange'> {
42
38
  /** div (default), button for a tap tile — pick tiles render as a label */
43
39
  as?: 'div' | 'button';
44
40
  }
45
- /** Tile — a big square: icon top, label bottom; `pick` toggles, `stat` shows a value, `upload` is the dashed slot. A `pick` or `upload` tile is filled in: On a sheet, never in a Card: the sheet is the paper the member fills in (Lh 2026-10-18). */
46
- export declare function Tile({ icon, label, value, sub, pick, type, name, checked, defaultChecked, onChange, on, stat, upload, wide, tone, as, className, children, ...rest }: TileProps): import("react").DetailedReactHTMLElement<{
41
+ /** Tile — a big square: icon top, label bottom; `pick` toggles, `upload` is the dashed slot. A `pick` or `upload` tile is filled in: On a sheet, never in a Card: the sheet is the paper the member fills in (Lh 2026-10-18). A number with its icon and label in a cell is a `KvCell` (Lh 2026-10-20). */
42
+ export declare function Tile({ icon, label, sub, pick, type, name, checked, defaultChecked, onChange, on, upload, wide, tone, as, className, children, ...rest }: TileProps): import("react").DetailedReactHTMLElement<{
47
43
  translate?: "yes" | "no" | undefined | undefined;
48
44
  slot?: string | undefined | undefined;
49
45
  style?: import("react").CSSProperties | undefined;
@@ -6,11 +6,11 @@ import { Icon } from '../type/Icon.js';
6
6
  export function TileGrid({ cols3, bleed, className, ...rest }) {
7
7
  return _jsx("div", { className: cx('tile-grid', cols3 && 'tile-grid--3', bleed && 'bleed', className), ...rest });
8
8
  }
9
- /** Tile — a big square: icon top, label bottom; `pick` toggles, `stat` shows a value, `upload` is the dashed slot. A `pick` or `upload` tile is filled in: On a sheet, never in a Card: the sheet is the paper the member fills in (Lh 2026-10-18). */
10
- export function Tile({ icon, label, value, sub, pick, type = 'checkbox', name, checked, defaultChecked, onChange, on, stat, upload, wide, tone, as, className, children, ...rest }) {
9
+ /** Tile — a big square: icon top, label bottom; `pick` toggles, `upload` is the dashed slot. A `pick` or `upload` tile is filled in: On a sheet, never in a Card: the sheet is the paper the member fills in (Lh 2026-10-18). A number with its icon and label in a cell is a `KvCell` (Lh 2026-10-20). */
10
+ export function Tile({ icon, label, sub, pick, type = 'checkbox', name, checked, defaultChecked, onChange, on, upload, wide, tone, as, className, children, ...rest }) {
11
11
  const el = pick ? 'label' : as ?? 'div';
12
- const text = value != null || (label != null && sub != null)
13
- ? _jsxs("span", { className: "tile__text", children: [value != null && _jsx("span", { className: "tile__value", children: value }), label != null && _jsx("span", { className: "tile__label", children: label }), sub != null && _jsx("span", { className: "tile__sub", children: sub })] })
12
+ const text = label != null && sub != null
13
+ ? _jsxs("span", { className: "tile__text", children: [_jsx("span", { className: "tile__label", children: label }), _jsx("span", { className: "tile__sub", children: sub })] })
14
14
  : label != null && _jsx("span", { className: "tile__label", children: label });
15
- return createElement(el, { type: el === 'button' ? 'button' : undefined, className: cx('tile', pick && 'tile--pick', on && 'tile--on', stat && 'tile--stat', upload && 'tile--upload', wide && 'tile--wide', tone && `tile--${tone}`, className), ...rest }, pick && _jsx("input", { type: type, name: name, checked: checked, defaultChecked: defaultChecked, onChange: onChange }), icon && _jsx(Icon, { name: icon }), text, children);
15
+ return createElement(el, { type: el === 'button' ? 'button' : undefined, className: cx('tile', pick && 'tile--pick', on && 'tile--on', upload && 'tile--upload', wide && 'tile--wide', tone && `tile--${tone}`, className), ...rest }, pick && _jsx("input", { type: type, name: name, checked: checked, defaultChecked: defaultChecked, onChange: onChange }), icon && _jsx(Icon, { name: icon }), text, children);
16
16
  }
@@ -8,6 +8,6 @@ export interface SpinnerProps extends Omit<ComponentProps<'span'>, 'children'> {
8
8
  children?: ReactNode;
9
9
  }
10
10
  /** Spinner — "working on it": a ring that turns, alone or with a line of words beside it. For a wait of unknown length — a known
11
- * share is a `Progress`. `role="status"`: the words (or `label`) are read out when it appears. In a row's `end`, a card, a
11
+ * share is a `Progress`. `role="status"`: the words (or `label`) are read out when it appears. At a row's end, a card, a
12
12
  * sheet's body, an `EmptyState`'s `icon`; a whole SCREEN that is not known yet shows nothing instead (`Screen ready`). */
13
13
  export declare function Spinner({ size, label, className, children, ...rest }: SpinnerProps): import("react").JSX.Element;
@@ -2,7 +2,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { cx } from '../cx.js';
3
3
  import { Icon } from '../type/Icon.js';
4
4
  /** Spinner — "working on it": a ring that turns, alone or with a line of words beside it. For a wait of unknown length — a known
5
- * share is a `Progress`. `role="status"`: the words (or `label`) are read out when it appears. In a row's `end`, a card, a
5
+ * share is a `Progress`. `role="status"`: the words (or `label`) are read out when it appears. At a row's end, a card, a
6
6
  * sheet's body, an `EmptyState`'s `icon`; a whole SCREEN that is not known yet shows nothing instead (`Screen ready`). */
7
7
  export function Spinner({ size = 'md', label, className, children, ...rest }) {
8
8
  return (_jsxs("span", { className: cx('spinner', className), role: "status", "aria-label": children == null ? label : undefined, ...rest, children: [_jsx(Icon, { name: "loader-circle", size: size }), children] }));
@@ -0,0 +1,7 @@
1
+ import { type ComponentProps } from 'react';
2
+ export interface BodyProps extends ComponentProps<'div'> {
3
+ }
4
+ /** Body — the content row, under the `Head` and over the `Foot`: what the member reads or fills in. In a `Sheet` and a stack sheet it is the one thing that
5
+ * scrolls, and its scroll padding keeps a focused control above the cut; content never goes straight into the Sheet. In a `Modal` it is the light part the
6
+ * question is about, scrolling inside when tall; a Modal with no Body is the confirm. Knobs `--body-gap`, `--body-pad-top`. */
7
+ export declare function Body({ className, ...rest }: BodyProps): import("react").JSX.Element;
@@ -0,0 +1,11 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { useContext } from 'react';
3
+ import { cx } from '../cx.js';
4
+ import { StackPullCtx } from '../sheets/stackCard.js';
5
+ /** Body — the content row, under the `Head` and over the `Foot`: what the member reads or fills in. In a `Sheet` and a stack sheet it is the one thing that
6
+ * scrolls, and its scroll padding keeps a focused control above the cut; content never goes straight into the Sheet. In a `Modal` it is the light part the
7
+ * question is about, scrolling inside when tall; a Modal with no Body is the confirm. Knobs `--body-gap`, `--body-pad-top`. */
8
+ export function Body({ className, ...rest }) {
9
+ const pulling = useContext(StackPullCtx);
10
+ return _jsx("div", { className: cx('body', pulling && 'is-refreshing', className), ...rest });
11
+ }
@@ -0,0 +1,10 @@
1
+ import type { ComponentProps, ReactNode } from 'react';
2
+ export interface EndProps extends ComponentProps<'span'> {
3
+ /** the trailing value, right-aligned (title type) */
4
+ value?: ReactNode;
5
+ /** the muted caption under `value` */
6
+ caption?: ReactNode;
7
+ }
8
+ /** End — the value at the end of a line over its caption, right-aligned (+1.0 h over Tue); or its children.
9
+ * A control at the end (an IconBtn, a Chip, a Check) takes no End: it goes straight in the line after the Words. */
10
+ export declare function End({ value, caption, className, children, ...rest }: EndProps): import("react").JSX.Element;
@@ -0,0 +1,7 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { cx } from '../cx.js';
3
+ /** End — the value at the end of a line over its caption, right-aligned (+1.0 h over Tue); or its children.
4
+ * A control at the end (an IconBtn, a Chip, a Check) takes no End: it goes straight in the line after the Words. */
5
+ export function End({ value, caption, className, children, ...rest }) {
6
+ return (_jsxs("span", { className: cx('end', className), ...rest, children: [value != null && _jsx("span", { className: "t-title", children: value }), caption != null && _jsx("span", { className: "t-caption t-muted", children: caption }), children] }));
7
+ }
@@ -0,0 +1,17 @@
1
+ import type { ComponentProps, ReactNode } from 'react';
2
+ export interface FootProps extends ComponentProps<'div'> {
3
+ /** a SMALL LINK under the buttons, centred on its own short row (a `Link sm` — "details for developers") — the same as an
4
+ * ActionBar's ctaLink (Lh 2026-09-23). It never takes a share of the buttons' line */
5
+ link?: ReactNode;
6
+ /** a foot of a few ROUND buttons (`IconBtn`, a size `lg` / `xl` for the big ones): they keep their own size instead of sharing the line, and
7
+ * stand as ONE group in the middle of the foot, a fixed gap apart (wider than the Btns' — `--foot-gap` sets it). The Btn foot is
8
+ * unchanged: this is the other foot, for a few big round answers */
9
+ round?: boolean;
10
+ }
11
+ /** Foot — the last row of a card, a sheet or a stack sheet: what the member can do next, its buttons on one line. One takes the width, up to
12
+ * four share it evenly. Btns only in the row; a small link goes in `link`, under it. A `Btn form` keeps its own size here: as wide as its words,
13
+ * centred — for a foot whose one button should not fill the card. With `round` it is the foot of a few round `IconBtn`s (`size="xl"`, one
14
+ * `primary` with a word, the others with icons): one centred group, a fixed gap between them. In a `Sheet` it stands on the box's bottom edge
15
+ * at every state. A `Card` finds its `Foot` by type (a turning card draws it on both faces), so a consumer's own component that wraps a `Foot`
16
+ * is not found. */
17
+ export declare function Foot({ link, round, className, children, ...rest }: FootProps): import("react").JSX.Element;
@@ -0,0 +1,11 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { cx } from '../cx.js';
3
+ /** Foot — the last row of a card, a sheet or a stack sheet: what the member can do next, its buttons on one line. One takes the width, up to
4
+ * four share it evenly. Btns only in the row; a small link goes in `link`, under it. A `Btn form` keeps its own size here: as wide as its words,
5
+ * centred — for a foot whose one button should not fill the card. With `round` it is the foot of a few round `IconBtn`s (`size="xl"`, one
6
+ * `primary` with a word, the others with icons): one centred group, a fixed gap between them. In a `Sheet` it stands on the box's bottom edge
7
+ * at every state. A `Card` finds its `Foot` by type (a turning card draws it on both faces), so a consumer's own component that wraps a `Foot`
8
+ * is not found. */
9
+ export function Foot({ link, round, className, children, ...rest }) {
10
+ return (_jsxs("div", { className: cx('foot', link != null && 'foot--link', round && 'foot--round', className), ...rest, children: [children, link != null && _jsx("div", { className: "foot__link", children: link })] }));
11
+ }
@@ -0,0 +1,15 @@
1
+ import { type ComponentProps } from 'react';
2
+ export interface HeadProps extends ComponentProps<'div'> {
3
+ /** top-align a genuinely tall multi-line title with its control */
4
+ top?: boolean;
5
+ /** a stage sheet's head: the title and its meta centred between a round button either side (`lead` · `Words` · `trail`), the three columns kept when one is missing */
6
+ centre?: boolean;
7
+ /** the sheet measured this title as wider than its line, so the title runs sideways */
8
+ long?: boolean;
9
+ }
10
+ /**
11
+ * Head — the first row of a card, a sheet or a stack sheet: what this is, and the one thing beside it. Its children are, in order, an optional
12
+ * lead (a `Lead`, a `StackBack`, a `JourneyBadge`), the `Words` (the title takes the room, its meta under it), chips, and an end (an `IconBtn`, a `Chip`, an `End`).
13
+ * Never put buttons here that finish the card: they go in the foot. A `Card` finds its `Head` by type, so a consumer's own component that wraps a `Head` is not found.
14
+ */
15
+ export declare function Head({ top, centre, long, className, children, ...rest }: HeadProps): import("react").JSX.Element;
@@ -0,0 +1,34 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { Children, cloneElement, isValidElement, useContext } from 'react';
3
+ import { cx } from '../cx.js';
4
+ import { Text } from '../type/Text.js';
5
+ import { StackHeadCtx } from '../sheets/stackCard.js';
6
+ import { HeadCtx, onTitleKey } from './headCtx.js';
7
+ /** a title written by hand (a `Text` h1/h2/display, a bare h1-h3) is the stack's keyboard way in as a `Words` title is. The head renders the tab stop and, when
8
+ * the sheet measured it long, `.is-long`. Nothing sets either on the node afterwards */
9
+ function markTitle(node, tab, long) {
10
+ if (!isValidElement(node))
11
+ return node;
12
+ const el = node;
13
+ const level = el.type === Text ? el.props.level : null;
14
+ const intrinsic = typeof el.type === 'string' && /^h[1-3]$/.test(el.type);
15
+ if (level !== 'h1' && level !== 'h2' && level !== 'display' && !intrinsic)
16
+ return node;
17
+ return cloneElement(el, {
18
+ ...(long ? { className: cx(el.props.className, 'is-long') } : {}),
19
+ ...(tab != null ? {
20
+ tabIndex: tab,
21
+ onKeyDown: (e) => { el.props.onKeyDown?.(e); if (!e.defaultPrevented)
22
+ onTitleKey(e); },
23
+ } : {}),
24
+ });
25
+ }
26
+ /**
27
+ * Head — the first row of a card, a sheet or a stack sheet: what this is, and the one thing beside it. Its children are, in order, an optional
28
+ * lead (a `Lead`, a `StackBack`, a `JourneyBadge`), the `Words` (the title takes the room, its meta under it), chips, and an end (an `IconBtn`, a `Chip`, an `End`).
29
+ * Never put buttons here that finish the card: they go in the foot. A `Card` finds its `Head` by type, so a consumer's own component that wraps a `Head` is not found.
30
+ */
31
+ export function Head({ top, centre, long, className, children, ...rest }) {
32
+ const tab = useContext(StackHeadCtx);
33
+ return (_jsx("div", { className: cx('head', top && 'head--top', centre && 'head--centre', className), ...rest, children: _jsx(HeadCtx.Provider, { value: { tab, long: !!long }, children: tab == null && !long ? children : Children.map(children, (c) => markTitle(c, tab, !!long)) }) }));
34
+ }
@@ -0,0 +1,10 @@
1
+ import type { ComponentProps } from 'react';
2
+ import { type IconName } from '../type/Icon.js';
3
+ export interface LeadProps extends ComponentProps<'span'> {
4
+ /** an icon drawn in the box; without it, the box holds its children (a number, an initial) */
5
+ icon?: IconName;
6
+ }
7
+ /** Lead — the thing at the start of a line: the outlined box holding an `icon` or its children. In a `Row` it is the 48 box of a menu row;
8
+ * in a `Card row` (and a FeedCard's head) the 44 box in the card's own ink, a number or an initial in label type (`.card--row > .lead`, css/parts.css).
9
+ * A person's or a group's lead takes no box: an Avatar or an AvatarStack goes straight in the line as its first child. */
10
+ export declare function Lead({ icon, className, children, ...rest }: LeadProps): import("react").JSX.Element;
@@ -0,0 +1,9 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { cx } from '../cx.js';
3
+ import { Icon } from '../type/Icon.js';
4
+ /** Lead — the thing at the start of a line: the outlined box holding an `icon` or its children. In a `Row` it is the 48 box of a menu row;
5
+ * in a `Card row` (and a FeedCard's head) the 44 box in the card's own ink, a number or an initial in label type (`.card--row > .lead`, css/parts.css).
6
+ * A person's or a group's lead takes no box: an Avatar or an AvatarStack goes straight in the line as its first child. */
7
+ export function Lead({ icon, className, children, ...rest }) {
8
+ return _jsx("span", { className: cx('lead', className), ...rest, children: icon ? _jsx(Icon, { name: icon }) : children });
9
+ }
@@ -0,0 +1,13 @@
1
+ import { type ComponentProps, type ReactNode } from 'react';
2
+ export interface WordsProps extends Omit<ComponentProps<'span'>, 'title'> {
3
+ /** the title line */
4
+ title?: ReactNode;
5
+ /** the caption under it (may hold a Dot) */
6
+ meta?: ReactNode;
7
+ /** the title as a heading element in that type level; without it the title is a span in title type. In a `Head` the title is always a heading: `h2` unless this says otherwise (`h1` for the one-feature page) */
8
+ level?: 'h1' | 'h2' | 'display';
9
+ }
10
+ /** Words — the words of a line, filling the room between the lead and the end: a `title` over a `meta` caption;
11
+ * its children come under them, or instead of them (a Wave for a voice note). In a `Head` there is no box round them: the title and the meta
12
+ * stand in the head, and a `className`, an `id` or any other attribute goes on the title. */
13
+ export declare function Words({ title, meta, level, className, children, ...rest }: WordsProps): import("react").JSX.Element;
@@ -0,0 +1,20 @@
1
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useContext } from 'react';
3
+ import { cx } from '../cx.js';
4
+ import { HeadCtx, onTitleKey } from './headCtx.js';
5
+ /** Words — the words of a line, filling the room between the lead and the end: a `title` over a `meta` caption;
6
+ * its children come under them, or instead of them (a Wave for a voice note). In a `Head` there is no box round them: the title and the meta
7
+ * stand in the head, and a `className`, an `id` or any other attribute goes on the title. */
8
+ export function Words({ title, meta, level, className, children, ...rest }) {
9
+ const head = useContext(HeadCtx);
10
+ if (head) {
11
+ const as = level ?? 'h2';
12
+ const Title = as === 'h1' ? 'h1' : 'h2';
13
+ const { onKeyDown, ...own } = rest;
14
+ const attrs = head.tab != null ? { tabIndex: head.tab, onKeyDown: (e) => { onKeyDown?.(e); if (!e.defaultPrevented)
15
+ onTitleKey(e); } } : { onKeyDown };
16
+ return (_jsxs(_Fragment, { children: [title != null && _jsx(Title, { className: cx('words__title', `t-${as}`, head.long && 'is-long', className), ...own, ...attrs, children: title }), meta != null && _jsx("span", { className: "words__meta t-caption t-muted", children: meta }), children] }));
17
+ }
18
+ const Title = level === 'h1' ? 'h1' : level ? 'h2' : 'span';
19
+ return (_jsxs("span", { className: cx('words', className), ...rest, children: [title != null && _jsx(Title, { className: cx('words__title', level && `t-${level}`), children: title }), meta != null && _jsx("span", { className: "words__meta", children: meta }), children] }));
20
+ }
@@ -0,0 +1,7 @@
1
+ import { type KeyboardEvent } from 'react';
2
+ /** what a `Words` in a head reads: the stack's tab stop and whether the title runs. `null` outside a head */
3
+ export declare const HeadCtx: import("react").Context<{
4
+ tab: 0 | -1 | null;
5
+ long: boolean;
6
+ } | null>;
7
+ export declare const onTitleKey: (e: KeyboardEvent<HTMLElement>) => void;
@@ -0,0 +1,9 @@
1
+ import { createContext } from 'react';
2
+ /** what a `Words` in a head reads: the stack's tab stop and whether the title runs. `null` outside a head */
3
+ export const HeadCtx = createContext(null);
4
+ export const onTitleKey = (e) => {
5
+ if (e.key !== 'Enter' && e.key !== ' ')
6
+ return;
7
+ e.preventDefault();
8
+ e.currentTarget.click();
9
+ };
@@ -11,5 +11,5 @@ export interface EmptyStateProps extends Omit<ComponentProps<'div'>, 'title'> {
11
11
  }
12
12
  /** EmptyState — centred: a large icon, a heading, a line of body text, an optional CTA. A component in the content: alone in a `Step` it stands at the content's human centre, 40% down (Lh 2026-09-23 — no longer a `Centre` over the screen)
13
13
  * or inside a card's section ("nothing here yet" under a heading) — the ink follows the card it sits in; full ink throughout, never muted (Lh 2026-09-16).
14
- * Knob `--empty-state-at`, a number 0–1: the share of the holder's free room that stands above it. In a `Step` the default is 0.4 (two parts above, three below); alone in a `SheetBody` it is 0 (at the top). A theme's `:root { --empty-state-at: 0.2 }` lifts it everywhere, or set it on a `Screen`, `Step` or `Sheet` for the ones inside. */
14
+ * Knob `--empty-state-at`, a number 0–1: the share of the holder's free room that stands above it. In a `Step` the default is 0.4 (two parts above, three below); alone in a Sheet's `Body` it is 0 (at the top). A theme's `:root { --empty-state-at: 0.2 }` lifts it everywhere, or set it on a `Screen`, `Step` or `Sheet` for the ones inside. */
15
15
  export declare function EmptyState({ icon, title, action, level, className, children, ...rest }: EmptyStateProps): import("react").JSX.Element;
@@ -2,7 +2,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { cx } from '../cx.js';
3
3
  /** EmptyState — centred: a large icon, a heading, a line of body text, an optional CTA. A component in the content: alone in a `Step` it stands at the content's human centre, 40% down (Lh 2026-09-23 — no longer a `Centre` over the screen)
4
4
  * or inside a card's section ("nothing here yet" under a heading) — the ink follows the card it sits in; full ink throughout, never muted (Lh 2026-09-16).
5
- * Knob `--empty-state-at`, a number 0–1: the share of the holder's free room that stands above it. In a `Step` the default is 0.4 (two parts above, three below); alone in a `SheetBody` it is 0 (at the top). A theme's `:root { --empty-state-at: 0.2 }` lifts it everywhere, or set it on a `Screen`, `Step` or `Sheet` for the ones inside. */
5
+ * Knob `--empty-state-at`, a number 0–1: the share of the holder's free room that stands above it. In a `Step` the default is 0.4 (two parts above, three below); alone in a Sheet's `Body` it is 0 (at the top). A theme's `:root { --empty-state-at: 0.2 }` lifts it everywhere, or set it on a `Screen`, `Step` or `Sheet` for the ones inside. */
6
6
  export function EmptyState({ icon, title, action, level = 'h2', className, children, ...rest }) {
7
7
  const H = level;
8
8
  return (_jsxs("div", { className: cx('empty-state', className), ...rest, children: [icon, title != null && _jsx(H, { className: "t-h2", children: title }), typeof children === 'string' ? _jsx("p", { className: "t-body", children: children }) : children, action != null && _jsx("div", { className: "empty-state__action", children: action })] }));
@@ -16,7 +16,7 @@ export function Screen({ fill, palette, as = 'div', top, bottom, ready = true, c
16
16
  const el = own.current;
17
17
  if (!el || !el.querySelector(':scope > .action-bar') || !el.querySelector(':scope > .step > .sheet'))
18
18
  return;
19
- warnOnce('screen-sheet-bar', 'cardds Screen: a bottom bar on a screen with a Sheet — the sheet finishes on its own foot (CardFoot); drop the bar.');
19
+ warnOnce('screen-sheet-bar', 'cardds Screen: a bottom bar on a screen with a Sheet — the sheet finishes on its own foot (Foot); drop the bar.');
20
20
  });
21
21
  /* the BOTTOM layer's height, published on the screen as --bottom-h (Lh 2026-09-23): the walk's pages end with that much room
22
22
  more, so every last word scrolls up clear of the layer. Measured, because the layer is as tall as its rows and the text size. */
@@ -10,9 +10,9 @@ export interface ModalProps extends ComponentProps<'article'> {
10
10
  onDismiss?: (reason: 'scrim' | 'escape') => void;
11
11
  }
12
12
  /**
13
- * Modal — the ask: ONE card at the centre of a `SheetStage dim`, two parts — a `ModalBody` (the thing being asked about, light, scrolls
13
+ * Modal — the ask: ONE card at the centre of a `SheetStage dim`, two parts — a `Body` (the thing being asked about, light, scrolls
14
14
  * when tall) and a `ModalAsk` (the dark end that asks). No border. Two beats on appearing: drops in from above, then the ModalAsk comes
15
- * out below while the ModalBody moves up.
15
+ * out below while the Body moves up.
16
16
  * A COMPONENT, not a wiring: it does not know its buttons. They are plain `IconBtn`s / `Btn`s with the app's `onClick` — the app
17
17
  * records the answer and sets `open={false}`; the modal's one behaviour is how it leaves. `onDismiss` asks to leave unanswered (a tap on
18
18
  * the scrim, Esc). When it has arrived, cardds puts the cursor in the field marked `data-autofocus`, else the first field, else the question — write no focus code.
@@ -21,10 +21,6 @@ export interface ModalProps extends ComponentProps<'article'> {
21
21
  * effect's first run sets `shown` to false, its cleanup drops the wait, and the second run returns early.
22
22
  */
23
23
  export declare function Modal({ open, onClosed, onDismiss, className, ...rest }: ModalProps): import("react").JSX.Element;
24
- export interface ModalBodyProps extends ComponentProps<'div'> {
25
- }
26
- /** ModalBody — the light part of a `Modal`: the case, request or item the question is about (a CardHead, rows, a timeline); scrolls inside when tall. */
27
- export declare function ModalBody({ className, ...rest }: ModalBodyProps): import("react").JSX.Element;
28
24
  export interface ModalAskProps extends Omit<ComponentProps<'div'>, 'title'> {
29
25
  /** the question */
30
26
  title: ReactNode;
@@ -34,7 +30,7 @@ export interface ModalAskProps extends Omit<ComponentProps<'div'>, 'title'> {
34
30
  light?: boolean;
35
31
  }
36
32
  /** ModalAsk — the dark end of a `Modal` (`light` = paper and dark words): the question, its fine print, and the answers: a `BtnTrio` of round icon buttons, or a `BtnRow` of word `Btn`s (it fills the card, each button's words on one line; a `Btn quiet` there is edged in the ask's own ink) — plain buttons
37
- * with the app's `onClick`; the ModalAsk does nothing with them. A `Modal` holding ONLY a ModalAsk (no `ModalBody`) is the CONFIRM: a question with nothing
33
+ * with the app's `onClick`; the ModalAsk does nothing with them. A `Modal` holding ONLY a ModalAsk (no `Body`) is the CONFIRM: a question with nothing
38
34
  * to show above it — the ModalAsk is the whole card and comes in with it in one beat. */
39
35
  export declare function ModalAsk({ title, caption, light, className, children, ...rest }: ModalAskProps): import("react").JSX.Element;
40
36
  export interface BtnTrioProps extends ComponentProps<'div'> {
@@ -3,9 +3,9 @@ import { Children, Fragment, useEffect, useRef } from 'react';
3
3
  import { focusWhenActive, useLatest } from '../state.js';
4
4
  import { cx } from '../cx.js';
5
5
  /**
6
- * Modal — the ask: ONE card at the centre of a `SheetStage dim`, two parts — a `ModalBody` (the thing being asked about, light, scrolls
6
+ * Modal — the ask: ONE card at the centre of a `SheetStage dim`, two parts — a `Body` (the thing being asked about, light, scrolls
7
7
  * when tall) and a `ModalAsk` (the dark end that asks). No border. Two beats on appearing: drops in from above, then the ModalAsk comes
8
- * out below while the ModalBody moves up.
8
+ * out below while the Body moves up.
9
9
  * A COMPONENT, not a wiring: it does not know its buttons. They are plain `IconBtn`s / `Btn`s with the app's `onClick` — the app
10
10
  * records the answer and sets `open={false}`; the modal's one behaviour is how it leaves. `onDismiss` asks to leave unanswered (a tap on
11
11
  * the scrim, Esc). When it has arrived, cardds puts the cursor in the field marked `data-autofocus`, else the first field, else the question — write no focus code.
@@ -57,12 +57,8 @@ export function Modal({ open = true, onClosed, onDismiss, className, ...rest })
57
57
  }, [has, open]);
58
58
  return _jsx("article", { ref: ref, className: cx('card modal', className), ...rest });
59
59
  }
60
- /** ModalBody — the light part of a `Modal`: the case, request or item the question is about (a CardHead, rows, a timeline); scrolls inside when tall. */
61
- export function ModalBody({ className, ...rest }) {
62
- return _jsx("div", { className: cx('modal__lift', className), ...rest });
63
- }
64
60
  /** ModalAsk — the dark end of a `Modal` (`light` = paper and dark words): the question, its fine print, and the answers: a `BtnTrio` of round icon buttons, or a `BtnRow` of word `Btn`s (it fills the card, each button's words on one line; a `Btn quiet` there is edged in the ask's own ink) — plain buttons
65
- * with the app's `onClick`; the ModalAsk does nothing with them. A `Modal` holding ONLY a ModalAsk (no `ModalBody`) is the CONFIRM: a question with nothing
61
+ * with the app's `onClick`; the ModalAsk does nothing with them. A `Modal` holding ONLY a ModalAsk (no `Body`) is the CONFIRM: a question with nothing
66
62
  * to show above it — the ModalAsk is the whole card and comes in with it in one beat. */
67
63
  export function ModalAsk({ title, caption, light, className, children, ...rest }) {
68
64
  return (_jsxs("div", { className: cx('modal__drawer', light && 'modal__drawer--light', className), ...rest, children: [_jsx("h2", { className: "t-h2", tabIndex: -1, children: title }), caption != null && _jsx("p", { className: "t-caption", children: caption }), children] }));