@usableapp/cardds 0.7.69 → 0.7.71

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -2,6 +2,16 @@
2
2
 
3
3
  > For: a project that consumes `@usableapp/cardds` and its AI — what changed between versions, written as before → after, so a screen can be brought up to date without reading the source. Newest first. A consumer never edits cardds; if something here leaves you without a way to do what you did before, report the gap.
4
4
 
5
+ ## 0.7.71 — 2026-10-08
6
+
7
+ ### Fixed
8
+ - **A popover `Menu` / `Dropdown` on an iPhone was as tall as the screen** (Lh 2026-10-08) — in Safari on iOS 26 an open menu ran from under its button to the foot of the screen, its rows spread out over that height. Before → after: three rows stood 706px tall → 163px, each row its own 48px. Nothing to change in an app: the same markup, the same props. Chrome and Android never showed it.
9
+
10
+ ## 0.7.70 — 2026-10-08
11
+
12
+ ### Added
13
+ - **`Home rise="flood"` / `SheetStack closed rise="flood"`: an opened sheet floods the whole screen, over the top bar** (Lh 2026-10-08) — a tap on a sheet head brings that sheet up over the `AppBar` to fill the screen from its top edge to its bottom. The bar is covered, and hidden from the keyboard and a screen reader, while a sheet is open; it is back at once when the sheet folds. At the screen's top the open sheet's top corners are square; folded, they are round again. Closed, the home looks exactly as before: the bar on top, the `base` under it, the heads at the bottom. `<Home rise="flood" top={…} title="…" sheets={…} base={…} />`. Before: an open sheet always stopped under the top bar; that is still the default (`rise="under"`), so nothing to change in a screen that does not ask for it. Only for a `closed` stack in a `Screen fill` (`Home` always is); on any other stack it warns once and is ignored. In plain markup the class is `sheet-stack--flood`. On a phone running edge to edge the sheet stops at the status bar's line (`--safe-top`), it does not go under it. Gallery: screen · Home, the `Flood` story.
14
+
5
15
  ## 0.7.69 — 2026-10-07
6
16
 
7
17
  ### Fixed
package/README.md CHANGED
@@ -188,6 +188,12 @@ the card is the material, the sheet is what it is in the stack.
188
188
  mark changes colour the same way. Right after a fold, while the
189
189
  pile is still coming back, a tap is answered by where each head RESTS: the
190
190
  head that will stand under the finger opens, whatever is passing there.
191
+ An open sheet stops under the top bar. `rise="flood"` on the `SheetStack` (or
192
+ `Home`) — `sheet-stack--flood` in markup — lets it rise over the bar and fill
193
+ the whole screen instead: the stack is then the whole screen, the bar a layer
194
+ under the sheets (hidden while one is open), nothing resizes. At the screen's
195
+ top the open sheet's top corners are square; folded, they are round again. It needs
196
+ `--closed` in a `Screen fill`.
191
197
  - **Five sheets max.** Slots are declared per `:nth-child` in `stack.css`; a
192
198
  sixth would render at slot 0, on top of the first.
193
199
  - **Every head needs its own `.sheet-stack__back`** if the stack is `--tap`. It stays
package/css/layover.css CHANGED
@@ -127,6 +127,7 @@
127
127
  --menu-pad: var(--sp-2); /* published: a row's radius is concentric with the panel's */
128
128
  padding: var(--menu-pad);
129
129
  min-width: 12rem;
130
+ align-content: start; /* rows keep their own height: WebKit stretches a popover grid's rows to the whole room under its anchor (iOS 26.5, 2026-10-08) */
130
131
  background: var(--menu-bg, var(--action-bg));
131
132
  color: var(--menu-ink, var(--action-ink));
132
133
  box-shadow: var(--menu-shadow, var(--shadow-float));
package/css/stack.css CHANGED
@@ -30,6 +30,7 @@
30
30
  </div>
31
31
  --closed (with --tap): nothing open on arrival — every sheet peeks at the
32
32
  bottom, the headroom above is the base's (block below).
33
+ --flood (with --closed): an open sheet fills the whole screen, over the top bar.
33
34
  Direct children only, five at most (slots are :nth-child below):
34
35
  first = the back of the stack, last = the front. README "Sheet stack
35
36
  markup". The SheetStack component opens and closes a --tap stack; without it the
@@ -295,6 +296,48 @@
295
296
  .sheet-stack__base:has(> .screen__centre:only-child) { align-content: center; }
296
297
  .sheet-stack__base > .screen__centre { padding-inline: 0; }
297
298
 
299
+ /* ============================================================
300
+ .sheet-stack--flood — a closed stack whose open sheet FLOODS the screen: it rises over the top bar and fills the whole screen
301
+ (`rise="flood"`), instead of stopping under it. Only for a --closed --tap stack in a .screen--fill.
302
+
303
+ A sheet is rigid: it cannot grow to cover the bar, it only moves by translate. So the stack is made the
304
+ whole screen instead, and every sheet is screen-height always. The bar leaves the column and becomes a
305
+ LAYER at the screen's top, under the sheets (they paint at --level 1-5, the bar at 0): closed, the peeks
306
+ still end at the stack's bottom, --_headroom unchanged; open, the same translate lands the sheet flush
307
+ at the stack's top, which is now the screen's top, and the bar is what it covers. Nothing resizes, no
308
+ new state, no script — Screen only measures the bar (--top-h; a bar is a floor, it grows with the text).
309
+ The screen's safe-top padding stays: a sheet stops at that line, never under the phone's status bar.
310
+
311
+ The base must not sit under the bar or take its taps: it starts below the bar (margin, not padding —
312
+ its box does not overlap the bar) and is shorter by the same amount, so its room is exactly what it was
313
+ under a bar in the column. That amount is the bar plus the breathing under it today (--sp-2 under an
314
+ appbar, the screen's gap under a topbar). Covered, the bar is also hidden (visibility), after the sheet
315
+ has covered it and at once when the sheet folds, so a keyboard and a screen reader do not reach it.
316
+ ============================================================ */
317
+ .screen--fill:has(> .sheet-stack--flood) { --_bar-gap: var(--sp-2); }
318
+ .screen--fill:has(> .sheet-stack--flood):has(> .topbar:first-child) { --_bar-gap: var(--screen-gap); }
319
+ .screen--fill:has(> .sheet-stack--flood) > :is(.appbar, .topbar):first-child {
320
+ position: absolute;
321
+ inset: var(--safe-top) 0 auto;
322
+ max-width: none;
323
+ margin: 0;
324
+ z-index: 0; /* under every sheet (--level 1-5); the base clears it by margin */
325
+ }
326
+ .sheet-stack--flood { --_bar: calc(var(--top-h, var(--header-h)) + var(--_bar-gap)); }
327
+ .sheet-stack--flood > .sheet-stack__base {
328
+ margin-top: var(--_bar);
329
+ height: calc(var(--_headroom, 0px) - var(--_bar));
330
+ }
331
+ .screen--fill:has(> .sheet-stack--flood > .stack-card.is-open, > .sheet-stack--flood > :not(.stack-card) > .stack-card.is-open) > :is(.appbar, .topbar):first-child {
332
+ visibility: hidden;
333
+ transition: visibility 0s linear var(--motion-sheet); /* gone once the sheet has covered it; back at once on a fold (the rule above has no delay) */
334
+ }
335
+ /* at the screen's top the open sheet's top corners are SQUARE (Lh 2026-10-08): round, they left two small pockets of the
336
+ bar's ground at the glass. They square as the sheet rises and round again as it folds, in the slide's own time. Paint
337
+ only, the sheet's size is untouched. A pull on the head keeps them square until the finger lets go. */
338
+ .sheet-stack--flood > .card { transition: translate var(--motion-sheet) ease, border-radius var(--motion-sheet) ease; }
339
+ .sheet-stack--flood > .card.is-open { border-top-left-radius: 0; border-top-right-radius: 0; }
340
+
298
341
  /* back button: lives in a slot the header reserves once open, and DISSOLVES
299
342
  into it — an opacity fade in place, never a slide in from the left (the
300
343
  slide read as a second thing moving while the sheet itself moved; Lh,
package/dist/cardds.css CHANGED
@@ -1054,6 +1054,7 @@ button:enabled { cursor: pointer; } /* a disabled one keeps the arrow */
1054
1054
  </div>
1055
1055
  --closed (with --tap): nothing open on arrival — every sheet peeks at the
1056
1056
  bottom, the headroom above is the base's (block below).
1057
+ --flood (with --closed): an open sheet fills the whole screen, over the top bar.
1057
1058
  Direct children only, five at most (slots are :nth-child below):
1058
1059
  first = the back of the stack, last = the front. README "Sheet stack
1059
1060
  markup". The SheetStack component opens and closes a --tap stack; without it the
@@ -1319,6 +1320,48 @@ button:enabled { cursor: pointer; } /* a disabled one keeps the arrow */
1319
1320
  .sheet-stack__base:has(> .screen__centre:only-child) { align-content: center; }
1320
1321
  .sheet-stack__base > .screen__centre { padding-inline: 0; }
1321
1322
 
1323
+ /* ============================================================
1324
+ .sheet-stack--flood — a closed stack whose open sheet FLOODS the screen: it rises over the top bar and fills the whole screen
1325
+ (`rise="flood"`), instead of stopping under it. Only for a --closed --tap stack in a .screen--fill.
1326
+
1327
+ A sheet is rigid: it cannot grow to cover the bar, it only moves by translate. So the stack is made the
1328
+ whole screen instead, and every sheet is screen-height always. The bar leaves the column and becomes a
1329
+ LAYER at the screen's top, under the sheets (they paint at --level 1-5, the bar at 0): closed, the peeks
1330
+ still end at the stack's bottom, --_headroom unchanged; open, the same translate lands the sheet flush
1331
+ at the stack's top, which is now the screen's top, and the bar is what it covers. Nothing resizes, no
1332
+ new state, no script — Screen only measures the bar (--top-h; a bar is a floor, it grows with the text).
1333
+ The screen's safe-top padding stays: a sheet stops at that line, never under the phone's status bar.
1334
+
1335
+ The base must not sit under the bar or take its taps: it starts below the bar (margin, not padding —
1336
+ its box does not overlap the bar) and is shorter by the same amount, so its room is exactly what it was
1337
+ under a bar in the column. That amount is the bar plus the breathing under it today (--sp-2 under an
1338
+ appbar, the screen's gap under a topbar). Covered, the bar is also hidden (visibility), after the sheet
1339
+ has covered it and at once when the sheet folds, so a keyboard and a screen reader do not reach it.
1340
+ ============================================================ */
1341
+ .screen--fill:has(> .sheet-stack--flood) { --_bar-gap: var(--sp-2); }
1342
+ .screen--fill:has(> .sheet-stack--flood):has(> .topbar:first-child) { --_bar-gap: var(--screen-gap); }
1343
+ .screen--fill:has(> .sheet-stack--flood) > :is(.appbar, .topbar):first-child {
1344
+ position: absolute;
1345
+ inset: var(--safe-top) 0 auto;
1346
+ max-width: none;
1347
+ margin: 0;
1348
+ z-index: 0; /* under every sheet (--level 1-5); the base clears it by margin */
1349
+ }
1350
+ .sheet-stack--flood { --_bar: calc(var(--top-h, var(--header-h)) + var(--_bar-gap)); }
1351
+ .sheet-stack--flood > .sheet-stack__base {
1352
+ margin-top: var(--_bar);
1353
+ height: calc(var(--_headroom, 0px) - var(--_bar));
1354
+ }
1355
+ .screen--fill:has(> .sheet-stack--flood > .stack-card.is-open, > .sheet-stack--flood > :not(.stack-card) > .stack-card.is-open) > :is(.appbar, .topbar):first-child {
1356
+ visibility: hidden;
1357
+ transition: visibility 0s linear var(--motion-sheet); /* gone once the sheet has covered it; back at once on a fold (the rule above has no delay) */
1358
+ }
1359
+ /* at the screen's top the open sheet's top corners are SQUARE (Lh 2026-10-08): round, they left two small pockets of the
1360
+ bar's ground at the glass. They square as the sheet rises and round again as it folds, in the slide's own time. Paint
1361
+ only, the sheet's size is untouched. A pull on the head keeps them square until the finger lets go. */
1362
+ .sheet-stack--flood > .card { transition: translate var(--motion-sheet) ease, border-radius var(--motion-sheet) ease; }
1363
+ .sheet-stack--flood > .card.is-open { border-top-left-radius: 0; border-top-right-radius: 0; }
1364
+
1322
1365
  /* back button: lives in a slot the header reserves once open, and DISSOLVES
1323
1366
  into it — an opacity fade in place, never a slide in from the left (the
1324
1367
  slide read as a second thing moving while the sheet itself moved; Lh,
@@ -4780,6 +4823,7 @@ button:enabled { cursor: pointer; } /* a disabled one keeps the arrow */
4780
4823
  --menu-pad: var(--sp-2); /* published: a row's radius is concentric with the panel's */
4781
4824
  padding: var(--menu-pad);
4782
4825
  min-width: 12rem;
4826
+ align-content: start; /* rows keep their own height: WebKit stretches a popover grid's rows to the whole room under its anchor (iOS 26.5, 2026-10-08) */
4783
4827
  background: var(--menu-bg, var(--action-bg));
4784
4828
  color: var(--menu-ink, var(--action-ink));
4785
4829
  box-shadow: var(--menu-shadow, var(--shadow-float));
@@ -28,14 +28,16 @@ export function Screen({ fill, palette, as = 'div', top, bottom, ready = true, c
28
28
  }
29
29
  return watchSize([bar], () => el.style.setProperty('--bottom-h', `${bar.offsetHeight}px`));
30
30
  }, [bottom, ready]);
31
- /* a TRACK as the top, over a stage: its height published as --top-h — the stage keeps that line free for it (step.css) */
31
+ /* the TOP's height published as --top-h, a TRACK or a bar: a stage keeps that line free under a track (step.css); a
32
+ stack that rises over the bar (stack.css, `rise="flood"`) starts its base below it. A bar's height is a floor, not a size (it grows
33
+ with the text), so it is measured */
32
34
  useEffect(() => {
33
- const el = own.current, track = el?.querySelector(':scope > .track:first-child');
34
- if (!el || !track) {
35
+ const el = own.current, lead = el?.querySelector(':scope > :is(.track, .appbar, .topbar):first-child');
36
+ if (!el || !lead) {
35
37
  el?.style.removeProperty('--top-h');
36
38
  return;
37
39
  }
38
- return watchSize([track], () => el.style.setProperty('--top-h', `${track.offsetHeight}px`));
40
+ return watchSize([lead], () => el.style.setProperty('--top-h', `${lead.offsetHeight}px`));
39
41
  }, [top, ready]);
40
42
  const setRef = (node) => {
41
43
  own.current = node;
@@ -19,6 +19,8 @@ export interface SheetStackProps extends ComponentProps<'div'> {
19
19
  fanned?: boolean;
20
20
  /** nothing open on arrival (tap mode): every sheet, the front one too, peeks at the stack's bottom; the room above is the base's (`base`). A tap opens any sheet over it */
21
21
  closed?: boolean;
22
+ /** how an OPEN sheet meets the top bar of a closed stack in a `Screen fill`: `under` (default) stops under the bar · `flood` rises over it and fills the WHOLE screen (from the safe-top line down), the bar covered and hidden while a sheet is open. Only for a `closed` tap stack (warns once otherwise). Nothing resizes: the bar is a layer under the sheets */
23
+ rise?: 'under' | 'flood';
22
24
  /** what the base shows in a closed stack's headroom, behind the sheets, laid out against the peeks (a picture, a greeting) */
23
25
  base?: ReactNode;
24
26
  }
@@ -29,11 +31,12 @@ export interface SheetStackProps extends ComponentProps<'div'> {
29
31
  * with CHILDREN, in this order: `<StackBack />` (tap mode), `<Text level="h2" caps>Shopping</Text>`, an `<IconRow>` of Icons —
30
32
  * never the `heading` prop (a stack head is back · h2 · tabs on ONE line — StackSheet builds it). Put it last in a `Screen fill`, it bleeds
31
33
  * off the bottom. `closed`: nothing open on arrival — every head peeks at the bottom and the base above shows `base` (a home screen
32
- * with a picture over the menu); the `base` slot is rendered after the sheets, so it never takes a sheet's slot. This is the sheet STACK; a single bottom sheet is `Sheet`.
34
+ * with a picture over the menu); the `base` slot is rendered after the sheets, so it never takes a sheet's slot. An open sheet fills everything under the top bar,
35
+ * or with `rise="flood"` the whole screen, covering the bar. This is the sheet STACK; a single bottom sheet is `Sheet`.
33
36
  *
34
37
  * A COMPONENT, not a wiring: WHICH sheet is open is the app's (`open` + `onOpenChange`, or `defaultOpen`) — so a button anywhere, a route or
35
38
  * the phone's back button can open and close a section. WHAT the sheet does with that, and with `refreshing`, lives on the card (`stack`):
36
39
  * the place classes, the grip, the pull. The stack only says which index. Sheets are rigid — the open one slides up, the ones after it leave
37
40
  * below, the ones before tuck under nearest-first; closing reverses in two beats. Only `translate` (stack.css); never a size.
38
41
  */
39
- export declare function SheetStack({ mode, open, defaultOpen, onOpenChange, onRefresh, refreshing, mark, fanned, closed, base, className, children, onClickCapture, onPointerDownCapture, ref: passed, ...rest }: SheetStackProps): import("react").JSX.Element;
42
+ export declare function SheetStack({ mode, open, defaultOpen, onOpenChange, onRefresh, refreshing, mark, fanned, closed, rise, base, className, children, onClickCapture, onPointerDownCapture, ref: passed, ...rest }: SheetStackProps): import("react").JSX.Element;
@@ -48,14 +48,15 @@ function stamp(child, place) {
48
48
  * with CHILDREN, in this order: `<StackBack />` (tap mode), `<Text level="h2" caps>Shopping</Text>`, an `<IconRow>` of Icons —
49
49
  * never the `heading` prop (a stack head is back · h2 · tabs on ONE line — StackSheet builds it). Put it last in a `Screen fill`, it bleeds
50
50
  * off the bottom. `closed`: nothing open on arrival — every head peeks at the bottom and the base above shows `base` (a home screen
51
- * with a picture over the menu); the `base` slot is rendered after the sheets, so it never takes a sheet's slot. This is the sheet STACK; a single bottom sheet is `Sheet`.
51
+ * with a picture over the menu); the `base` slot is rendered after the sheets, so it never takes a sheet's slot. An open sheet fills everything under the top bar,
52
+ * or with `rise="flood"` the whole screen, covering the bar. This is the sheet STACK; a single bottom sheet is `Sheet`.
52
53
  *
53
54
  * A COMPONENT, not a wiring: WHICH sheet is open is the app's (`open` + `onOpenChange`, or `defaultOpen`) — so a button anywhere, a route or
54
55
  * the phone's back button can open and close a section. WHAT the sheet does with that, and with `refreshing`, lives on the card (`stack`):
55
56
  * the place classes, the grip, the pull. The stack only says which index. Sheets are rigid — the open one slides up, the ones after it leave
56
57
  * below, the ones before tuck under nearest-first; closing reverses in two beats. Only `translate` (stack.css); never a size.
57
58
  */
58
- export function SheetStack({ mode = 'tap', open, defaultOpen = null, onOpenChange, onRefresh, refreshing = null, mark = 'dots', fanned, closed, base, className, children, onClickCapture, onPointerDownCapture, ref: passed, ...rest }) {
59
+ export function SheetStack({ mode = 'tap', open, defaultOpen = null, onOpenChange, onRefresh, refreshing = null, mark = 'dots', fanned, closed, rise = 'under', base, className, children, onClickCapture, onPointerDownCapture, ref: passed, ...rest }) {
59
60
  const [current, request] = useControlled(open, defaultOpen, onOpenChange);
60
61
  const prev = useRef(current);
61
62
  const [closingFrom, setClosingFrom] = useState(null);
@@ -136,6 +137,9 @@ export function SheetStack({ mode = 'tap', open, defaultOpen = null, onOpenChang
136
137
  e.stopPropagation();
137
138
  request(i);
138
139
  };
140
+ const flood = rise === 'flood' && !!closed && mode === 'tap';
141
+ if (rise === 'flood' && !flood)
142
+ warnOnce('stack-rise', 'cardds SheetStack: rise="flood" needs a closed tap stack (`closed`, mode "tap") — the bar a sheet rises over is the screen\'s, and only a closed stack leaves its base under it. Ignored.');
139
143
  const closing = current == null && closingFrom != null;
140
144
  const anchor = current ?? closingFrom;
141
145
  const items = Children.toArray(children);
@@ -158,5 +162,5 @@ export function SheetStack({ mode = 'tap', open, defaultOpen = null, onOpenChang
158
162
  };
159
163
  return stamp(child, place);
160
164
  }) : items;
161
- return (_jsxs("div", { className: cx('sheet-stack', mode !== 'static' && `sheet-stack--${mode}`, fanned && 'sheet-stack--fanned', closed && 'sheet-stack--closed', className), ...rest, ref: setRef, onClickCapture: clickCapture, onPointerDownCapture: pointerDownCapture, children: [stamped, base != null && _jsx("div", { className: "sheet-stack__base", children: base })] }));
165
+ return (_jsxs("div", { className: cx('sheet-stack', mode !== 'static' && `sheet-stack--${mode}`, fanned && 'sheet-stack--fanned', closed && 'sheet-stack--closed', flood && 'sheet-stack--flood', className), ...rest, ref: setRef, onClickCapture: clickCapture, onPointerDownCapture: pointerDownCapture, children: [stamped, base != null && _jsx("div", { className: "sheet-stack__base", children: base })] }));
162
166
  }
@@ -37,6 +37,8 @@ export interface HomeProps extends Omit<ComponentProps<typeof Screen>, 'children
37
37
  refreshing?: number | null;
38
38
  /** the grip on every sheet: `dots` (default) or `line` — as `SheetStack mark` */
39
39
  mark?: 'dots' | 'line';
40
+ /** how an open sheet meets the top: `under` (default) stops under the AppBar · `flood` rises over it and fills the WHOLE screen, the bar covered while a sheet is open — as `SheetStack rise` */
41
+ rise?: 'under' | 'flood';
40
42
  /** CONTENT above the sheet heads — one `Page`: a welcome, the news, a picture. It reads
41
43
  * from the top; a sheet coming up covers it */
42
44
  base?: ReactNode;
@@ -51,9 +53,9 @@ export interface HomeProps extends Omit<ComponentProps<typeof Screen>, 'children
51
53
  }
52
54
  /**
53
55
  * Home — TEMPLATE, a `Screen` with its parts filled — top: the AppBar · content: the `base` (title, welcome…) over a closed `SheetStack` · no bottom
54
- * (the stack is content, never the bottom: open, a sheet fills everything under the top).
56
+ * (the stack is content, never the bottom: open, a sheet fills everything under the top, or with `rise="flood"` the whole screen).
55
57
  * Every section is a `StackSheet` of three parts: SHEET HEAD (title + icon tabs, a count over a tab; the back button once open) · SHEET BODY (scrolls) · SHEET FOOT (the CTA,
56
- * at the bottom). A tap on a head brings that sheet up to fill everything under the top. With a LayerHistory above and no `open` prop, that open sheet is one history entry named `home`. Only the open sheet's body is rendered — three
58
+ * at the bottom). A tap on a head brings that sheet up to fill everything under the top (`rise="flood"`: the whole screen, over the bar). With a LayerHistory above and no `open` prop, that open sheet is one history entry named `home`. Only the open sheet's body is rendered — three
57
59
  * sections on one home never cost three pages (`keep` holds a body once opened).
58
60
  */
59
- export declare function Home({ top, title, sheets, open: openProp, defaultOpen, onOpenChange, onRefresh, refreshing, mark, base, keep, backLabel, backAt, children, ...rest }: HomeProps): import("react").JSX.Element;
61
+ export declare function Home({ top, title, sheets, open: openProp, defaultOpen, onOpenChange, onRefresh, refreshing, mark, rise, base, keep, backLabel, backAt, children, ...rest }: HomeProps): import("react").JSX.Element;
@@ -11,12 +11,12 @@ const TONES = [2, 3, 1, 2, 3];
11
11
  const LEAVE = 800; // ms: a closing sheet keeps its body until it has slid home
12
12
  /**
13
13
  * Home — TEMPLATE, a `Screen` with its parts filled — top: the AppBar · content: the `base` (title, welcome…) over a closed `SheetStack` · no bottom
14
- * (the stack is content, never the bottom: open, a sheet fills everything under the top).
14
+ * (the stack is content, never the bottom: open, a sheet fills everything under the top, or with `rise="flood"` the whole screen).
15
15
  * Every section is a `StackSheet` of three parts: SHEET HEAD (title + icon tabs, a count over a tab; the back button once open) · SHEET BODY (scrolls) · SHEET FOOT (the CTA,
16
- * at the bottom). A tap on a head brings that sheet up to fill everything under the top. With a LayerHistory above and no `open` prop, that open sheet is one history entry named `home`. Only the open sheet's body is rendered — three
16
+ * at the bottom). A tap on a head brings that sheet up to fill everything under the top (`rise="flood"`: the whole screen, over the bar). With a LayerHistory above and no `open` prop, that open sheet is one history entry named `home`. Only the open sheet's body is rendered — three
17
17
  * sections on one home never cost three pages (`keep` holds a body once opened).
18
18
  */
19
- export function Home({ top, title, sheets, open: openProp, defaultOpen = null, onOpenChange, onRefresh, refreshing, mark, base, keep, backLabel = word('back'), backAt, children, ...rest }) {
19
+ export function Home({ top, title, sheets, open: openProp, defaultOpen = null, onOpenChange, onRefresh, refreshing, mark, rise, base, keep, backLabel = word('back'), backAt, children, ...rest }) {
20
20
  const layer = useOptionalLayer('home');
21
21
  const [local, change] = useControlled(openProp, defaultOpen, onOpenChange);
22
22
  // A passed `open` is the parent's. With no LayerHistory the index stays local, so a story without a router still opens sheets.
@@ -50,5 +50,5 @@ export function Home({ top, title, sheets, open: openProp, defaultOpen = null, o
50
50
  }, [leaving]);
51
51
  const [tabs, setTabs] = useState(() => Object.fromEntries(sheets.map((x, i) => [i, x.defaultTab ?? x.tabs?.[0]?.id])));
52
52
  const shows = (i) => i === shown || i === leaving || (keep && seen.has(i));
53
- return (_jsxs(Screen, { fill: true, top: top, ...rest, children: [_jsx(Text, { level: "h1", as: "h1", sr: true, children: title }), _jsx(SheetStack, { closed: true, open: shown, onOpenChange: request, onRefresh: onRefresh, refreshing: refreshing, mark: mark, base: base, children: sheets.map((s, i) => (_jsx(StackSheet, { tone: s.tone ?? TONES[i], paper: s.paper, title: s.title, icon: s.icon, tabs: s.tabs, tab: tabs[i], onTabChange: (t) => setTabs((v) => ({ ...v, [i]: t })), backLabel: backLabel, backAt: backAt, foot: typeof s.foot === 'function' ? s.foot(tabs[i]) : s.foot, children: shows(i) ? (typeof s.body === 'function' ? s.body(tabs[i]) : s.body) : null }, i))) }), children] }));
53
+ return (_jsxs(Screen, { fill: true, top: top, ...rest, children: [_jsx(Text, { level: "h1", as: "h1", sr: true, children: title }), _jsx(SheetStack, { closed: true, open: shown, onOpenChange: request, onRefresh: onRefresh, refreshing: refreshing, mark: mark, rise: rise, base: base, children: sheets.map((s, i) => (_jsx(StackSheet, { tone: s.tone ?? TONES[i], paper: s.paper, title: s.title, icon: s.icon, tabs: s.tabs, tab: tabs[i], onTabChange: (t) => setTabs((v) => ({ ...v, [i]: t })), backLabel: backLabel, backAt: backAt, foot: typeof s.foot === 'function' ? s.foot(tabs[i]) : s.foot, children: shows(i) ? (typeof s.body === 'function' ? s.body(tabs[i]) : s.body) : null }, i))) }), children] }));
54
54
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@usableapp/cardds",
3
- "version": "0.7.69",
3
+ "version": "0.7.71",
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.",