@usableapp/cardds 0.7.74 → 0.7.75

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 (45) hide show
  1. package/CHANGELOG.md +21 -0
  2. package/README.md +9 -9
  3. package/css/layover.css +1 -1
  4. package/css/lists.css +1 -1
  5. package/dist/actions/{ActionNote.d.ts → BarNote.d.ts} +3 -3
  6. package/dist/actions/{ActionNote.js → BarNote.js} +2 -2
  7. package/dist/actions/BtnGroup.d.ts +11 -0
  8. package/dist/actions/{Segment.js → BtnGroup.js} +4 -4
  9. package/dist/actions/{WaitLink.d.ts → Countdown.d.ts} +3 -3
  10. package/dist/actions/{WaitLink.js → Countdown.js} +2 -2
  11. package/dist/actions/Link.d.ts +2 -2
  12. package/dist/cardds.css +2 -2
  13. package/dist/cards/Card.d.ts +1 -1
  14. package/dist/cards/CardParts.d.ts +1 -1
  15. package/dist/cards/CardParts.js +1 -1
  16. package/dist/catalog.json +1 -1
  17. package/dist/index.d.ts +7 -7
  18. package/dist/index.js +7 -7
  19. package/dist/journey/{TileBadge.d.ts → JourneyBadge.d.ts} +3 -3
  20. package/dist/journey/{TileBadge.js → JourneyBadge.js} +2 -2
  21. package/dist/numbers/NumberBand.d.ts +18 -0
  22. package/dist/numbers/{Band.js → NumberBand.js} +4 -4
  23. package/dist/scaffold/ActionBar.d.ts +2 -2
  24. package/dist/scaffold/ActionBar.js +1 -1
  25. package/dist/scaffold/Centre.d.ts +3 -3
  26. package/dist/scaffold/Centre.js +3 -3
  27. package/dist/scaffold/Page.d.ts +1 -1
  28. package/dist/scaffold/Page.js +1 -1
  29. package/dist/scaffold/Pager.d.ts +11 -0
  30. package/dist/scaffold/Pager.js +6 -0
  31. package/dist/scaffold/Panes.d.ts +2 -2
  32. package/dist/scaffold/Panes.js +3 -3
  33. package/dist/scaffold/Screen.d.ts +1 -1
  34. package/dist/scaffold/Screen.js +1 -1
  35. package/dist/sheets/{Drawer.d.ts → Modal.d.ts} +12 -12
  36. package/dist/sheets/{Drawer.js → Modal.js} +9 -9
  37. package/dist/templates/Walkthrough.d.ts +4 -4
  38. package/dist/templates/Walkthrough.js +2 -2
  39. package/lib/mcp.mjs +1 -1
  40. package/lint/rules.mjs +5 -4
  41. package/package.json +1 -1
  42. package/dist/actions/Segment.d.ts +0 -11
  43. package/dist/numbers/Band.d.ts +0 -18
  44. package/dist/scaffold/PagerAt.d.ts +0 -11
  45. package/dist/scaffold/PagerAt.js +0 -6
package/dist/index.d.ts CHANGED
@@ -11,7 +11,7 @@ export * from './scaffold/Screen.js';
11
11
  export * from './scaffold/AppBar.js';
12
12
  export * from './scaffold/TopBar.js';
13
13
  export * from './scaffold/ActionBar.js';
14
- export * from './scaffold/PagerAt.js';
14
+ export * from './scaffold/Pager.js';
15
15
  export * from './scaffold/Panes.js';
16
16
  export * from './scaffold/FloatBar.js';
17
17
  export * from './scaffold/FilterRow.js';
@@ -34,18 +34,18 @@ export * from './sheets/SheetBody.js';
34
34
  export * from './sheets/SheetStage.js';
35
35
  export * from './sheets/Step.js';
36
36
  export * from './sheets/SheetSteps.js';
37
- export * from './sheets/Drawer.js';
37
+ export * from './sheets/Modal.js';
38
38
  export * from './actions/Btn.js';
39
39
  export * from './actions/IconBtn.js';
40
40
  export * from './actions/IconBtnGroup.js';
41
41
  export * from './actions/Chip.js';
42
42
  export * from './actions/Dot.js';
43
43
  export * from './actions/Dropdown.js';
44
- export * from './actions/Segment.js';
44
+ export * from './actions/BtnGroup.js';
45
45
  export * from './actions/Fab.js';
46
46
  export * from './actions/BtnRow.js';
47
47
  export * from './actions/Link.js';
48
- export * from './actions/WaitLink.js';
48
+ export * from './actions/Countdown.js';
49
49
  export * from './forms/Field.js';
50
50
  export * from './forms/FileBtn.js';
51
51
  export * from './forms/AddRow.js';
@@ -60,7 +60,7 @@ export * from './numbers/Ring.js';
60
60
  export * from './numbers/Progress.js';
61
61
  export * from './numbers/Spinner.js';
62
62
  export * from './numbers/Track.js';
63
- export * from './numbers/Band.js';
63
+ export * from './numbers/NumberBand.js';
64
64
  export * from './numbers/DotGrid.js';
65
65
  export * from './numbers/Picker.js';
66
66
  export * from './numbers/Badge.js';
@@ -95,11 +95,11 @@ export * from './layover/Banner.js';
95
95
  export * from './layover/Callout.js';
96
96
  export * from './layover/Snackbar.js';
97
97
  export * from './journey/Route.js';
98
- export * from './journey/TileBadge.js';
98
+ export * from './journey/JourneyBadge.js';
99
99
  export * from './journey/NoteRow.js';
100
100
  export * from './templates/Walkthrough.js';
101
101
  export * from './templates/Home.js';
102
102
  export * from './sheets/StackSheet.js';
103
103
  export * from './templates/CallSheet.js';
104
104
  export * from './templates/Splash.js';
105
- export * from './actions/ActionNote.js';
105
+ export * from './actions/BarNote.js';
package/dist/index.js CHANGED
@@ -15,7 +15,7 @@ export * from './scaffold/Screen.js';
15
15
  export * from './scaffold/AppBar.js';
16
16
  export * from './scaffold/TopBar.js';
17
17
  export * from './scaffold/ActionBar.js';
18
- export * from './scaffold/PagerAt.js';
18
+ export * from './scaffold/Pager.js';
19
19
  export * from './scaffold/Panes.js';
20
20
  export * from './scaffold/FloatBar.js';
21
21
  export * from './scaffold/FilterRow.js';
@@ -38,18 +38,18 @@ export * from './sheets/SheetBody.js';
38
38
  export * from './sheets/SheetStage.js';
39
39
  export * from './sheets/Step.js';
40
40
  export * from './sheets/SheetSteps.js';
41
- export * from './sheets/Drawer.js';
41
+ export * from './sheets/Modal.js';
42
42
  export * from './actions/Btn.js';
43
43
  export * from './actions/IconBtn.js';
44
44
  export * from './actions/IconBtnGroup.js';
45
45
  export * from './actions/Chip.js';
46
46
  export * from './actions/Dot.js';
47
47
  export * from './actions/Dropdown.js';
48
- export * from './actions/Segment.js';
48
+ export * from './actions/BtnGroup.js';
49
49
  export * from './actions/Fab.js';
50
50
  export * from './actions/BtnRow.js';
51
51
  export * from './actions/Link.js';
52
- export * from './actions/WaitLink.js';
52
+ export * from './actions/Countdown.js';
53
53
  export * from './forms/Field.js';
54
54
  export * from './forms/FileBtn.js';
55
55
  export * from './forms/AddRow.js';
@@ -64,7 +64,7 @@ export * from './numbers/Ring.js';
64
64
  export * from './numbers/Progress.js';
65
65
  export * from './numbers/Spinner.js';
66
66
  export * from './numbers/Track.js';
67
- export * from './numbers/Band.js';
67
+ export * from './numbers/NumberBand.js';
68
68
  export * from './numbers/DotGrid.js';
69
69
  export * from './numbers/Picker.js';
70
70
  export * from './numbers/Badge.js';
@@ -99,11 +99,11 @@ export * from './layover/Banner.js';
99
99
  export * from './layover/Callout.js';
100
100
  export * from './layover/Snackbar.js';
101
101
  export * from './journey/Route.js';
102
- export * from './journey/TileBadge.js';
102
+ export * from './journey/JourneyBadge.js';
103
103
  export * from './journey/NoteRow.js';
104
104
  export * from './templates/Walkthrough.js';
105
105
  export * from './templates/Home.js';
106
106
  export * from './sheets/StackSheet.js';
107
107
  export * from './templates/CallSheet.js';
108
108
  export * from './templates/Splash.js';
109
- export * from './actions/ActionNote.js';
109
+ export * from './actions/BarNote.js';
@@ -1,5 +1,5 @@
1
1
  import type { ComponentProps, ReactNode } from 'react';
2
- export interface TileBadgeProps extends ComponentProps<'span'> {
2
+ export interface JourneyBadgeProps extends ComponentProps<'span'> {
3
3
  /** the small label above ("Gate") */
4
4
  label: ReactNode;
5
5
  /** the big value ("21") */
@@ -11,5 +11,5 @@ export interface TileBadgeProps extends ComponentProps<'span'> {
11
11
  /** pinned to a CardMedia's corner (rating, price) */
12
12
  corner?: boolean;
13
13
  }
14
- /** TileBadge — a tiny label over a big value: gate, seat, date, rating. */
15
- export declare function TileBadge({ label, value, sub, plain, corner, className, ...rest }: TileBadgeProps): import("react").JSX.Element;
14
+ /** JourneyBadge — a tiny label over a big value: gate, seat, date, rating. */
15
+ export declare function JourneyBadge({ label, value, sub, plain, corner, className, ...rest }: JourneyBadgeProps): import("react").JSX.Element;
@@ -1,6 +1,6 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { cx } from '../cx.js';
3
- /** TileBadge — a tiny label over a big value: gate, seat, date, rating. */
4
- export function TileBadge({ label, value, sub, plain, corner, className, ...rest }) {
3
+ /** JourneyBadge — a tiny label over a big value: gate, seat, date, rating. */
4
+ export function JourneyBadge({ label, value, sub, plain, corner, className, ...rest }) {
5
5
  return (_jsxs("span", { className: cx('tile-badge', plain && 'tile-badge--plain', corner && 'tile-badge--corner', className), ...rest, children: [_jsx("span", { className: "tile-badge__label", children: label }), _jsx("span", { className: "tile-badge__value", children: value }), sub != null && _jsx("span", { className: "tile-badge__label", children: sub })] }));
6
6
  }
@@ -0,0 +1,18 @@
1
+ import type { ComponentProps, ReactNode } from 'react';
2
+ import type { Tone } from '../cards/Card.js';
3
+ export interface NumberBandProps extends ComponentProps<'article'> {
4
+ /** the two-line label (use a `<br />` to break it) */
5
+ label: ReactNode;
6
+ /** the giant number; wrap a unit in `<small>` */
7
+ num: ReactNode;
8
+ /** the footnote under the label */
9
+ foot?: ReactNode;
10
+ /** the paper — see `Tone` */
11
+ tone?: Tone;
12
+ }
13
+ /** NumberBand — a data card: 2-line label · giant number · footnote. Stack several in a NumberBandStack for one boundary with hairlines. */
14
+ export declare function NumberBand({ label, num, foot, tone, className, ...rest }: NumberBandProps): import("react").JSX.Element;
15
+ export interface NumberBandStackProps extends ComponentProps<'div'> {
16
+ }
17
+ /** NumberBandStack — several NumberBands as ONE card boundary, a hairline between each. */
18
+ export declare function NumberBandStack({ className, ...rest }: NumberBandStackProps): import("react").JSX.Element;
@@ -1,10 +1,10 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { cx } from '../cx.js';
3
- /** Band — a data card: 2-line label · giant number · footnote. Stack several in a BandStack for one boundary with hairlines. */
4
- export function Band({ label, num, foot, tone, className, ...rest }) {
3
+ /** NumberBand — a data card: 2-line label · giant number · footnote. Stack several in a NumberBandStack for one boundary with hairlines. */
4
+ export function NumberBand({ label, num, foot, tone, className, ...rest }) {
5
5
  return (_jsxs("article", { className: cx('card card--band', tone && tone !== 1 && `card--${tone}`, className), ...rest, children: [_jsx("span", { className: "band__label", children: label }), _jsx("span", { className: "band__num", children: num }), foot != null && _jsx("span", { className: "band__foot", children: foot })] }));
6
6
  }
7
- /** BandStack — several Bands as ONE card boundary, a hairline between each. */
8
- export function BandStack({ className, ...rest }) {
7
+ /** NumberBandStack — several NumberBands as ONE card boundary, a hairline between each. */
8
+ export function NumberBandStack({ className, ...rest }) {
9
9
  return _jsx("div", { className: cx('band-stack', className), ...rest });
10
10
  }
@@ -9,13 +9,13 @@ export interface ActionBarProps extends ComponentProps<'div'> {
9
9
  * ActionBar — the screen's ONE move: the Screen's `bottom` (`<Screen fill bottom={<ActionBar…>}>`), the last part of its flex
10
10
  * column, as tall as its rows. Never after `</Screen>`, never positioned by hand.
11
11
  * Plain: one wide primary Btn (the step that commits).
12
- * `pager`: Back + Next as peers — buttons only. The PagerAt dots may be the bar's first row (a Walkthrough).
12
+ * `pager`: the pager bar — Back + Next as peers, buttons only. The `Pager` (the dots that say where the walk is) is a different thing: it may be the bar's first row (a Walkthrough).
13
13
  * `tiers`: an ActionBarTier with a control (a Slider) above the button tier.
14
14
  * In a `Screen fill` it is a layer over the content's foot; on a scrolling Screen a sticky last row.
15
15
  */
16
16
  export declare function ActionBar({ variant, inline, className, style, ...rest }: ActionBarProps): import("react").JSX.Element;
17
17
  export interface ActionBarTierProps extends ComponentProps<'div'> {
18
- /** the tier that holds the round pager (two IconBtns at the edges) */
18
+ /** the tier that holds the round pager bar (two IconBtns at the edges) */
19
19
  pager?: boolean;
20
20
  }
21
21
  /** ActionBarTier — one row inside an `ActionBar variant="tiers"`: a control row, or the pager row. */
@@ -4,7 +4,7 @@ import { cx } from '../cx.js';
4
4
  * ActionBar — the screen's ONE move: the Screen's `bottom` (`<Screen fill bottom={<ActionBar…>}>`), the last part of its flex
5
5
  * column, as tall as its rows. Never after `</Screen>`, never positioned by hand.
6
6
  * Plain: one wide primary Btn (the step that commits).
7
- * `pager`: Back + Next as peers — buttons only. The PagerAt dots may be the bar's first row (a Walkthrough).
7
+ * `pager`: the pager bar — Back + Next as peers, buttons only. The `Pager` (the dots that say where the walk is) is a different thing: it may be the bar's first row (a Walkthrough).
8
8
  * `tiers`: an ActionBarTier with a control (a Slider) above the button tier.
9
9
  * In a `Screen fill` it is a layer over the content's foot; on a scrolling Screen a sticky last row.
10
10
  */
@@ -3,7 +3,7 @@ export interface CentreProps extends ComponentProps<'div'> {
3
3
  }
4
4
  /**
5
5
  * Centre — the centred group: ONE thing to read at the human centre of the screen, when that thing is not a lone
6
- * card: an EmptyState; a Card with its quiet second Btn and the PagerAt under it.
6
+ * card: an EmptyState; a Card with its quiet second Btn and the Pager under it.
7
7
  * On a plain `Screen` (the usual case, Lh 2026-09-12) it is in flow: the column is one phone tall, the group takes
8
8
  * the room after the TopBar and centres a short card; a long card makes the page longer and scrolls up under the
9
9
  * bar — nothing is capped, nothing shrinks. On a `Screen fill` it floats at 40% down (`--screen-centre`), capped
@@ -17,8 +17,8 @@ export interface PagesProps extends ComponentProps<'div'> {
17
17
  * Pages — the pages of a walk, all one height: every page is rendered, stacked in one cell as tall as the
18
18
  * tallest at any text size. Give the current one `aria-current="step"` and every other one `inert`
19
19
  * (hidden in place, out of the tab order). Turn with slideTo().
20
- * Pages of cards: `Card`s, inside a Centre, above the PagerAt. Pages of words: `Page`s, as a Step's
21
- * reading block (no Sheet on that screen — a pager never works beside one), the round pager in the ActionBar.
20
+ * Pages of cards: `Card`s, inside a Centre, above the Pager. Pages of words: `Page`s, as a Step's
21
+ * reading block (no Sheet on that screen — a pager bar never works beside one), the round pager bar in the ActionBar.
22
22
  */
23
23
  export declare function Pages({ className, ...rest }: PagesProps): import("react").JSX.Element;
24
24
  /** the direction a Centre's card slides: `next` — out to the left, the new one in from the right; `back` — the reverse */
@@ -3,7 +3,7 @@ import { flushSync } from 'react-dom';
3
3
  import { cx } from '../cx.js';
4
4
  /**
5
5
  * Centre — the centred group: ONE thing to read at the human centre of the screen, when that thing is not a lone
6
- * card: an EmptyState; a Card with its quiet second Btn and the PagerAt under it.
6
+ * card: an EmptyState; a Card with its quiet second Btn and the Pager under it.
7
7
  * On a plain `Screen` (the usual case, Lh 2026-09-12) it is in flow: the column is one phone tall, the group takes
8
8
  * the room after the TopBar and centres a short card; a long card makes the page longer and scrolls up under the
9
9
  * bar — nothing is capped, nothing shrinks. On a `Screen fill` it floats at 40% down (`--screen-centre`), capped
@@ -17,8 +17,8 @@ export function Centre({ className, ...rest }) {
17
17
  * Pages — the pages of a walk, all one height: every page is rendered, stacked in one cell as tall as the
18
18
  * tallest at any text size. Give the current one `aria-current="step"` and every other one `inert`
19
19
  * (hidden in place, out of the tab order). Turn with slideTo().
20
- * Pages of cards: `Card`s, inside a Centre, above the PagerAt. Pages of words: `Page`s, as a Step's
21
- * reading block (no Sheet on that screen — a pager never works beside one), the round pager in the ActionBar.
20
+ * Pages of cards: `Card`s, inside a Centre, above the Pager. Pages of words: `Page`s, as a Step's
21
+ * reading block (no Sheet on that screen — a pager bar never works beside one), the round pager bar in the ActionBar.
22
22
  */
23
23
  export function Pages({ className, ...rest }) {
24
24
  return _jsx("div", { className: cx('pages', className), ...rest });
@@ -6,7 +6,7 @@ export interface PageProps extends ComponentProps<'section'> {
6
6
  /**
7
7
  * Page — ONE page of words in the content: a picture (optional, first — it stays put while the words scroll), a heading, the words,
8
8
  * maybe a CtaPack. A component INSIDE the content box, never the box itself (Lh 2026-09-23): several Pages in a `Pages` = a walk the
9
- * pager turns (slideTo); a single one sits in the content (a Step) directly. Was BaseContent until 2026-09-23 — "words on the base" meant
9
+ * pager bar turns (slideTo); a single one sits in the content (a Step) directly. Was BaseContent until 2026-09-23 — "words on the base" meant
10
10
  * a still, one-scaffold screen, which is gone.
11
11
  */
12
12
  export declare function Page({ bleed, className, ...rest }: PageProps): import("react").JSX.Element;
@@ -3,7 +3,7 @@ import { cx } from '../cx.js';
3
3
  /**
4
4
  * Page — ONE page of words in the content: a picture (optional, first — it stays put while the words scroll), a heading, the words,
5
5
  * maybe a CtaPack. A component INSIDE the content box, never the box itself (Lh 2026-09-23): several Pages in a `Pages` = a walk the
6
- * pager turns (slideTo); a single one sits in the content (a Step) directly. Was BaseContent until 2026-09-23 — "words on the base" meant
6
+ * pager bar turns (slideTo); a single one sits in the content (a Step) directly. Was BaseContent until 2026-09-23 — "words on the base" meant
7
7
  * a still, one-scaffold screen, which is gone.
8
8
  */
9
9
  export function Page({ bleed, className, ...rest }) {
@@ -0,0 +1,11 @@
1
+ import type { ComponentProps } from 'react';
2
+ export interface PagerProps extends Omit<ComponentProps<'span'>, 'children'> {
3
+ /** the current page, 1-based */
4
+ at: number;
5
+ /** how many pages */
6
+ of: number;
7
+ /** accessible name; defaults to "Page {at} of {of}" */
8
+ label?: string;
9
+ }
10
+ /** Pager — the "where am I" dots: one accent dot at the current page. Under the card that changes (a child of Centre), or a Step's child after its Pages — pinned to the base's foot. One per walk, never inside a page; in a Walkthrough it is the bottom layer's first row (an ActionBar child, centred). */
11
+ export declare function Pager({ at, of, label, className, ...rest }: PagerProps): import("react").JSX.Element;
@@ -0,0 +1,6 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { cx } from '../cx.js';
3
+ /** Pager — the "where am I" dots: one accent dot at the current page. Under the card that changes (a child of Centre), or a Step's child after its Pages — pinned to the base's foot. One per walk, never inside a page; in a Walkthrough it is the bottom layer's first row (an ActionBar child, centred). */
4
+ export function Pager({ at, of, label, className, ...rest }) {
5
+ return (_jsx("span", { className: cx('pager__at', className), role: "img", "aria-label": label ?? `Page ${at} of ${of}`, ...rest, children: Array.from({ length: of }, (_, i) => _jsx("i", { className: i + 1 === at ? 'is-at' : undefined }, i)) }));
6
+ }
@@ -1,6 +1,6 @@
1
1
  import { type ComponentProps } from 'react';
2
2
  export interface PanesProps extends Omit<ComponentProps<'div'>, 'onChange'> {
3
- /** CONTROLLED: the pane in sight (0-based). The app owns it — pair it with `onAtChange`; a `Segment` beside it sets the same number. */
3
+ /** CONTROLLED: the pane in sight (0-based). The app owns it — pair it with `onAtChange`; a `BtnGroup` beside it sets the same number. */
4
4
  at?: number;
5
5
  /** UNCONTROLLED: the pane it starts on (default 0) */
6
6
  defaultAt?: number;
@@ -14,7 +14,7 @@ export interface PanesProps extends Omit<ComponentProps<'div'>, 'onChange'> {
14
14
  /**
15
15
  * Panes — a few panes side by side INSIDE a card or a sheet, one in sight: the member swipes left / right to the next, the dots
16
16
  * under them say where (Lh 2026-10-02). Children are `Pane`s. It holds a number, controlled like an input: `at` + `onAtChange`, or
17
- * `defaultAt` — so a `Segment` over it (the app's wiring) names the panes and moves to one by a tap; the swipe is never the only way.
17
+ * `defaultAt` — so a `BtnGroup` over it (the app's wiring) names the panes and moves to one by a tap; the swipe is never the only way.
18
18
  * Not a walk: pages of words on the base are `Pages`. The panes out of sight are `inert`.
19
19
  */
20
20
  export declare function Panes({ at, defaultAt, onAtChange, dots, label, className, children, ...rest }: PanesProps): import("react").JSX.Element;
@@ -2,11 +2,11 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { Children, cloneElement, isValidElement, useEffect, useLayoutEffect, useRef } from 'react';
3
3
  import { reducedMotion, useControlled, watchSize } from '../state.js';
4
4
  import { cx } from '../cx.js';
5
- import { PagerAt } from './PagerAt.js';
5
+ import { Pager } from './Pager.js';
6
6
  /**
7
7
  * Panes — a few panes side by side INSIDE a card or a sheet, one in sight: the member swipes left / right to the next, the dots
8
8
  * under them say where (Lh 2026-10-02). Children are `Pane`s. It holds a number, controlled like an input: `at` + `onAtChange`, or
9
- * `defaultAt` — so a `Segment` over it (the app's wiring) names the panes and moves to one by a tap; the swipe is never the only way.
9
+ * `defaultAt` — so a `BtnGroup` over it (the app's wiring) names the panes and moves to one by a tap; the swipe is never the only way.
10
10
  * Not a walk: pages of words on the base are `Pages`. The panes out of sight are `inert`.
11
11
  */
12
12
  export function Panes({ at, defaultAt = 0, onAtChange, dots = true, label, className, children, ...rest }) {
@@ -37,7 +37,7 @@ export function Panes({ at, defaultAt = 0, onAtChange, dots = true, label, class
37
37
  request(Math.min(panes.length - 1, Math.max(0, Math.round(r.scrollLeft / r.clientWidth))));
38
38
  }, 120);
39
39
  };
40
- return (_jsxs("div", { className: cx('panes', className), role: "group", "aria-label": label, ...rest, children: [_jsx("div", { className: "panes__rail", ref: rail, onScroll: scrolled, children: panes.map((p, i) => cloneElement(p, { inert: i !== current || undefined })) }), dots && panes.length > 1 && _jsx(PagerAt, { at: current + 1, of: panes.length })] }));
40
+ return (_jsxs("div", { className: cx('panes', className), role: "group", "aria-label": label, ...rest, children: [_jsx("div", { className: "panes__rail", ref: rail, onScroll: scrolled, children: panes.map((p, i) => cloneElement(p, { inert: i !== current || undefined })) }), dots && panes.length > 1 && _jsx(Pager, { at: current + 1, of: panes.length })] }));
41
41
  }
42
42
  /** Pane — one pane of a `Panes`: components one after another, as in a card. */
43
43
  export function Pane({ className, ...rest }) {
@@ -21,7 +21,7 @@ export interface ScreenProps extends ComponentProps<'div'> {
21
21
  * Screen — the mobile screen in THREE parts: **top** and **content** (the children) in one flex column, the content down to the
22
22
  * screen's foot; **bottom** a LAYER over it there (Lh 2026-09-23). Top = `TopBar` / `AppBar`, or `top="lead"` (no bar, the words start
23
23
  * 15% down). Content = a box of components one after another — a `Step` (`Pages` of `Page`s / a `Page` [+ `Sheet`]), a `Centre`, a
24
- * `SheetStack`… Bottom = an `ActionBar` (the walk's `PagerAt` its first row) or a `FloatBar`, covering the content's foot on purpose. Max 34.375rem (550px) wide, the standard inset, no vertical padding. A plain Screen (no `fill`) scrolls.
24
+ * `SheetStack`… Bottom = an `ActionBar` (the walk's `Pager` its first row) or a `FloatBar`, covering the content's foot on purpose. Max 34.375rem (550px) wide, the standard inset, no vertical padding. A plain Screen (no `fill`) scrolls.
25
25
  * ONE OUTCOME, DRAWN ONCE (Lh 2026-10-01): a screen that can come out more than one way is not drawn until the app knows which —
26
26
  * `ready={false}` until then.
27
27
  * The slots set the order; children laid by hand in the same order still work.
@@ -5,7 +5,7 @@ import { cx } from '../cx.js';
5
5
  * Screen — the mobile screen in THREE parts: **top** and **content** (the children) in one flex column, the content down to the
6
6
  * screen's foot; **bottom** a LAYER over it there (Lh 2026-09-23). Top = `TopBar` / `AppBar`, or `top="lead"` (no bar, the words start
7
7
  * 15% down). Content = a box of components one after another — a `Step` (`Pages` of `Page`s / a `Page` [+ `Sheet`]), a `Centre`, a
8
- * `SheetStack`… Bottom = an `ActionBar` (the walk's `PagerAt` its first row) or a `FloatBar`, covering the content's foot on purpose. Max 34.375rem (550px) wide, the standard inset, no vertical padding. A plain Screen (no `fill`) scrolls.
8
+ * `SheetStack`… Bottom = an `ActionBar` (the walk's `Pager` its first row) or a `FloatBar`, covering the content's foot on purpose. Max 34.375rem (550px) wide, the standard inset, no vertical padding. A plain Screen (no `fill`) scrolls.
9
9
  * ONE OUTCOME, DRAWN ONCE (Lh 2026-10-01): a screen that can come out more than one way is not drawn until the app knows which —
10
10
  * `ready={false}` until then.
11
11
  * The slots set the order; children laid by hand in the same order still work.
@@ -10,30 +10,30 @@ 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 `Lift` (the thing being asked about, light, scrolls
14
- * when tall) and a `Drawer` (the dark end that asks). No border. Two beats on appearing: drops in from above, then the drawer comes
15
- * out below while the lift moves up.
13
+ * Modal — the ask: ONE card at the centre of a `SheetStage dim`, two parts — a `ModalBody` (the thing being asked about, light, scrolls
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.
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.
19
19
  */
20
20
  export declare function Modal({ open, onClosed, onDismiss, className, ...rest }: ModalProps): import("react").JSX.Element;
21
- export interface LiftProps extends ComponentProps<'div'> {
21
+ export interface ModalBodyProps extends ComponentProps<'div'> {
22
22
  }
23
- /** Lift — the light part of a `Modal`: the case, request or item the question is about (a CardHead, rows, a timeline); scrolls inside when tall. */
24
- export declare function Lift({ className, ...rest }: LiftProps): import("react").JSX.Element;
25
- export interface DrawerProps extends Omit<ComponentProps<'div'>, 'title'> {
23
+ /** 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. */
24
+ export declare function ModalBody({ className, ...rest }: ModalBodyProps): import("react").JSX.Element;
25
+ export interface ModalAskProps extends Omit<ComponentProps<'div'>, 'title'> {
26
26
  /** the question */
27
27
  title: ReactNode;
28
28
  /** the fine print under it */
29
29
  caption?: ReactNode;
30
- /** a LIGHT drawer: the card's paper and dark words instead of the dark end (Lh 2026-09-29) — same layout, same trio; the one solid button turns dark */
30
+ /** a LIGHT ModalAsk: the card's paper and dark words instead of the dark end (Lh 2026-09-29) — same layout, same trio; the one solid button turns dark */
31
31
  light?: boolean;
32
32
  }
33
- /** Drawer — the dark end of a `Modal` (`light` = paper and dark words): the question, its fine print, and a `BtnTrio` (or two buttons) — plain buttons
34
- * with the app's `onClick`; the Drawer does nothing with them. A `Modal` holding ONLY a Drawer (no `Lift`) is the CONFIRM: a question with nothing
35
- * to show above it — the Drawer is the whole card and comes in with it in one beat. */
36
- export declare function Drawer({ title, caption, light, className, children, ...rest }: DrawerProps): import("react").JSX.Element;
33
+ /** ModalAsk — the dark end of a `Modal` (`light` = paper and dark words): the question, its fine print, and a `BtnTrio` (or two buttons) — plain buttons
34
+ * 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
35
+ * to show above it — the ModalAsk is the whole card and comes in with it in one beat. */
36
+ export declare function ModalAsk({ title, caption, light, className, children, ...rest }: ModalAskProps): import("react").JSX.Element;
37
37
  export interface BtnTrioProps extends ComponentProps<'div'> {
38
38
  }
39
39
  /** BtnTrio — three round buttons with dotted connectors between them (× · ? · ✓); the middle one inverted. */
@@ -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 `Lift` (the thing being asked about, light, scrolls
7
- * when tall) and a `Drawer` (the dark end that asks). No border. Two beats on appearing: drops in from above, then the drawer comes
8
- * out below while the lift moves up.
6
+ * Modal — the ask: ONE card at the centre of a `SheetStage dim`, two parts — a `ModalBody` (the thing being asked about, light, scrolls
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.
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.
@@ -54,14 +54,14 @@ export function Modal({ open = true, onClosed, onDismiss, className, ...rest })
54
54
  }, [has, open]);
55
55
  return _jsx("article", { ref: ref, className: cx('card modal', className), ...rest });
56
56
  }
57
- /** Lift — the light part of a `Modal`: the case, request or item the question is about (a CardHead, rows, a timeline); scrolls inside when tall. */
58
- export function Lift({ className, ...rest }) {
57
+ /** 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. */
58
+ export function ModalBody({ className, ...rest }) {
59
59
  return _jsx("div", { className: cx('modal__lift', className), ...rest });
60
60
  }
61
- /** Drawer — the dark end of a `Modal` (`light` = paper and dark words): the question, its fine print, and a `BtnTrio` (or two buttons) — plain buttons
62
- * with the app's `onClick`; the Drawer does nothing with them. A `Modal` holding ONLY a Drawer (no `Lift`) is the CONFIRM: a question with nothing
63
- * to show above it — the Drawer is the whole card and comes in with it in one beat. */
64
- export function Drawer({ title, caption, light, className, children, ...rest }) {
61
+ /** ModalAsk — the dark end of a `Modal` (`light` = paper and dark words): the question, its fine print, and a `BtnTrio` (or two buttons) — plain buttons
62
+ * 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
63
+ * to show above it — the ModalAsk is the whole card and comes in with it in one beat. */
64
+ export function ModalAsk({ title, caption, light, className, children, ...rest }) {
65
65
  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] }));
66
66
  }
67
67
  /** BtnTrio — three round buttons with dotted connectors between them (× · ? · ✓); the middle one inverted. */
@@ -23,8 +23,8 @@ export interface WalkthroughProps extends Omit<ComponentProps<typeof Screen>, 'c
23
23
  defaultAt?: number;
24
24
  /** a page was turned to: its index */
25
25
  onAtChange?: (at: number) => void;
26
- /** the ONE row of the bottom panel. Omitted: the round pager (back · next). A function gets `{ at, count, go }`,
27
- * e.g. the pager until the last page, then one wide Btn. */
26
+ /** the ONE row of the bottom panel. Omitted: the round pager bar (back · next). A function gets `{ at, count, go }`,
27
+ * e.g. the pager bar until the last page, then one wide Btn. */
28
28
  cta?: ReactNode | ((c: WalkthroughCta) => ReactNode);
29
29
  /** rows of controls ABOVE the CTA row (a text-size Slider…), one row each, top to bottom — keep them few */
30
30
  controls?: ReactNode[];
@@ -32,9 +32,9 @@ export interface WalkthroughProps extends Omit<ComponentProps<typeof Screen>, 'c
32
32
  ctaLink?: ReactNode;
33
33
  /** layers over the screen: the app's `SheetStage dim` + `Modal` that a link or button opens (the app wires it) */
34
34
  children?: ReactNode;
35
- /** the pager's back button name — cardds names it in the page's language (`<html lang>`); pass it only for different words */
35
+ /** the pager bar's back button name — cardds names it in the page's language (`<html lang>`); pass it only for different words */
36
36
  backLabel?: string;
37
- /** the pager's next button name — cardds names it in the page's language (`<html lang>`); pass it only for different words */
37
+ /** the pager bar's next button name — cardds names it in the page's language (`<html lang>`); pass it only for different words */
38
38
  nextLabel?: string;
39
39
  }
40
40
  /**
@@ -5,7 +5,7 @@ import { Screen } from '../scaffold/Screen.js';
5
5
  import { Step } from '../sheets/Step.js';
6
6
  import { Page } from '../scaffold/Page.js';
7
7
  import { Pages, slideTo } from '../scaffold/Centre.js';
8
- import { PagerAt } from '../scaffold/PagerAt.js';
8
+ import { Pager } from '../scaffold/Pager.js';
9
9
  import { ActionBar, ActionBarTier } from '../scaffold/ActionBar.js';
10
10
  import { CardHead } from '../cards/CardHead.js';
11
11
  import { Text } from '../type/Text.js';
@@ -50,7 +50,7 @@ export function Walkthrough({ pages, at: atProp, defaultAt = 0, onAtChange, cta,
50
50
  const row = typeof cta === 'function' ? cta({ at, count, go }) : cta;
51
51
  const custom = row !== undefined && row !== null;
52
52
  const pager = (_jsxs(_Fragment, { children: [_jsx(IconBtn, { icon: "arrow-left", label: backLabel, disabled: at === 0, onClick: () => go(at - 1) }), _jsx(IconBtn, { invert: true, icon: "arrow-right", label: nextLabel, disabled: at === count - 1, onClick: () => go(at + 1) })] }));
53
- const dots = count > 1 ? _jsx(PagerAt, { at: at + 1, of: count }) : null;
53
+ const dots = count > 1 ? _jsx(Pager, { at: at + 1, of: count }) : null;
54
54
  const bottom = dots || controls.length || ctaLink != null ? (_jsxs(ActionBar, { variant: "tiers", children: [dots, controls.map((c, i) => _jsx(ActionBarTier, { children: c }, i)), _jsx(ActionBarTier, { pager: true, children: custom ? row : pager }), ctaLink != null && _jsx(ActionBarTier, { className: "action-bar__tier--link", children: ctaLink })] })) : (_jsx(ActionBar, { variant: "pager", children: custom ? row : pager }));
55
55
  return (_jsxs(Screen, { fill: true, bottom: bottom, ...rest, children: [_jsx(Step, { ref: step, children: _jsx(Pages, { ...swipe, children: pages.map((p, i) => (_jsxs(Page, { "aria-current": i === at ? 'step' : undefined, inert: i !== at || undefined, children: [p.media, _jsx(CardHead, { level: "h1", heading: p.heading }), typeof p.body === 'string' ? _jsx(Text, { level: "body", children: p.body }) : p.body] }, i))) }) }), children] }));
56
56
  }
package/lib/mcp.mjs CHANGED
@@ -14,7 +14,7 @@ const bool = (description) => ({ type: 'boolean', description });
14
14
  const TOOLS = [
15
15
  {
16
16
  name: 'list_components',
17
- description: 'The names of every cardds component and what each is FOR, nothing else. represents is what it stands for (person, group, time…) or, for a control that stands for no one thing, the KIND of data it carries (words, number, picture, signal); the word in parentheses after a name in the text is its represents. Every component shows information; shape says how much: "one" (one thing), "group-brief" (a group read at a glance or in a line), "group-detail" (a group read in detail), "—" (a helper function or structure, not something drawn). interact says whether the member can do something with it: false (it only shows or holds), or the kinds, any of pick, act, write, navigate — alone when shape is one (a Btn acts), as a group when shape is a group (a Segment picks among several). Two kinds mean it does both. group is the src folder, not what it is for. Call this once, first, in a session that will build a UI with cardds, to learn what exists; then get_component for the ones you use.',
17
+ description: 'The names of every cardds component and what each is FOR, nothing else. represents is what it stands for (person, group, time…) or, for a control that stands for no one thing, the KIND of data it carries (words, number, picture, signal); the word in parentheses after a name in the text is its represents. Every component shows information; shape says how much: "one" (one thing), "group-brief" (a group read at a glance or in a line), "group-detail" (a group read in detail), "—" (a helper function or structure, not something drawn). interact says whether the member can do something with it: false (it only shows or holds), or the kinds, any of pick, act, write, navigate — alone when shape is one (a Btn acts), as a group when shape is a group (a BtnGroup picks among several). Two kinds mean it does both. group is the src folder, not what it is for. Call this once, first, in a session that will build a UI with cardds, to learn what exists; then get_component for the ones you use.',
18
18
  inputSchema: { type: 'object', properties: {} },
19
19
  run: (cat) => catalog.list(cat),
20
20
  },
package/lint/rules.mjs CHANGED
@@ -115,15 +115,16 @@ export function checkSource(source, file, { classes = carddsClasses() } = {}) {
115
115
  const findings = [];
116
116
  const lines = source.split('\n');
117
117
  let text = source.replace(/\/\*[\s\S]*?\*\/|(?<![:'"`\\])\/\/[^\n]*/g, (c) => c.replace(/[^\n]/g, ' ')); // comments out, lines kept
118
+ const add = (rule, level, idx, message) => {
119
+ const line = lineOf(text, idx);
120
+ if (!/cardds-ignore-next-line/.test(lines[line - 2] ?? '')) findings.push({ rule, level, file, line, message }); // `// cardds-ignore-next-line` — a deliberate case (a story that shows the wrong way)
121
+ };
122
+
118
123
  // a component imported under another name (`Home as HomeScreen`) is still that component: its tags read by the name the rules know. Lines kept
119
124
  // (seen 2026-10-08: a prototype's `<HomeScreen backLabel="กลับ">` passed control-name-repeat)
120
125
  for (const imp of text.matchAll(/import\s*(?:type\s*)?\{([^}]*)\}\s*from\s*['"][^'"]*cardds[^'"]*['"]/g)) {
121
126
  for (const [, name, alias] of imp[1].matchAll(/\b([A-Z]\w*)\s+as\s+([A-Z]\w*)/g)) text = text.replace(new RegExp(`(</?)${alias}\\b`, 'g'), `$1${name}`);
122
127
  }
123
- const add = (rule, level, idx, message) => {
124
- const line = lineOf(text, idx);
125
- if (!/cardds-ignore-next-line/.test(lines[line - 2] ?? '')) findings.push({ rule, level, file, line, message }); // `// cardds-ignore-next-line` — a deliberate case (a story that shows the wrong way)
126
- };
127
128
 
128
129
  // 1 · a cardds class written by hand: the look comes without the behaviour — import the component
129
130
  for (const m of text.matchAll(/\bclass(?:Name)?(?=\s*=)/g)) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@usableapp/cardds",
3
- "version": "0.7.74",
3
+ "version": "0.7.75",
4
4
  "license": "UNLICENSED",
5
5
  "type": "module",
6
6
  "description": "card-first mobile design system, React-first: the components in src/ are thin wrappers over the CSS contract (css/*.css stays the only truth); gallery/ shows every story live (npm run dev), tests/ measures the geometry.",
@@ -1,11 +0,0 @@
1
- import type { ComponentProps } from 'react';
2
- export interface SegmentProps extends ComponentProps<'div'> {
3
- }
4
- /** Segment — a tab strip / day picker whose items share the width; children are SegmentItems. */
5
- export declare function Segment({ className, ...rest }: SegmentProps): import("react").JSX.Element;
6
- export interface SegmentItemProps extends ComponentProps<'button'> {
7
- /** the selected tab (filled) */
8
- selected?: boolean;
9
- }
10
- /** SegmentItem — one tab in a Segment. */
11
- export declare function SegmentItem({ selected, className, ...rest }: SegmentItemProps): import("react").JSX.Element;
@@ -1,18 +0,0 @@
1
- import type { ComponentProps, ReactNode } from 'react';
2
- import type { Tone } from '../cards/Card.js';
3
- export interface BandProps extends ComponentProps<'article'> {
4
- /** the two-line label (use a `<br />` to break it) */
5
- label: ReactNode;
6
- /** the giant number; wrap a unit in `<small>` */
7
- num: ReactNode;
8
- /** the footnote under the label */
9
- foot?: ReactNode;
10
- /** the paper — see `Tone` */
11
- tone?: Tone;
12
- }
13
- /** Band — a data card: 2-line label · giant number · footnote. Stack several in a BandStack for one boundary with hairlines. */
14
- export declare function Band({ label, num, foot, tone, className, ...rest }: BandProps): import("react").JSX.Element;
15
- export interface BandStackProps extends ComponentProps<'div'> {
16
- }
17
- /** BandStack — several Bands as ONE card boundary, a hairline between each. */
18
- export declare function BandStack({ className, ...rest }: BandStackProps): import("react").JSX.Element;
@@ -1,11 +0,0 @@
1
- import type { ComponentProps } from 'react';
2
- export interface PagerAtProps extends Omit<ComponentProps<'span'>, 'children'> {
3
- /** the current page, 1-based */
4
- at: number;
5
- /** how many pages */
6
- of: number;
7
- /** accessible name; defaults to "Page {at} of {of}" */
8
- label?: string;
9
- }
10
- /** PagerAt — the "where am I" dots: one accent dot at the current page. Under the card that changes (a child of Centre), or a Step's child after its Pages — pinned to the base's foot. One per walk, never inside a page; in a Walkthrough it is the bottom layer's first row (an ActionBar child, centred). */
11
- export declare function PagerAt({ at, of, label, className, ...rest }: PagerAtProps): import("react").JSX.Element;
@@ -1,6 +0,0 @@
1
- import { jsx as _jsx } from "react/jsx-runtime";
2
- import { cx } from '../cx.js';
3
- /** PagerAt — the "where am I" dots: one accent dot at the current page. Under the card that changes (a child of Centre), or a Step's child after its Pages — pinned to the base's foot. One per walk, never inside a page; in a Walkthrough it is the bottom layer's first row (an ActionBar child, centred). */
4
- export function PagerAt({ at, of, label, className, ...rest }) {
5
- return (_jsx("span", { className: cx('pager__at', className), role: "img", "aria-label": label ?? `Page ${at} of ${of}`, ...rest, children: Array.from({ length: of }, (_, i) => _jsx("i", { className: i + 1 === at ? 'is-at' : undefined }, i)) }));
6
- }